3

The following is some background as to what I want to do and why. The actual question is at the very bottom...

I have an API that has some format. For instance

int f(void *str1, void *str2) {...}

I want to reimplement the system behind the API so that it can be a drop in replacement for the old system. However it turns out that str1 is now unnecessary and moreover doesn't make sense to have if you're aware that you're using my new system. Therefore, I want to be able to expose the underlying API that makes sense:

int f_internal(void *str2);

Right now I have code that looks like this:

#ifdef  USE_INTERNAL
#define INTERNAL(sym) sym##_internal
#else
#define INTERNAL(sym) sym
#endif

extern "C" {
#ifndef USE_INTERNAL
int f(void *str1, void *str2){
  return INTERNAL(f)(str2);
}
#endif

SOME_ATTRIBUTES
int
INTERNAL(f)(void *str2){
  ... // actual content
} EXPORT_FUNCTION_MACRO(INTERNAL(f), 1);

}

The effect is if I define USE_INTERNAL, a call to f(a) works, but I don't define it then we have to use the f(a, b).

The problem I am encountering is that EXPORT_FUNCTION_MACRO itself defines another function name but doesn't evaluate INTERNAL(F) first. This results in the message

dir: error: pasting ")" and "_" does not give a valid preprocessing token
       INTERNAL(sym) \
--- NOTE--- EXPORT_FUNCTION_MACRO takes args 'sys' and 'n'
other_dir: note: in definition of macro ‘EXPORT_FUNCTION_MACRO’
   void _example##sym##_##n(void) {} \

WHAT I WANT TO DO:

I want to have a compile flag that can change the number of arguments needed to call something. Maybe something like (if it existed)

using f(a, b) = f_internal(a);

Any ideas?

