2

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 :-(

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
fortran
  • 74,053
  • 25
  • 135
  • 175
  • You sure you don't want variadic functions? http://en.wikipedia.org/wiki/Varargs#Variadic_functions_in_C.2C_Objective-C.2C_C.2B.2B.2C_and_D – Inshallah Jul 24 '09 at 15:59
  • Pretty sure I don't want them! ^_^ I've already used them and I'm very concerned about losing static type checking. – fortran Jul 24 '09 at 16:02
  • 1
    Perhaps I'm missing something, but this looks fine to me. Where's the extra complexity? And what is it that gets nested? – Steve Melnikoff Jul 25 '09 at 16:39
  • I'm with Steve here. The code looks just find and not very complex. If you can clearly separate debug and release logic you can do it a bit more clean, but in general it's just fine. We use this logic all the time to add __FILE__ __LINE__ information for debugging purposes. – Ilya Jul 26 '09 at 09:10

4 Answers4

2

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
  • 91
  • 6
  • 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 Jul 24 '09 at 15:59
  • 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 Jul 24 '09 at 16:08
  • @fortran: Why do you need nesting at all? Do you have more than two versions (debug and final) of each function? – Christoph Jul 24 '09 at 17:14
  • 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 Jul 24 '09 at 17:15
  • @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 Jul 24 '09 at 22:32
2

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
  • 39,555
  • 5
  • 63
  • 78
1

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 Butler
  • 1,346
  • 7
  • 13
0

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
  • 74,053
  • 25
  • 135
  • 175
  • you've accepted your own answer... however it doesn't mean that your answer is the best answer for the question you asked. It's an opinionated choice and I see a lot of reasonable answers to your original quesiton that do answer the question. – Eric Jan 31 '22 at 01:26