views:

366

answers:

4

I'm trying to change the behaviour of some functions in C with help of the preprocessor; and also add optional parameters that can be set on or off...

The basic pattern for the optional parameters is easy:

#ifdef OPT_PARAM
  #define my_func(a, b, opt) _my_func(a, b, opt)
#else
  #define my_func(a, b, opt) _my_func(a, b)
#endif

/*the rest of the code always calls "my_func" with all the params
and not the underscored version...*/

#ifdef OPT_PARAM
void _my_func(int a, int b, int opt)
#else
void _my_func(int a, int b)
#endif
{
  /*... more #ifdefs surrounding opt uses */
}

the pattern for wrapping a function conditionally is similar, but the problem is that the underscores start to add up (one extra for each level of nesting, that can be a different function or just a #define for the next level in case it's not wrapped).

So, any ideas about how to reduce the code complexity here?

P.S. I'd be willing to use Python... but this is for a driver :-(

+2  A: 

Seems like you want to use default arguments or something that is not available in C. This seems to be a bad idea. Why not handle things in a more C fashion, where if you don't want to specify an argument you just pass NULL or -1 (standard).

void function (int a, int b, int c) {
  if (c != -1) {
    // something
  }
}

function(a,b,-1);
function(a,b,c);
Tobias R
It's for debugging purposes; I need to get rid of the extra parameters in the final build... and anyway, that doesn't solve the problem of nesting wrappers :-s
fortran
I think he's trying to use optional parameteres. If it's set then call my_func(a, b, opt) else call my_func(a, b).
Gert
@fortran: Why do you need nesting at all? Do you have more than two versions (debug and final) of each function?
Christoph
Not sure what you mean with nesting wrappers? you might want to look at variable arguments in the preprocessor. something along the following lines #define func(a,b,...) _func(a,b,##) but I am not sure that it solves your problem. C just isn't designed for that :)
Tobias R
@Cristoph I want to do three kinds of different debugging actions: logging (in fact this is a "wrapping macro" more than a function), add fingerprints in memory allocations and... I'd swear there was a third thing, but it's too late know and I don't remember now ^_^
fortran
+1  A: 

I think this may be closer to what you want, but I am unsure. My understanding is the idea is to allow c functions with arbitrary numbers of arguments, that are type checked and can be eliminated at compile time.

Let me warn you about the use of underscores in identifiers, by quoting the standard. You may run into a reserved identifier. However, the likelihood of this is unknown to me.

ISO/IEC 9899:1999 (E) 7.1.3

— All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use.

This solution requires GCC. The version of GCC must also support weak symbols. The idea is to allow the compiler to look for the right function definition using weak symbols. Additionally the contents of the function are simplified by using the knowledge that the compiler should prune dead branches, ie:

if (0) { ... }

at compile time without further analysis (GCC 4.x certainly does this). By defining the non-existent optional parameters as c pre-processor (cpp) symbolsyou can avoid having cpp conditionals in the function body (if so desired). See how opt1 and opt2 are defined for f_opt0 below.

#include <assert.h>
#include <stdio.h>

extern void f_opt0(int a, int b) __attribute__((weak));
extern void f_opt1(int a, int b, int opt1) __attribute__((weak));
extern void f_opt2(int a, int b, int opt1, int opt2) __attribute__((weak));

#ifdef OPT0
void f_opt0(int a, int b) {
#define opt1 0
#define opt2 0
#endif
#ifdef OPT1
void f_opt1(int a, int b, int opt1) {
#define opt2 0
#endif
#ifdef OPT2
void f_opt2(int a, int b, int opt1, int opt2) {
#endif
  if (opt1) printf("opt1=%d\n", opt1);
  if (opt2) printf("opt2=%d\n", opt2);
  printf("a+b=%d\n", a+b);
  #undef opt1
  #undef opt2
}

#define f(a, b, o1, o2) \
  if (f_opt2) f_opt2(a, b, o1, o2); \
  else if (f_opt1) f_opt1(a, b, o1); \
  else if (f_opt0) f_opt0(a, b); \
  else { assert(0 && "no f() defined!"); }

int main(void) {
  f(1, 2, 1, 1);
  return 0;
}

My testing was very limited, and I do not advocate this as good design in C. It seems prone to problems and is troublesome to comprehend. However, I hope it addresses your goals.

Marc
+2  A: 

Can you use a C++ compiler? You could use (of C++ features) just function overloading.

Another option is

#ifdef OPT_PARAM
#  define OPT(X) , X
#else
#  define OPT(X)  
#endif

int my_func(int a, int b OPT(int opt) ) {
#ifndef OPT_PARAM
  int opt = default_value;
#endif
  ... // Rest of code
}  


...
// calling it
my_func(2, 4 OPT( 42 ) );
Logan Capaldo
A: 

In the end I just added a new decorator that handled uniformly the extra parameters and changed the obscure underscores by more descriptive names.

Now it's a more orthogonal design in which I can plug and unplug behaviour at compile time with no runtime overhead.

fortran