Member function pointer problem

Hi, everybody!

I was learning some new concepts in C++ today and heard about, yet not really understood completely, delegates.

After this I tried to implement something like this:

1
2
3
4
5
6
7
8
9
10
11
template<typename T>
class delegate{
public:
private:
    T _m_fpFunction;
};

int main(int argc, char* argv[]){
    delegate<void (*)(void)> d;
    return 0;
}


Works fine, but there's no "real" functionality. What I want to accomplish, is to create a class that will accept a member function pointer of any class. As some of you might already know, member function pointers have this restriction I'm trying to, should I say, "broke". The use of this class would be like the following:

1
2
3
4
5
6
7
8
9
10
int main(int argc, char* argv[]){
    delegate<void (*)(void)> d;

    void(Foo::*fptr)(void);
    d = fptr;

    void(Foo2::*fptr2)(void);
    d = fptr2;
    return 0;
}


But the question is, how to do this? Any suggestions?
Last edited on
Ah, I see the problem.
So, you have the delegate class instantiated based on a type of function pointer as its template argument, and you want operator= to take this kind of function pointer but as a member-function pointer.

Maybe something like this instead? It seems hard to deal with a function like that because you can't know how many parameters to give it...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
template<typename T>
class Delegate
{
    void (T::*mfp)();
public:
    Delegate &operator=(void (T::*mf)())
    {
        mfp = mf;
        return*this;
    }

    void operator()(T &t)
    {
        t.*mfp();
    }
};
I'll have to think about it to come up with anything better; C++ isn't very much of a reflective language...
Last edited on
Interesting problem. I made a little class:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <iostream>
#include <typeinfo>
#include <stdexcept>

template <typename T>
class Delegate;

template <typename R, typename ...Args>
class Delegate<R(Args...)>
{
private:
    struct Dummy {};
    typedef R(Dummy::*ptr_t)(Args...); // To accept every pointer type
public:
    template <typename T>
    Delegate(R (T::*ptr)(Args...))
     : ptr_(reinterpret_cast<ptr_t>(ptr)), ty_(&typeid(T))
     {

     }
     template <typename T>
     Delegate const& operator= (R (T::*ptr)(Args...))
     {
         ptr_ = reinterpret_cast<ptr_t>(ptr);
         ty_ = &typeid(T);
         return *this;
     }
     template <typename T>
     R operator() (T& obj, Args&&... params)
     {
         if (*ty_ != typeid(T)) // can't continue if wrong type
            throw std::runtime_error("bad delegate call");
         (obj.*reinterpret_cast<R(T::*)(Args...)>(ptr_))(std::forward<Args>(params)...);
     }
private:
    ptr_t ptr_;
    std::type_info const* ty_; // keep the type of the object
};


An exemple on how to use it:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
struct A
{
    void fct(int i)
    {
        std::cout << "A call" << i << std::endl;
    }
};

struct B
{
    void fct(int i)
    {
        std::cout << "B call" << i * 2 << std::endl;
    }
};

int main()
{
    try
    {
        A a;
        B b;
        Delegate<void(int)> d = &A::fct;
        d(a, 5);
        d = &B::fct;
        d(b, 5);
    }
    catch (std::exception& e)
    {
        std::cout << e.what() << std::endl;
    }
    return 0;
}

It work but it is limited because objects passed to operator() must have exactly the same type, the type hierarchy is not taked into account.
Feel free to ask if my code is not clear.

If you don't have variadic templates, remove it or use macros instead.

EDIT: fixed typo, added includes
Last edited on
1
2
    void(Foo::*fptr)(void);
    void(Foo2::*fptr2)(void);
¿Do you realize that the types are different?
It would be equivalent to
1
2
void (*f1)(Foo *this);
void (*f2)(Foo2 *this);
¿How are you planning to use it?
Excellent suggestions & implementations. Thanks a lot! It takes a while to explore these...

Anyway, L B's code seems quite simple yet helpful, although aquaz's code might be the most complete so far in terms of the functionality I'm looking for, but when I look at the code... Sheesh, member function pointers seem to be quite complex indeed! Great piece of code, but could there be a non-RTTI approach to achieve the goal?

¿Do you realize that the types are different?
¿How are you planning to use it?


Yes, I realize the types are different. I'm trying to break this restriction, and it seems that there are different possibilities - I'm just looking for the most efficient and the most readable solution. I don't expect you to write the whole class, although I will totally appreciate it.

More suggestions?
Last edited on
HI , LB ,

I am confused as ..also in the past i have seen this , but was not able to understand .

Why do you use this operator.

1
2
3
4
void operator()(T &t)
    {
        t.*mfp();
    }

I'm confused, too.

Could you please elaborate from these lines:
1
2
template <typename R, typename ...Args>
class Delegate<R(Args...)>

1
2
template <typename T>
R operator() (T& obj, Args&&... params)
Hi @aquaz , i am not able to get what ptr_t variable means
Hi, bluecoder!

It's a typedef:
typedef R(Dummy::*ptr_t)(Args...);

I figured out the ellipsis mechanism (...) already, but inside template arguments it looks quite odd. Also, what is this:

Args&&

I've seen similiar in the future version of C++ called C++0x, but here... I don't know, could somebody tell us what is all this?
typename ...Args is variadic template(c++11). It means there is an unspecified number of templates arguments. Then you can use it with Args... to expand the types like in class Delegate<R(Args...)> where I use template specialization to get the return type and the parameter type of the signature.
Then if for example I write Delegate<int(char, float)> d, R will be int and Args will be char, float. This technique is very useful to parse template parameters of the form T(A, B(...), ...).

In
1
2
template <typename T>
R operator() (T& obj, Args&&... params)

I expand Args to make operator() have the right signature. So Delegate<int(char, float)> d will produce something like
1
2
template <typename T>
int operator() (T& obj, char&&, float&&)


the && is an r-value reference used here for perfect forwarding. More infos:
http://en.wikipedia.org/wiki/Variadic_templates
OK, no wonder my compiler couldn't compile the code correctly. I don't have C++11 support for my compiler (Code::Blocks) yet. What compiler are you using?
I used code blocks to compile the code so it should work. I forget to add includes, i will add them to my code, maybe that's why you cannot compile.
Take a look at your compiler options, i selected gnu gcc.
I added the include files. What compiler do you use with code blocks?
bluecoder wrote:
HI , LB ,

I am confused as ..also in the past i have seen this , but was not able to understand .

Why do you use this operator.

12
13
14
15
void operator()(T &t)
    {
        t.*mfp();
    }
I used it so that you could use a delegate object as if it were a function ;)


@aquaz: Delegate<void(int)> d = &A::fct;
Shouldn't it be void(*)(int)? Correct me if it is something in the new C++11 standard...
Last edited on
No, Delegate<void(int)> d = &A::fct; is fine. I'm pretty sure it's valid in old c++ because boost use it. But you must use template specialization to take profit of it like i explained in 5 posts above.
In VC++2008, this code won't compile:
9223372036854775805
9223372036854775806
template<typename T> class C{};
C<void(int)> c; //error 
So, I guess it's only C++11. It would make sense since the compiler *should* be able to automagically know that is it a function pointer even without the (*) to help it out.
Last edited on
Did you really tried to compile it? It compile fine in my VC++ 2008 with language extensions disabled. I seriously doubt boost::spirit is written in C++11.

void(int) is not the type of a function pointer but a function signature, you can use it for many other things than function pointers.
Topic archived. No new replies allowed.