There are two classes A
and B
, each having functions f
and g
(with the same signatures).
The function bool is_A(uintptr_t ptr)
returns true if and only if ptr
points to an instance of A
, and similarly for is_B
.
The function A* use_A(uintptr_t ptr)
converts ptr
to A*
.
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.
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));