I want to implement a generic function returning a default value, and then use it through a function pointer as a replacement for other functions with different prototypes.
For example:
int replacement() {
return 43;
}
int (*abstract)(int i, const char *c);
int main() {
abstract = reinterpret_cast<decltype(abstract)>(replacement);
int res = (*abstract)(55, "Test");
std::cout << "Result is " << res << std::endl;
}
This code works, and actually does what I expect, but according to the standard it's undefined behaviour. Or is it?
Can I rely on the ABI to do the right thing? The parameters are passed in registers or pushed onto the stack, then if they are not used, it shouldn't be a problem. The reinterpret_cast
or other equivalent cast is used for example in GTK+ and in Vulkan to pass a generic function type as pointer and letting the developers un-cast the right function type on the other end.
Is it just working by chance, or can I rely on this behaviour?
It's working purely by chance. You can never rely on undefined behavior.
.
int stub() { return 42; }
int real(int i) { return i * 2; }
void foo(bool active) {
int (*ptr)(int);
if (active) {
ptr = ℜ
} else {
ptr = reinterpret_cast<int (*)(int)>(&stub);
}
int arg = ptr(99);
if (active) {
submit_request(arg);
}
}
The compiler could see that active == false
leads to UB and therefore optimize foo
under the assumption that active == true
always.
(No compiler I've tried does that, but that only means they haven't bothered to add such an optimization yet.)
Here's what you should do instead: variadic templates let you define a stub for any signature you need:
#include <cstdio>
void submit_request(int);
template <typename... Args>
int stub(Args...) {
std::printf("stub");
return 42;
}
int real(int i) { return i * 2; }
void foo(bool active) {
int (*ptr)(int);
if (active) {
ptr = ℜ
} else {
ptr = static_cast<int (*)(int)>(&stub);
}
int arg = ptr(99);
if (active) {
submit_request(arg);
}
}