0

I got a simple class which just wraps a function call to a classes method. When compiling (Using Visual Studio 2013) I get the error message:

Error 2 error C2664: 'Sleipnir::Core::Delegate::Delegate(const Sleipnir::Core::Delegate &)' : cannot convert argument 2 from 'void (__cdecl *)(void *const ,int &&)' to 'void (__cdecl *)(void *const ,int &&)' d:\programmieren\delegate\delegate\delegate.h 29 1 Delegate

which is not really helpful.

My code:

    template<typename T>
    class Delegate;

    template<typename R, typename... A>
    class Delegate<R(A...)>
    {
        using TFunc = R(*)(void* const, A&&...);

    private:
        Delegate(void* const obj, TFunc stub)
            : _objectPtr(obj),
            _stubPtr(stub){}
    public:
        Delegate(R(*const funcPtr)(A...))
            : Delegate(nullptr, functionStub<R(*)(A...)>)
        {

        }
    private:
        template <R(*funcPtr)(A...)>
        static R functionStub(void* const, A&&... args)
        {
            return funcPtr(::std::forward<A>(args)...);
        }
    private:
        void*   _objectPtr;
        TFunc   _stubPtr;
    };

anyone got an idea why this happens, or how to fix it?

spongebrot
  • 13
  • 1
  • 1
    How are you instantiating the template? – David G Dec 16 '14 at 00:27
  • `Delegate del = Delegate(&test);` where test is a simple function – spongebrot Dec 16 '14 at 00:27
  • I think to pass a pointer to a function you should not preceed it's name with `&`, try without it – Oleg Andriyanov Dec 16 '14 at 00:30
  • can you show the code that uses the class template? – Cheers and hth. - Alf Dec 16 '14 at 00:31
  • 1
    `functionStub` is not right at all. The template takes a function pointer, not a type. Why not pass `funcPtr` as a parameter? – David G Dec 16 '14 at 00:31
  • if i remove the preceeding `&` it doesn't make any difference and i dont want to call the function, but pass the `functionStub` function itself to the constructor, so i can call it some time later – spongebrot Dec 16 '14 at 00:43
  • @0x499602D2: +1 for observation, but the constructor argument is a run-time thing, can't be used as template parameter. – Cheers and hth. - Alf Dec 16 '14 at 00:53
  • out of interest, are you intending the delegate to point to a member function or a free function? I ask because it stores an object pointer. – Richard Hodges Dec 16 '14 at 00:53
  • So my plan is to store both: member functions and free functions with the same class, so i can simply just call the delegates `operator(Arg...)` at some point and it executes the stored function. I think im currently not doing completely wrong, but the error message i get by the compiler just doesnt make any sense to me.. it's like cannot convert an apple to an apple... – spongebrot Dec 16 '14 at 00:57
  • What's wrong with using `std::function`? – wakjah Dec 16 '14 at 01:13

3 Answers3

1

The first problem I found was that you were referencing functionStub as functionStub<R(*)(A...)> which was wrong because functionStub takes a function pointer, not a type. The logical fix would be to pass in the address of funcPtr as functionStub<funcPtr>, but that fails as well. I believe this is because function arguments are not constant expressions so passing in the address of funcPtr is ill-formed because templates can only instantiate with constant expressions and types.

So I suggested in the comments that you should pass funcPtr to the parameter of functionStub by changing its signature to the following:

static R functionStub(R (*funcPtr)(A...), void* const, A... args);

The subsequent problem with this is that the function type differs from TFunc. You can solve this by std::bind()'ing functionStub to funcPtr first:

: Delegate(nullptr, std::bind(&functionStub, funcPtr, placeholders::_1))

This also requires changing TFunc to a std::function:

using TFunc = std::function<R (void* const, A...)>;

Now we have gotten rid of the errors, but the bind only placeholds one parameter for the call. A... could be any number of parameters so we have to use some mechanism that placeholds for a variadic number of arguments.

You can use the easy_bind() method as shown in this thread.

: Delegate(nullptr, easy_bind(&functionStub, funcPtr))

And lastly, &functionStub can't be deduced as a std::function type by easy_bind()'s arguments, so you can use a make_function() for that.

