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.