Chris
  • 566
  • 2
  • 7
  • 22
  • 1
    Why not a simple trampoline function `int f(void *str1, void *str2) { return f_internal(str2); }`? This way, `f()` can be exported as usual. – Scheff's Cat Dec 17 '19 at 17:18
  • At the moment I am confused by `EXPORT_FUNCTION_MACRO(INTERNAL(F, 1));` which appears to use the INTERNAL macro with 2 arguments? – Gem Taylor Dec 17 '19 at 17:19
  • Concerning the API: Extra arguments which are not anymore needed wouldn't me concern as much as new arguments which wasn't needed before... ;-) – Scheff's Cat Dec 17 '19 at 17:21
  • @Gem Taylor, that was a typo I fixed it – Chris Dec 17 '19 at 17:24
  • Sorry, and capital F? – Gem Taylor Dec 17 '19 at 17:26
  • Scheff, I have to export the function that does the work, whether it's named `f` or `f_internal`. The trampoline function is the current implementation. The trampoline is the actual function if `USE_INTERNAL` is defined. I would the user to always call `f`, but depending on their understanding of the library they're linking in, they can use `f` with 1 fewer argument, which makes sense in their context. – Chris Dec 17 '19 at 17:27
  • capital F typo also fixed. – Chris Dec 17 '19 at 17:28
  • And you still get the same errors? – Gem Taylor Dec 17 '19 at 17:29
  • yes. This above is not my actual code, but a simplified example for stack overflow. – Chris Dec 17 '19 at 17:30
  • Why `f_internal`? As this is C++, you could *overload* the two functions: `void f(void* str); void f(void* str, void*) { f(str); }` or simply use a default argument: `void f(void* str, void* = nullptr);` – Aconcagua Dec 17 '19 at 17:50
  • Aconcagua, I was wondering if there may be a way to do this in C (note the extern "C" {}), but I think overloading is the way to go. – Chris Dec 17 '19 at 17:57
  • @Chris Ah, sorry, overlooked the `extern "C"`... – Aconcagua Dec 17 '19 at 17:58
  • So fix your `EXPORT_FUNCTION_MACRO` to expand the arguments before concatenating them. How is `EXPORT_FUNCTION_MACRO` related to what you are trying to do? `using f(a, b) = f_internal(a);` - you mean `using f(a, b) = f_internal(b);` right? – KamilCuk Dec 17 '19 at 18:03
  • 1
    @Chris You might be interested in [this post](https://stackoverflow.com/questions/479207/how-to-achieve-function-overloading-in-c). You'd have the famous `#ifdef __cplusplus` macros anyway, and you could provide the C-style overloads this way inside an `#else` block... – Aconcagua Dec 17 '19 at 18:05
  • @KamilCuk The end result is that I have aliases to functions, which is what I'm trying to do. The implementation I chose doesn't work because of what you mentioned. I don't know how to expand the arguments before concatenating them. – Chris Dec 17 '19 at 18:06
  • Ah, correcting my previous post: Default argument is out, as you drop first, not second parameter. Sorry. – Aconcagua Dec 17 '19 at 18:09
  • Is the alias meant to have external or internal linkage? You described how your current code does not work. Can you show the code _should_ work with some examples in case the macro USE_INTERAL is defined or not? Is f_internal extern? Can't you just `#define f(a, b) f(b)` ? – KamilCuk Dec 17 '19 at 18:09
  • f that calls f_internal has external linkage. – Chris Dec 17 '19 at 18:11
  • `but depending on their understanding of the library they're linking in` : If your user understand your library correctly, they would be able to call the function with another name. – ph3rin Dec 17 '19 at 18:11
  • @Kaenbyou Rin My library has higher performance, and so for that reason I might want to link in this library and not want to change the source code that actually uses the api. If you were building a system from scratch, you would use the "new" API that uses one fewer argument. – Chris Dec 17 '19 at 18:14
  • 1
    @Chris That is the point. If the user is building something from scratch, they would simply call the function with the new name (e.g. `f2(b)`), while the old code base can continue to use `f(a, b)`. The logic is: if a user is aware that they can call the function with fewer argument, they should also be aware that the function has a different name. – ph3rin Dec 17 '19 at 18:20
  • @Kaenbyou Rin, I see what you mean. I would've liked to keep the name the same, but if that's not a problem in terms of API design, then I suppose I have no problem with. Just wanted to keep everything clean and alike. – Chris Dec 17 '19 at 18:27

3 Answers3

3

This results in the message

To fix the message let macro arguments expand before concatenating them.

#define EXPORT_FUNCTION_MACRO_X(sys, n) EXPORT_FUNCTION_MACRO(sys, n)
EXPORT_FUNCTION_MACRO_X(INTERNAL(f), 1);
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
2

You could achieve that with a macro in the C part:

#define f(...) CONCAT(f, NUM(__VA_ARGS__, 2, 1)) (__VA_ARGS__)
#define CONCAT(X, Y) CC(X, Y)
#define CC(X, Y) X ## Y
#define NUM(X, Y, N, ...) N

#ifdef __cplusplus
extern "C"
#endif
void f_internal(void* str);

// specifically compiled for C or C++ anyway (and most likely inlined)
// -> no need for extern "C" for these:
void f1(void* str) { f_internal(str); }
void f2(void* unused, void* str) { f_internal(str); }

The macro f would select the correct function out of f1 and f2, which again would call f_internal with the correct argument. Works for both C and C++. If you prefer, you could still just provide two overloads for C++ separately and let only the C people deal with the macros.

I doubt one could call that 'elegant'; if you want to qualify as 'ugly' – up to you. But at least it works...

Aconcagua
  • 24,880
  • 4
  • 34
  • 59
1

The following code behaves like this:

  • If USE_INTERNAL is defined, defines an inline (c++) / static (c) function int f(void* str).

  • Otherwise, defines an inline (c++) / static (c) function int f(void* str1, void* str2).

Both functions are trampolines to the actual (int f_internal(void* str)) function.

Note that since the functions are defined in the header, inline (c++) / static (c) is required to keep them from violating the ODR rule (I am a c++ person, so I don't know any way better than static to achieve this in c. If there is, please let me know).

#ifdef __cplusplus
extern "C"
#endif
int f_internal(void* str);

#ifdef USE_INTERNAL

#ifdef __cplusplus
inline
#else
static
#endif
int f(void* str1, void* str2) {
    return f_internal(str2);
}

#else

#ifdef __cplusplus
inline
#else
static
#endif
int f(void* str) {
    return f_internal(str);
}

#endif
ph3rin
  • 4,426
  • 1
  • 18
  • 42