template <class Callable>
std::function<Callable> make_function(Callable* callable)
{
    return { callable };
}

// ...
: Delegate(nullptr, easy_bind(make_function(&functionStub), funcPtr))

Demo

Community
  • 1
  • 1
David G
  • 94,763
  • 41
  • 167
  • 253
0

It's failing because of this line:

using TFunc = R(*)(void* const, A&&...);

The delegate's public constructor is deferring to the private constructor, which is expecting a pointer to a function that takes a void* followed by an int. You are passing a function that takes only an int.

I suspect you're looking to build a delegate that can handle both free functions and functions that take a context parameter (such as a this pointer).

std::function takes care of all of that stuff for you. Are you able to write your delegate in terms of std::function<R(A&&...)> ? If so, you can use std::bind to store the object pointer for a member function callback.

Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
  • I am passing `static R functionStub(void* const, A&&... args)` which takes a void* and then the Args (in my case int) and not the function itself (thats why im using this wrapper function, where i will add another one which calls a classes member function). the `operator()` will look something like that: `R operator()(A... a) const { return _stubPtr(_objectPtr, ::std::forward(args)...); }` – spongebrot Dec 16 '14 at 01:01
0

Got it to work now, thank to 0x499602D2

Here's the code to my Delegate class:

    template <class Callable>
    std::function<Callable> make_function(Callable* callable)
    {
        return{ callable };
    }

    template<typename T>
    class Delegate;

    template<typename R, typename... A>
    class Delegate<R(A...)>
    {
        using TFunc = std::function<R(void* const, A...)>;
    private:
        Delegate(void* obj, TFunc stub)
            : _objectPtr(obj),
            _stubPtr(stub)
        {}
    public:
        Delegate(R(*funcPtr)(A...))
            : Delegate(nullptr, easy_bind(make_function(&functionStub), funcPtr))
        { }

        template <class C>
        Delegate(C* const objectPtr, R(C:: * const methodPtr)(A...))
            : Delegate(objectPtr, easy_bind(make_function(&methodStub<C>), methodPtr))
        { }

        template <class C>
        Delegate(C* const objectPtr, R(C:: * const methodPtr)(A...) const)
            : Delegate(objectPtr, easy_bind(make_function(&constMethodStub<C>), methodPtr))
        { }
    public:
        R operator()(A... args) const
        {
            return _stubPtr(_objectPtr, ::std::forward<A>(args)...);
        }
    private:
        static R functionStub(R(*funcPtr)(A...), void* const, A... args)
        {
            return funcPtr(std::forward<A>(args)...);
        }
        template <class C>
        static R methodStub(R(C::* const methodPtr)(A...), void* const objectPtr, A... args)
        {
            return (static_cast<C*>(objectPtr)->*methodPtr)(std::forward<A>(args)...);
        }
        template <class C>
        static R constMethodStub(R(C::* const methodPtr)(A...) const, void* const objectPtr, A... args)
        {
            return (static_cast<C*>(objectPtr)->*methodPtr)(std::forward<A>(args)...);
        }
    private:
        void*   _objectPtr;
        TFunc   _stubPtr;

And how i use it in my main class:

    void test(int x)
{
    std::cout << "Test - print: " << x << std::endl;
}

struct Printer
{
    void print(int x){
        std::cout << "Printer - print: " << x << std::endl;
    }

    void print2(int x) const{
        std::cout << "Printer - print2: " << x << std::endl;
    }
};

int main(int argc, void* args)
{

    Printer printer;

    Delegate<void(int)> del(&test);
    Delegate<void(int)> del2(&printer, &Printer::print);
    Delegate<void(int)> del3(&printer, &Printer::print2);
    del(1);
    del2(1);
    del3(1);
}
spongebrot
  • 13
  • 1
  • It's perfectly OK to answer your own question. That said, this answer seems a bit lazy to me: Posted the code that does the trick without any explanation. You can do better! :-) – TobiMcNamobi Dec 16 '14 at 12:00
  • well, @0x499602D2 explained pretty well what was wrong, and what i had to change, so i didnt think any more explanation was needed. I just wanted to post the code, so if someone else is interested in what the working code looks like, can view it now. – spongebrot Dec 16 '14 at 12:25