Home > Blockchain >  C( ): Replace function declaration with macros but not its invocations
C( ): Replace function declaration with macros but not its invocations

Time:10-20

I have a library I can't change where some function is declared, implemented and used, in single file. Lots of other stuff is done in that same file. I want to override that single function to do another thing. The issue is that when I use renaming macro, all the places where that macro/function name is found are replaced, both declarations and invocations. I want some way to rename only declaration or invocations. Then I'll be able to simply drop-in my own function under initial name as a replacement.

The code looks like this:

// library-module.cpp

int foo(int a, int b)  // declaration
{
    return a b;
}

int bar()
{
    return foo(1, 2);  // invocation
}


// main.cpp

// MAGIC HAPPENS HERE
#include "library-module.cpp"

int foo(int a, int b) // drop-in replacement
{
    return a-b;
}

int main()
{
    return bar(); // should be -1, not 3
}

Another option would be to do a vice-versa operation: to replace all invocations to some other symbol, but not the declaration. Though it's not preferred, because there might be other users of that function name in the wild.

Yet another option would be to do it in runtime, but I believe it's not that great.

Thank you.

CodePudding user response:

It may or may not be possible depending on how exactly the function is defined and used.

Assuming the exact declaration and usage you've shown, you can do this:

#define foo(a, b) FOO_LOW(CAT(DETECT_, a) CAT(DETECT_, b))(a, b)

#define CAT(a, b) CAT_(a, b)
#define CAT_(a, b) a##b

#define DETECT_int ,

#define FOO_LOW(...) FOO_LOW_(__VA_ARGS__)
#define FOO_LOW_(a, ...) CAT(FOO_IMPL, __VA_OPT__(_DECL))

#define FOO_IMPL(a, b) replacement(a, b)
#define FOO_IMPL_DECL(a, b) foo(a, b)


constexpr int foo(int a, int b) {return a   b;}
constexpr int replacement(int a, int b) {return a - b;}

static_assert(foo(100, 10) == 90);

This implementation requires C 20, and additionally MSVC needs /Zc:preprocessor and Clang (13 and earlier, see bug) needs -Wno-gnu-zero-variadic-macro-arguments. It's possible to implement with an earlier standard and without those flags, but the macro will be more convoluted.

This specific implementation has some limitations:

  • It checks if both arguments start with int, so if it happens in a function call, it will be incorrectly considered a declaration, e.g. foo(int(1), int(2)).
  • If an argument contains a , outside of ( ), you get a compilation error, e.g. foo(std::array{1,2}[0], 1).
  • If an argument begins with a punctuation, you get a compilation error, e.g. foo((1), (2)).

CodePudding user response:

The issue is that when I use renaming macro, all the places where that macro/function name is found are replaced, both declarations and invocations.

Yes.

I want some way to rename only declaration or invocations.

The preprocessor doesn't know the C or C language. It knows how to recognize identifiers, but it does not understand their C or C context, so as to distinguish declarations from other uses. Therefore, if you have a block of code that you cannot modify, then you cannot use the preprocessor to selectively replace only some appearances of a given identifier within that block, and not others.

Even if you could modify the code in question, many kinds of identifiers can be declared multiple times in the same scope, as long as they are not defined more than once. Thus, although in principle you could go through the code and insert #define and #undef directives at all the places needed for a given identifier to be recognized as a macro name in all declarative contexts, that is not easier in the general case than just modifying the declarations directly.

Overall, this just sounds like a bad idea from the beginning. It's not clear what to recommend as an alternative, however, because the question provides no detail about the higher-level goal or why the proposed approach sounded attractive.

  • Related