Is there a way to avoid code duplication in multiple similar functions? - c

I am writing a family of functions which are to be embedded in a small micro-controller operating at very near real time, so every clock cycle counts. The functions are almost identical.
The only way that I can see to do this without duplicating vast chunks of code is to use the really ugly and frowned upon method of declaring the code in an include file which is then intentionally included multiple times.
The following works to demonstrate the concept:
// func.inc
// The absence of an include guard is intentional, as each time this file gets included
// the output will be different
void FUNC(int x)
{
/* SNIP - lots and lots of code that is duplicated between
variant A and B (and more) of the function
for ( ... 4096 )
{
for( lots more nested loops)
{
*/
// IMPORTANT - I do not want to call functions here as it is
// in a tight loop withconsecutive memory accesses of
// different sizes of strided sparse arrays
#ifdef A
printf("A %d\n", x);
#endif
#ifdef B
printf("B %d\n", x);
#endif
/*
}
}
*/
// main.c
#include <stdio.h>
#define FUNC func_A
#define A
#include "func.inc"
#undef A
#undef FUNC
#define FUNC func_B
#define B
#include "func.inc"
#undef B
#undef FUNC
#define FUNC func_AB
#define A
#define B
#include "func.inc"
int main()
{
func_A(10);
func_B(20);
func_AB(30);
printf("Done\n");
return 0;
}
My problem is that whilst this works, it looks hideous, and might be very confusing to someone else trying to understand it. Using a pointer to a function is too inefficient to be a viable option in this case.
Is there a solution that anyone can suggest without simply duplicating several slightly different versions of the same function?

It's not really clear what's pseudo code and real code here, but overall you should not use #define + #undef + #include for the purpose of different code generation. (You could do it with "X macros" though as a last resort. Not an ideal solution but better than this.)
The solution to " IMPORTANT - I do not want to call functions here" is to call functions.
Function inlining has been a thing for some 30 years and 20 years ago C got explicit language support for it. And nowadays compilers are much better than programmers to determine what to inline. I'll make an example with explicit inline just to demonstrate that calling functions does not affect performance, if done correctly.
With traditional C, you would do something like this:
#include <stdio.h>
static inline void SNIP (void)
{
puts(__func__);
}
static inline void A_stuff (int val)
{
printf("%s %d\n", __func__, val);
}
static inline void B_stuff (int val)
{
printf("%s %d\n", __func__, val);
}
typedef enum { A=1, B=2 } AB_t;
void func(AB_t ab, int val)
{
SNIP();
if(ab & A)
A_stuff(val);
if(ab & B)
B_stuff(val);
}
int main()
{
func(A, 10);
func(B, 20);
func(A|B, 30);
printf("Done\n");
return 0;
}
That's the sane solution. The only functions that are actually called in the generated machine code are func and the printing functions.
Alternatively, you could have done code generation with "X macros" too - these exist solely for the purpose of avoiding code repetition, at the expense of readability. Wouldn't really recommend it here, but I'll include an example for completeness:
#include <stdio.h>
#define FUNC_LIST \
X(A, 10) \
X(B, 20) \
X(AB, 30) \
static inline void SNIP (void)
{
puts(__func__);
}
static inline void A_stuff (int val)
{
printf("%s %d\n", __func__, val);
}
static inline void B_stuff (int val)
{
printf("%s %d\n", __func__, val);
}
static inline void AB_stuff (int val)
{
A_stuff(val);
B_stuff(val);
}
#define X(opt, val) void func_##opt (int x) { SNIP(); opt##_stuff(x); }
FUNC_LIST
#undef X
int main()
{
#define X(opt, val) func_##opt(val),
FUNC_LIST
#undef X
printf("Done\n");
return 0;
}
This is quite unreadable just like the original code, except "X macros" are something of a de facto standard for icky macro tricks to avoid code repetition.
This creates multiple functions just like a C++ template, so it isn't ideal for that reason as well.

Edit: The question was originally tagged with C++, hence the answer.
Make a template!
// func.hpp
#ifndef FUNCITON_HPP
#define FUNCITON_HPP
enum Specifier : int {
A = 1 << 0,
B = 1 << 1,
};
#include <cstdio>
template <auto sp>
void foo(int x)
{
/* SNIP - lots and lots of code that is duplicated between
variant A and B (and more) of the function
for ( ... 4096 )
{
for( lots more nested loops)
{
*/
// IMPORTANT - I do not want to call functions here as it is
// in a tight loop withconsecutive memory accesses of
// different sizes of strided sparse arrays
if constexpr (static_cast<bool>(sp & Specifier::A)) {
std::printf("A %d\n", x);
}
if constexpr (static_cast<bool>(sp & Specifier::B)) {
std::printf("B %d\n", x);
}
}
#endif //!FUNCITON_HPP
Then
// func.cpp
#include "func.hpp"
auto constexpr func_a = foo<Specifier::A>; // Could also use a #define
auto constexpr func_b = foo<Specifier::B>;
auto constexpr func_ab = foo<Specifier::A | Specifier::B>;
int main()
{
func_a(1);
func_b(1);
func_ab(1);
}

Related

How could I implement in C language using macros and function pointers something similar to lambda functions in C++?

I want to implement in C something similar to lambda functions from C++( using macros and function pointers)
I think the biggest problem that I am facing is to define a function inside another function, and this thing is not possible in C. I think a better idea is to treat the lambda function ( passed through a macro ) as a lexical block.
I started some code:
#define func_name(line) func##line
#define line __LINE__
#define lambda(body, ret_type, ...) ret_type func_name(line)(__VA_ARGS__) \
{ \
body; \
} \
//#include <stdio.h>
lambda( printf("Hello from Lambda\n") , void, int a, float b)
int main(void) {
//printf("Hello World\n");
return 0;
}
I used gcc compiler with "-E" option to see preprocessor output:
void funcline(int a, float b) { printf("Hello from Lambda\n"); }
int main(void) {
return 0;
}
It's possible, but the lambdas will have to be stateless (no captures). You can use preprocessor to move function definitions around, but there is no way to use local variables from one function in another, or to introduce some kind of state into a function.
Also it makes the code very hard to debug. Since all functions using those pseudo-lambdas have to be wrapped in a macro, all line breaks in them are removed during preprocessing. It becomes impossible to place a breakpoint inside of such a function, or to advance through it line by line.
Here is an example of the usage. The implementation is at the end of the answer. The explanation of the syntax is right after the example.
Run on gcc.godbolt.org
#include <stdio.h>
#include <stdlib.h>
ENABLE_LAMBDAS(
void example1()
{
int arr[] = {4,1,3,2,5};
FUNC(int)(compare)(const void *a, const void *b)
(
return *(int*)a - *(int*)b;
)
qsort(arr, 5, sizeof(int), compare);
for (int i = 0; i < 5; i++ )
printf("%d ", arr[i]);
putchar('\n');
}
void example2()
{
int arr[] = {4,1,3,2,5};
qsort L_(arr, 5, sizeof(int), LAMBDA(int)(const void *a, const void *b)
(
return *(int*)a - *(int*)b;
));
for (int i = 0; i < 5; i++ )
printf("%d ", arr[i]);
putchar('\n');
}
int main()
{
example1();
example2();
}
) // ENABLE_LAMBDAS
Notice the ENABLE_LAMBDAS macro wrapping the whole snippet.
This example uses two ways of defining functions/lambdas:
FUNC(return_type)(name)(params)(body) just defines a function. The function definition is moved to the beginning of ENABLE_LAMBDAS, so it can be used inside of other functions.
LAMBDA(return_type)(params)(body) defines a pseudo-lambda. A function definition for it is generated at the beginning of ENABLE_LAMBDAS, with an automatically chosen unique name. LAMBDA... expands to that name.
If FUNC or LAMBDA are used inside of parentheses, the parentheses must be preceeded by the L_ macro. This is a limitation of the preprocessor, unfortunately.
The generated functions are always static.
Implementation:
// Creates a lambda.
// Usage:
// LAMBDA(return_type)(params)(body)
// Example:
// ptr = LAMBDA(int)(int x, int y)(return x + y;);
#define LAMBDA LAM_LAMBDA
// Defines a function.
// Usage:
// FUNC(return_type)(name)(params)(body)
// Example:
// FUNC(int)(foo)(int x, int y)(return x + y;)
// some_func(foo);
#define FUNC LAM_FUNC
// Any time a `LAMBDA` or `FUNC` appears inside of parentheses,
// those parentheses must be preceeded by this macro.
// For example, this is wrong:
// foo(LAMBDA(int)()(return 42;));
// While this works:
// foo L_(LAMBDA(int)()(return 42;));
#define L_ LAM_NEST
// `LAMBDA` and `FUNC` only work inside `ENABLE_LAMBDAS(...)`.
// `ENABLE_LAMBDAS(...)` expands to `...`, preceeded by function definitions for all the lambdas.
#define ENABLE_LAMBDAS LAM_ENABLE_LAMBDAS
// Lambda names are composed of this prefix and a numeric ID.
#ifndef LAM_PREFIX
#define LAM_PREFIX LambdaFunc_
#endif
// Implementation details:
// Returns nothing.
#define LAM_NULL(...)
// Identity macro.
#define LAM_IDENTITY(...) __VA_ARGS__
// Concats two arguments.
#define LAM_CAT(x, y) LAM_CAT_(x, y)
#define LAM_CAT_(x, y) x##y
// Given `(x)y`, returns `x`.
#define LAM_PAR(...) LAM_PAR_ __VA_ARGS__ )
#define LAM_PAR_(...) __VA_ARGS__ LAM_NULL(
// Given `(x)y`, returns `y`.
#define LAM_NO_PAR(...) LAM_NULL __VA_ARGS__
// Expands `...` and concats it with `_END`.
#define LAM_END(...) LAM_END_(__VA_ARGS__)
#define LAM_END_(...) __VA_ARGS__##_END
// A generic macro to define functions and lambdas.
// Usage: `LAM_DEFINE(wrap, ret)(name)(params)(body)`.
// In the encloding code, expands to `wrap(name)`.
#define LAM_DEFINE(wrap, ...) )(l,wrap,(__VA_ARGS__),LAM_DEFINE_0
#define LAM_DEFINE_0(name) name,LAM_DEFINE_1
#define LAM_DEFINE_1(...) (__VA_ARGS__),LAM_DEFINE_2
#define LAM_DEFINE_2(...) __VA_ARGS__)(c,
// Creates a lambda.
// Usage: `LAM_LAMBDA(ret)(params)(body)`.
#define LAM_LAMBDA(...) LAM_DEFINE(LAM_IDENTITY, __VA_ARGS__)(LAM_CAT(LAM_PREFIX, __COUNTER__))
// Defines a function.
// Usage: `LAM_FUNC(ret)(name)(params)(body)`.
#define LAM_FUNC(...) LAM_DEFINE(LAM_NULL, __VA_ARGS__)
// `LAM_LAMBDA` and `LAM_FUNC` only work inside of this macro.
#define LAM_ENABLE_LAMBDAS(...) \
LAM_END( LAM_GEN_LAMBDAS_A (c,__VA_ARGS__) ) \
LAM_END( LAM_GEN_CODE_A (c,__VA_ARGS__) )
// Processes lambdas and functions in the following parentheses.
#define LAM_NEST(...) )(open,)(c,__VA_ARGS__)(close,)(c,
// A loop. Returns the original code, with lambdas replaced with corresponding function names.
#define LAM_GEN_CODE_A(...) LAM_GEN_CODE_BODY(__VA_ARGS__) LAM_GEN_CODE_B
#define LAM_GEN_CODE_B(...) LAM_GEN_CODE_BODY(__VA_ARGS__) LAM_GEN_CODE_A
#define LAM_GEN_CODE_A_END
#define LAM_GEN_CODE_B_END
#define LAM_GEN_CODE_BODY(type, ...) LAM_CAT(LAM_GEN_CODE_BODY_, type)(__VA_ARGS__)
#define LAM_GEN_CODE_BODY_c(...) __VA_ARGS__
#define LAM_GEN_CODE_BODY_l(wrap, ret, name, ...) wrap(name)
#define LAM_GEN_CODE_BODY_open() (
#define LAM_GEN_CODE_BODY_close() )
// A loop. Generates lambda definitions, discarding all other code.
#define LAM_GEN_LAMBDAS_A(...) LAM_GEN_LAMBDAS_BODY(__VA_ARGS__) LAM_GEN_LAMBDAS_B
#define LAM_GEN_LAMBDAS_B(...) LAM_GEN_LAMBDAS_BODY(__VA_ARGS__) LAM_GEN_LAMBDAS_A
#define LAM_GEN_LAMBDAS_A_END
#define LAM_GEN_LAMBDAS_B_END
#define LAM_GEN_LAMBDAS_BODY(type, ...) LAM_CAT(LAM_GEN_LAMBDAS_BODY_, type)(__VA_ARGS__)
#define LAM_GEN_LAMBDAS_BODY_c(...)
#define LAM_GEN_LAMBDAS_BODY_l(wrap, ret, name, par, ...) static LAM_IDENTITY ret name par { __VA_ARGS__ }
#define LAM_GEN_LAMBDAS_BODY_open()
#define LAM_GEN_LAMBDAS_BODY_close()

Functions parameters using macro

I wanted to know if it was contraindicated to define functions parameters using a macro, knowing they could be variable. Does it break a coding convention ?
example:
#ifdef PREVIOUSLY_DEFINED_MACRO
# define PARAMETERS int a, long b
#else
# define PARAMETERS int a
#endif
void my_func(PARAMETERS)
{
...
}
Thanks !
The code is completely valid but, it's not a good coding practice.
Let's assume the following code snippet:
#ifdef PREV_DEFINED
#define ARGS int x, int y
#else
#define ARGS int x
#endif
#include <stdio.h>
// #define PREV_DEFINED
int func(ARGS) {
// In this context, only 'x' is available
// set by the macro
return (x + y); // ERROR, 'y' is undefined
// You need to make a different code, specifically for
// #ifdef PREV_DEFINED and #else
}
To solve this, you need to make two or more different functions within those #ifdef and #endif flags whose usage is controlled by the PREV_DEFINED macro that depends on how many parameters could be variadic. Eventually, this will make the code look worse.

Remove a function during compile by defining it as an empty macro

In this example code a macro is either defined or commented out by the programmer in order to remove a function from the released software.
#include <stdio.h>
#define MACRO //or omitted
#ifdef MACRO
void Function(const char* str)
{
printf("%s\n", str);
}
#else
#define Function(str)
#endif
int main(void)
{
Function("hello world");
getchar();
return 0;
}
Is there anything wrong with this?
Even though your solution works, I prefer the following approach:
#include <stdio.h>
#define MACRO //or omitted
#ifdef MACRO
#define FUNCTION(a) Function(a);
#else
#define FUNCTION(a)
#endif
#ifdef MACRO
void Function(const char* str)
{
printf("%s\n", str);
}
#endif
int main(void)
{
FUNCTION("hello world")
getchar();
return 0;
}
Note: FUNCTION is the macro, Function is the actual name of the function
This works by defining the macro FUNCTION(a) to a call to Function(const char*) when MACRO is enabled. On the other hand, when MACRO is disabled, calls to FUNCTION(a) will be defined to nothing.
I tend to prefer this method since it would be easier to abstract away the macro used to define your call from the macro defining your function definition. You might have cases where in release mode you only need to remove some of the calls to Function. In that case the definition of Function() is still required. For example:
#include <stdio.h>
#define DEBUG //or omitted
#ifdef DEBUG
#define FUNCTION(a) Function(a);
#else
#define FUNCTION(a)
#endif
void Function(const char* str)
{
printf("%s\n", str);
}
int main(void)
{
// Call function with Hello World only in debug
FUNCTION("hello world")
getchar();
// Call function with goodbye world in both debug and release
Function("goodbye world");
return 0;
}
It will work. But the entire symbol is removed this way. I prefer the following method.
#include <stdio.h>
#define MACRO 1
#if MACRO
void Function(const char* str)
{
printf("%s\n", str);
}
#else
void Function(const char *str){}
#endif
int main(void)
{
Function("hello world");
getchar();
return 0;
}
The following has changed:
The #if now compares a boolean. Your IDE is now able to find where MACRO is defined, in all cases. (right click, find definition)
You can, by changing MACRO to 2, change the entire meaning of Function. For example, a release compiled variant might contain a print to file or system log.
There will always be a symbol for Function, even if it does nothing, even in compiled code. This has the benefit that the string literals in the argument still count to the size statistics. As an embedded developer, I find this important.
Obviously, this is partly the preference of the one who crafts the code.

Why un-defining a macro takes effect before #undef is reached?

#include <stdio.h>
void f(void);
#define N 100
int main(void)
{
f();
#ifdef N
#undef N
#endif
return 0;
}
void f(void){
#if defined(N)
printf("N is %d\n",N);
#else
printf("N is undefined\n");
#endif // defined
}
Sorry same question like before but little different. Here same code it writes N is undefined
#include <stdio.h>
void f(void);
#define N 100
int main(void)
{
#if defined(N)
printf("N is %d\n",N);
#else
printf("N is undefined\n");
#endif // defined
/**f();
#ifdef N
#undef N
#endif**/
return 0;
}
/**void f(void){
}**/
But if I write the definitions from the f function in main instead it prints the output N is 100. Why does this print occur and why the difference?
You have to separate in your mind two timelines:
The compiling timeline - i.e. things that happen as your program is being compiled, and
The running timeline - i.e. the things that happen as your program runs
These two timelines are independent of each other. The compiling timeline roughly corresponds to the textual order of your program, with some corrections for #include processing and macro expansions. The running timeline is decided by your program's control structures, function invocations, and so on.
Since these two timelines are independent, a point X in code that is reached before point Y during the compile timeline may very well be reached after point Y during the running timeline. Moreover, it may be reached multiple times - both before and after another point Y.
Now let's go back to your question. #defines and macros are expanded at compile time, so before and after refer exclusively to the compile timeline. When you place #undef in main, above your function f, the un-definition happens before the use of N, leading to the compile-time error. The running timeline, according to which the call of f happens before the un-definition has no relevance, because #defines are an exclusively compile-time construct.

Usage of #if defined for function definition

I need to know if the usage of #if defined is correct and know the possible cons of using it this way.
#if defined TEST
int foo()
{
return 0;
}
int foo1()
{
return 0;
}
#else
int foo()
{
return 1;
}
int foo1()
{
return 1;
}
#endif
EDIT:
I am trying to use this code for two different platforms I am working on.
It is syntactically correct.
The primary disadvantage of that way of organizing it is that you repeat the definitions of the functions for both the test and non-test cases. What is not possible to tell from a carefully minimized example is how much of a problem that will be. The other way to organize it, assuming that the current return statements are surrogates for a more substantial block of code (at least in one of the two cases) would be:
int foo(void)
{
#if defined TEST
return 0;
#else
return 1;
#endif /* TEST */
}
int foo1(void)
{
#if defined TEST
return 0;
#else
return 1;
#endif /* TEST */
}
For these exact functions, you could do:
#ifdef TEST
#define RETVAL 0
#else
#define RETVAL 1
#endif /* TEST */
int foo(void)
{
return RETVAL;
}
int foo1(void)
{
return RETVAL;
}
However, your real functions are unlikely to be quite so simple, so this doesn't translate well.
The general goal is to avoid repetition, and to avoid conditional compilation showing in your functions.
the only problem I can think of is redundancy and for a serious project it's a really serious isuue. you should keep code duplication as small as possible, this is another way to do this with less code duplication:
int foo()
{
#if defined TEST
return 0;
#else
return 1;
#endif
}
int foo1()
{
#if defined TEST
return 0;
#else
return 1;
#endif
}
You could create a multiplatform library, putting the specific platform code in another specific platform library. That way, your multiplatform library can use the right specific library using #if directives. In other words, you will isolate specific platform code.

Resources