-
-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to initialize delegate with function pointer #2
Comments
Hi @quangr, thanks for reaching out -- I'm happy to hear you liked my blog post!
Are you wanting just a regular old free-function pointer, or class member-pointers? The restriction of C++11 makes this extremely tricky, although it is possible. The approach taken in this repo for C++17 will largely be similar to what is done in C++11, except when working in C++11 you won't have Free FunctionsThis can be solved in two possible ways: Statically-bound (as template parameters, like the blog-post talks about), or as a runtime-provided value (via the constructor) Runtime-ProvidedRuntime-provided free-functions can be solved with a little "trick", which I actually use in this repo's With this in mind, what you could do is have a data-member that stores some common pointer type (say, This differs from what I wrote in the blog post in some minor ways:
The stub then just casts the For example: template <typename R, typename...Args>
class Delegate<R(Args...)> {
public:
// Would be a good idea to use SFINAE to make sure this is only called with valid function pointers
template <typename UR, typename...UArgs>
Delegate(UR(*)(UArgs...)); // will implement below
...
private:
// [expr.reinterpret.cast/6] explicitly allows for a conversion between
// any two function pointer-types -- provided that the function pointer type
// is not used through the wrong pointer type.
// So we normalize all pointers to a simple `void(*)()` to allow for pointers
// bound in the constructor.
using any_function = void(*)();
// Note: the stub now takes 'const Delegate&' now so we can pull out the m_function_pointer
using stub_function = R(*)(const Delegate&, Args...);
union {
// .. other storage types ...
any_function m_function_pointer;
};
stub_function m_stub;
// These template parameters are used to cast back to the original function pointer
// type
template <typename UR, typename...UArgs>
static auto function_pointer_stub(const delegate& d, Args...args) -> R {
// cast back to the original type before calling
const auto original = reinterpret_cast<UR(*)(UArgs...)>(d.m_function_pointer);
return (*original)(std::forward<Args>(args)...);
}
}; Then your constructor just becomes: template <typename R, typename...Args>
template <typename UR, typename...UArgs>
Delegate<R(Args...)>::Delegate(UR(*fn)(UArgs...))
: m_any_function{reinterpret_cast<any_function>(fn)},
m_stub{&function_pointer_stub<UR,UArgs...>}
{
} With this, it should allow you to pass any function pointer to a The above supports conversion-based binding (e.g. you can bind an Statically-SpecifiedStatically-specified values in the constructor are a whole different class of problem. The issue is that the constructor needs the context of the template non-type argument, but doesn't have it. The way this library solves the problem is by creating // Note the 'bind<...>()' call
delegate<std::size_t(const char*)> d{bind<&std::strlen>()}; This is easy to do in C++17 with delegate<std::size_t(const char*)> d{bind<std::size_t(const char*), &std::strlen>()}; It works, but it's not quite as nice. The implementation of this would look something like: template <typename Fn, Fn* FunctionPointer>
struct function_bind_target {};
// User specified R(Args...) as the first argument, then a pointer of that type
template <typename Fn, Fn* FunctionPointer>
constexpr auto bind() -> function_bind_target<Fn,FunctionPointer> { return {}; }
...
template <typename R, typename...Args>
class Delegate<R(Args...)> {
...
template <typename UR, typename...UArgs, UR(*FunctionPointer)(UArgs...)>
Delegate(function_bind_target<UR(UArgs...),FunctionPointer>)
: m_stub{nonmember_stub<UR(UArgs...),FunctionPointer>}
{
}
...
} Member FunctionsRuntime-ProvidedRuntime-specified member pointers can't really be done without heap allocation. Unlike function pointers, member pointers do not provide any simple guarantee of conversions -- meaning there's no nice way to erase them at runtime (meaning a constructor of Statically-SpecifiedThis is basically the same answer as the statically-specified point for function pointers above, except you would need to extend it to include the class type. Anyway, I didn't mean for this post to run on as long as it has -- but hopefully this makes sense. Please let me know if any of that was unclear, or if there's anything else I can help with. Good luck! |
WOW, what a detailed AMAZING replay! Thanks for your kindness. It really opened my mind and I appreciate it. I still need to write some code to fully understand it, but there are some tricks I can understand and adapt right away, it is certainly THE most informative reply I have ever gotten from the internet! THANK you very much, have a nice day! |
Dear Matthew Rodusek, THANKS for writing such an amazing blog about how to creating delegate type in c++. It really helps a lot. I'm trying to create a timer with a function pointer. I want it to do something like this.
However, after mimic the delegate type, the best I can do is something like this.
I'm wondering if there's something that I can do to bind the function pointer in constructor in C++11. I'm really hoping to get some advice from a c++ expert like you. THANKS again for your fascinating post.
The text was updated successfully, but these errors were encountered: