Reputation: 841
I have two structs, struct B inherits from struct A. I want to pass a function as a parameter from struct B to struct A for struct A to use.
This is an example of what I want to achieve and the issue I'm running into, the full code would be TL;DR.
struct A
{
int32 mynum;
void Tick(float delta, bool doThis, void (funcparam)(float, bool, int32))
{
funcparam(delta, doThis, mynum);
}
};
struct B : public A
{
void RunThis(float deltaTime, bool doIt, int32 which)
{
// do something when called by Struct A
};
void DoSomething()
{
Tick(0.1f, true, &B::RunThis);
};
};
The issue is with this line: Tick(0.1f, true, &B::RunThis);
from the function void DoSomething()
unless I've done it wrong from the beginning, but I imagine I am passing it wrong since it's still within the header of the struct that I'm currently defining?
Error (I have modified the error to fit my example, I don't think I messed up..):
error C2664: 'void A::Tick(float,bool,void (__cdecl *)(float,bool))': cannot convert argument 3 from 'void (__cdecl B::* )(float,bool)' to 'void (__cdecl *)(float,bool)'
Omitting the B::
from &B::RunThis
of course doesn't solve anything.
Upvotes: 0
Views: 2081
Reputation: 114481
Unfortunately C++ function pointers do not have context... in other words a pointer to function accepting an integer
void (*f)(int x)
is not allowed to have any data "bound" to the pointer and can only access parameters and global variables. A work-around implemented for some compilers are "trampoline" libraries that allow binding custom data to function pointers, but they all rely on dynamic code creation at runtime, something not possible within the boundaries of standard C++.
The solution in standard C++ is to use
std::function<void(int)>
instead of
void (*)(int)
An std::function
object can keep data associated with the function and allows the creation of closures. It is also backward compatible with function pointers (i.e. a function pointer can be implicitly converted to an std::function
object).
Using that your code can become for example
struct A {
int32 mynum;
void Tick(float delta, bool doThis,
std::function<void(float, bool, int32)> funcparam) {
funcparam(delta, doThis, mynum);
}
};
struct B : public A {
void RunThis(float deltaTime, bool doIt, int32 which) {
// do something when called by Struct A
};
void DoSomething() {
Tick(0.1f, true, [this](float dt, bool di, int32 w) {
this->RunThis(dt, di, w);
});
}
};
where a lambda has been used to call the method.
A portable workaround if you cannot change the code accepting a function pointer is to have a preallocated set of function pointers for a given signature... for example
void *closure_data[10];
void (*closure_code[10])(void *, int);
void closure0(int x){ closure_code[0](closure_data[0], x); }
void closure1(int x){ closure_code[1](closure_data[1], x); }
void closure2(int x){ closure_code[2](closure_data[2], x); }
...
void closure9(int x){ closure_code[9](closure_data[9], x); }
void (*closures[10])(int) = {closure0,
closure1,
...
closure9};
and then you need to "allocate" a closure i
, putting your context data in the corresponding closure_data[i]
, the code in closure_code[i]
and pass as function pointer closures[i]
.
Normally decent C++03 code or C code accepting function pointers for callbacks always provide a context parameter... i.e. the interface has the form
void Tick(void *context,
void (*callback)(float, bool, int32, void *))
and will call the callback also passing the opaque void *context
specified by the caller that provided the function pointer.
Upvotes: 0
Reputation: 11885
First option: Use a virtual function instead.
Works best if you have 1 and only 1 function per derived class anyway.
struct A {
virtual void RunThis(float deltaTime, bool doIt, int32 which) = 0;
void Tick(float delta, bool doThis )
{
//...
RunThis(delta, doThis, which);
// ...
}
virtual ~A() // virtual destructor...
{}
};
struct B : public A
{
virtual void RunThis(float deltaTime, bool doIt, int32 which )
{
}
void DoSomething(/*...*/)
{
// ...
Tick(/*...*/);
}
};
Second option: std::function + lambda or member function in struct B
#include <functional>
struct A
{
void Tick(float delta, bool doit, std::function<void(float,bool,int32)> action )
{
}
};
struct B : public struct A
{
void DoSomething( /*...*/ )
{
// you can use a lambda, if convenient...
Tick(/*...*/,
[this](float delta, bool doit, int32_t which) -> void {
// ...
});
}
// or you can also use a pointer to member:
void RunThis(/*...*/) { /* ... */ }
void DoSomething(/*...*/)
{
std::function<void(float,bool,int32)> f = std::bind( &B::RunThis, this, _1,_2,_3);
Tick(/*...*/, f);
}
};
Upvotes: 1