Reputation: 13
I'm having issues with getting a partially-qualified function object to call later, with variable arguments, in another thread.
In GCC, I've been using a macro and typedef I made but I'm finishing up my project an trying to clear up warnings.
#define Function_Cast(func_ref) (SubscriptionFunction*) func_ref
typedef void(SubscriptionFunction(void*, std::shared_ptr<void>));
Using the Function_Cast macro like below results in "warning: casting between pointer-to-function and pointer-to-object is conditionally-supported"
Subscriber* init_subscriber = new Subscriber(this, Function_Cast(&BaseLoaderStaticInit::init), false);
All I really need is a pointer that I can make a std::bind<function_type> object of. How is this usually done?
Also, this conditionally-supported thing is really annoying. I know that on x86 my code will work fine and I'm aware of the limitations of relying on that sizeof(void*) == sizeof(this*) for all this*.
Also, is there a way to make clang treat function pointers like data pointers so that my code will compile? I'm interested to see how bad it fails (if it does).
Relevant Code:
#define Function_Cast(func_ref) (SubscriptionFunction*) func_ref
typedef void(SubscriptionFunction(void*, std::shared_ptr<void>));
typedef void(CallTypeFunction(std::shared_ptr<void>));
Subscriber(void* owner, SubscriptionFunction* func, bool serialized = true) {
this->_owner = owner;
this->_serialized = serialized;
this->method = func;
call = std::bind(&Subscriber::_std_call, this, std::placeholders::_1);
}
void _std_call(std::shared_ptr<void> arg) { method(_owner, arg); }
Upvotes: 0
Views: 128
Reputation: 24269
The problem here is that you are trying to use a member-function pointer in place of a function pointer, because you know that, under-the-hood, it is often implemented as function(this, ...)
.
struct S {
void f() {}
};
using fn_ptr = void(*)(S*);
void call(S* s, fn_ptr fn)
{
fn(s);
delete s;
}
int main() {
call(new S, (fn_ptr)&S::f);
}
But there's no guarantee this will actually work and obvious cases (virtual functions) where it probably won't.
Member functions are intended to be passed like this:
void call(S* s, void (S::*fn)())
and invoked like this:
(s->*fn)();
How people work around this when they want to support different types is to use a trampoline, which is a non-member function. You can do this with either a static [member] function or a lambda:
auto sub = new Subscriber(this, [](auto* s){ s->init(); });
or if you'd like type safety at your call site, a templated constructor:
template<typename T>
Subscriber(T* t, void(T::*fn)(), bool x);
If your Subscriber
constructor takes a std::function<void(void))>
rather than a function pointer you can pass a capturing lambda and eliminate the need to take a void*
:
new Subscriber([this](){ init(); }, false);
Upvotes: 0
Reputation: 69942
it's normally done something like this:
#include <functional>
#include <memory>
struct subscription
{
// RAII unsubscribe stuff in destructor here....
};
struct subscribable
{
subscription subscribe(std::function<void()> closure, std::weak_ptr<void> sentinel)
{
// perform the subscription
return subscription {
// some id so you can unsubscribe;
};
}
//
//
void notify_subscriber(std::function<void()> const& closure,
std::weak_ptr<void> const & sentinel)
{
if (auto locked = sentinel.lock())
{
closure();
}
}
};
Upvotes: 0