c++pointerstemplatesmacrosvariadic-macros

Use pointer and call function in C++


In the code, the following pattern is common:

answer = is_A(ptr) ? use_A(ptr)->f() : use_B(ptr)->f();
...
answer = is_A(ptr) ? use_A(ptr)->g() : use_B(ptr)->g();

Problem: is it possible to simplify this code (e.g. with a macro or a template)?

I tried the following macro:

#define call(ptr, func) (is_A(ptr) ? use_A(ptr)->func() : use_B(ptr)->func())

however I am getting "expression func cannot be used as a function".

Note: @Jarod42 and Pete Becker pointed out that this does compile. My use case turned out to be more specific than the above example. Either way, the accepted answer below solves the problem in my use case.

Note: as pointed out, this is easily and cleanly solved with virtual methods, however these tend to perform much slower in practice. I am looking for something lightweight.


Solution

  • One safe solution would be a function template:

    template<typename ReturnType>
    ReturnType call(uintptr_t ptr, ReturnType (A::*aFunc)(), ReturnType (B::*bFunc)()) {
        if (is_A(ptr)) { return (use_A(ptr)->*aFunc)(); }
        return (use_B(ptr)->*bFunc)();
    }
    

    Usage example:

    auto answer = call(ptr, &A::f, &B::f);
    auto anotherAnswer = call(ptr, &A::g, &B::g);
    

    If A::f or B::f require arguments, this solution can be extended to:

    template<typename ReturnType, typename... Args>
    ReturnType call(uintptr_t ptr, ReturnType (A::*aFunc)(Args...), ReturnType (B::*bFunc)(Args...), Args&&... args) {
        if (is_A(ptr)) { return (use_A(ptr)->*aFunc)(std::forward<Args>(args)...); }
        return (use_B(ptr)->*bFunc)(std::forward<Args>(args)...);
    }
    

    Usage example:

    auto answer = call(ptr, &A::f, &B::f);
    auto anotherAnswer = call(ptr, &A::g, &B::g, 1, 2);
    

    Macros are unsafe, bug-prone and generally recommended to be avoided. However, provided macro example works as long as both A::f and B::f take no arguments: call(ptr, f). If arguments support is needed, macro can be simplified to:

    #define call(ptr, func) (is_A(ptr) ? (use_A(ptr)->func) : (use_B(ptr)->func))
    

    Usage example:

    auto answer = call(ptr, f());
    auto anotherAnswer = call(ptr, g(42, 7));