Macro-driven conditional first argument to a function

Viewed 73

I have an API that behaves along the following lines:

#ifdef MYLIB_SINGLETON
  void* mylib_malloc(size_t size);
  void mylib_free(void* ptr);
  void mylib_log(int level, const char* msg, ...);
#else
  void* mylib_malloc(void* ctx, size_t size);
  void mylib_free(void* ctx, void* ptr);
  void mylib_log(void* ctx, int level, const char* msg, ...);
#endif

I want to remove the duplication. In C++, I would use a simple-ish template, but this is a C header. My options appear to be:

  • Macros.
  • Generate the header from a spec file as part of the build process.
  • Deal with it. Until this multiplexes 4-ways or more, it's not that fragile.

As far as macros go. The best I could do is the following, but I'm far from comfortable enough with macros to feel confident that it's as clean as it can get.


#ifdef MYLIB_SINGLETON
  #define MYLIB_FUNC_ARGS(...) ##__VA_ARGS__
#else
  #define MYLIB_FUNC_ARGS(...) void* ctx, ##__VA_ARGS__
#endif

void* mylib_malloc( MYLIB_FUNC_ARGS(size_t size) );
void mylib_free( MYLIB_FUNC_ARGS(void* free) );
void mylib_log( MYLIB_FUNC_ARGS(const char* msg, ...) );

It's not completely terrible, but not quite good enough to my eyes.

Edit: to clarify, it's not the only thing I came up with, just what I feel is the best. In particular the following works, but I don't like them for the stated reasons

// It looks like MYLIB_MAYBE_CTX alters the size parameter, instead of being a parameter in its own right.
void* mylib_malloc(MYLIB_MAYBE_CTX size_t size);

// Very confusing at a glance.
void* MYLIB_FUNC(mylib_malloc, size_t size);

I'd rather have a solution that would be more intuitive to read for users of the library. Something along the following lines would be nice. As far as I know, it's not possible, though.

void* mylib_malloc(MYLIB_MAYBE_CTX, size_t size);
void mylib_free(MYLIB_MAYBE_CTX, void* free);
void mylib_log(MYLIB_MAYBE_CTX, const char* msg, ...);

Is there some macro wizardry possible, or just something obvious I'm not seeing, that would allow me to provide both interfaces in one go while still intuitively looking like functions for a would-be user of the library? Alternatively, an authoritative "Nope, that's as good as you are going to get" answer would let me stop worrying about this.

1 Answers

Here's one possible syntax

#ifdef MYLIB_SINGLETON
      #define MYLIB_ARGS(...) (__VA_ARGS__)
#else
      #define MYLIB_ARGS(...) (void *ctx, ##__VA_ARGS__)
#endif

#define MYLIB_FUNC(x) x MYLIB_ARGS

void* MYLIB_FUNC(mylib_malloc)(size_t size);
void MYLIB_FUNC(mylib_free)(void* free);
void MYLIB_FUNC(mylib_log)(const char* msg, ...);
Related