Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Better way to call member function using function pointer?

6 views
Skip to first unread message

Ganesh Pagade

unread,
Nov 17, 2009, 3:05:42 PM11/17/09
to
Hi,

Following is isolated code reproducing the issue I encountered while
using function pointers:

//////////////////////////////////////////////
#include <iostream>
#include <string>
#include <map>

using namespace std;

class Sample
{
public:
Sample()
{
funPtrs["fun1"] = &Sample::fun1;
funPtrs["fun2"] = &Sample::fun2;
funPtrs["fun3"] = &Sample::fun3;
}

void process()
{
string str[] = { "fun1", "fun2", "fun3" };

// Method 1 - Compilation error.
for (int i = 0; i < 3; ++i)
{
// VS 2005 gives me error as:
// error C2064: term does not evaluate to a function taking 1
arguments
funPtrs[str[i]]("hi");
}

// Method 2 - Works fine.
for (int i = 0; i < 3; ++i)
{
ptr = funPtrs[str[i]];
(this->*ptr)("hello");
}
}

private:
void fun1(string str)
{
str = "fun1";
cout << str << endl;
}

void fun2(string str)
{
str = "fun2";
cout << str << endl;
}

void fun3(string str)
{
str = "fun3";
cout << str << endl;
}

private:
static map<string, void (Sample::*)(string)> funPtrs;

void (Sample::*ptr)(string);
};

map<string, void (Sample::*)(string)> Sample::funPtrs;

int main()
{
Sample s;

s.process();

return 0;
}
//////////////////////////////////////////////

To my understanding Method 1 gives error because function is called
using pointer which is not a member of Sample, due to which "this"
cannot be passed to the member function as argument. Hence the
mentioned error.

Adding a pointer as member variable specifically to make the function
calls, works fine (Method 2). I was wondering if this is the only way
to do it or there exist a better method/design to achieve this.

Regards,
Ganesh

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Alp Mestan

unread,
Nov 18, 2009, 4:31:31 AM11/18/09
to
On Nov 17, 9:05 pm, Ganesh Pagade <ganesh.pag...@gmail.com> wrote:
[snip]

> To my understanding Method 1 gives error because function is called
> using pointer which is not a member of Sample, due to which "this"
> cannot be passed to the member function as argument. Hence the
> mentioned error.
>
> Adding a pointer as member variable specifically to make the function
> calls, works fine (Method 2). I was wondering if this is the only way
> to do it or there exist a better method/design to achieve this.

As far as I know, there isn't any more practical way. However, I write
much less painfully such code using boost.bind like tools -- see [1].
Butn to sum up, when you have a member function pointer, if you want
to call it on a precise instance, you have to somehow "evaluate" that
adress w.r.t the given instance to be able to call it, thus the
dereferencing right after 'this->'.

[1] http://www.boost.org/doc/libs/1_40_0/libs/bind/bind.html#with_member_pointers

Seungbeom Kim

unread,
Nov 18, 2009, 8:48:55 AM11/18/09
to
Ganesh Pagade wrote:
>
> // Method 1 - Compilation error.
> for (int i = 0; i < 3; ++i)
> {
> // VS 2005 gives me error as:
> // error C2064: term does not evaluate to a function taking 1 arguments
> funPtrs[str[i]]("hi");
> }
>
> // Method 2 - Works fine.
> for (int i = 0; i < 3; ++i)
> {
> ptr = funPtrs[str[i]];
> (this->*ptr)("hello");
> }
>
> //////////////////////////////////////////////
>
> To my understanding Method 1 gives error because function is called
> using pointer which is not a member of Sample, due to which "this"
> cannot be passed to the member function as argument. Hence the
> mentioned error.
>
> Adding a pointer as member variable specifically to make the function
> calls, works fine (Method 2). I was wondering if this is the only way
> to do it or there exist a better method/design to achieve this.

Is there anything bad with Method 2? I don't see any.

You can, of course, skip the variable assignment and write directly:

(this->*funPtrs[str[i]])("hello");

--
Seungbeom Kim

Nick Hounsome

unread,
Nov 18, 2009, 8:49:16 AM11/18/09
to
On 17 Nov, 20:05, Ganesh Pagade <ganesh.pag...@gmail.com> wrote:
> Hi,
>
> Following is isolated code reproducing the issue I encountered while
> using function pointers:
>
> //////////////////////////////////////////////
> #include <iostream>
> #include <string>
> #include <map>
>
> using namespace std;
>
> class Sample
> {
> public:
> Sample()
> {
> funPtrs["fun1"] = &Sample::fun1;
> funPtrs["fun2"] = &Sample::fun2;
> funPtrs["fun3"] = &Sample::fun3;

This is the wrong way to go about initialising funPtrs (appologies if
this is just because you've simplified for your post).
Should probably be done by the constructor of a static object of class
SampleInit in Sample.cxx with the implementation of Sample() taken out
of line. That way it will be done once before any Sample object is
constructed.
(definition of funPtrs must come before the init because the order of
construction of statics in a single compilation unit is well defined)

> }
>
> void process()
> {
> string str[] = { "fun1", "fun2", "fun3" };
>
> // Method 1 - Compilation error.
> for (int i = 0; i < 3; ++i)
> {
> // VS 2005 gives me error as:
> // error C2064: term does not evaluate to a function taking 1
> arguments
> funPtrs[str[i]]("hi");

Pointer to a member of what object????
Yes you are calling it in a member function but you could just as
easily call it on another object - The compiler cannot assume that
just because you are in a member of "this" that you want to call the
function on "this" (see below).

> }
>
> // Method 2 - Works fine.
> for (int i = 0; i < 3; ++i)
> {
> ptr = funPtrs[str[i]];
> (this->*ptr)("hello");

but could just as easily be

(that->*ptr)("hello");

which is why 1 doesn't work.

NB there is no reason to have ptr and certainly no reason to make it a
member:

(this->*funPtrs[str[i]])("hello");

> }
> }
>
> private:
> void fun1(string str)
> {
> str = "fun1";
> cout << str << endl;
> }
>
> void fun2(string str)
> {
> str = "fun2";
> cout << str << endl;
> }
>
> void fun3(string str)
> {
> str = "fun3";
> cout << str << endl;
> }
>
> private:
> static map<string, void (Sample::*)(string)> funPtrs;
>
> void (Sample::*ptr)(string);

Not needed

> };
>
> map<string, void (Sample::*)(string)> Sample::funPtrs;
>
> int main()
> {
> Sample s;
>
> s.process();
>
> return 0;}
>
> //////////////////////////////////////////////
>
> To my understanding Method 1 gives error because function is called
> using pointer which is not a member of Sample, due to which "this"
> cannot be passed to the member function as argument. Hence the
> mentioned error.
>
> Adding a pointer as member variable specifically to make the function
> calls, works fine (Method 2). I was wondering if this is the only way
> to do it or there exist a better method/design to achieve this.

See above

> Regards,
> Ganesh

P.S. It's often helpful for this sort of mapping code to make the
strings static members of the class:

class Sample { ...
public: static const string FUN1;

That way you can maybe save a little space or time and sometimes avoid
errors, at least in test programs - The strings have to be created
somewhere and they have to be "known" so they might as well be in the
interface.

Kwall Kuno

unread,
Nov 18, 2009, 1:40:02 PM11/18/09
to
On Tue, 17 Nov 2009 14:05:42 CST, Ganesh Pagade wrote:

[snip]

> // VS 2005 gives me error as:
> // error C2064: term does not evaluate to a function taking 1
> arguments
> funPtrs[str[i]]("hi");

What's wrong with (this->*funPtrs[str[i]])("hi");
?
No extra member pointer needed.

Andrey Tarasevich

unread,
Nov 18, 2009, 1:49:42 PM11/18/09
to

No. This has absolutely nothing to do with the pointer 'ptr' being a
member or not, i.e. there's absolutely no need to introduce a member
'ptr' into your class.

The first "method" does not work simply because in C++ pointers of
'pointer-to-member' type cannot be dereferenced by '->' and '*'
operators. Instead, they have to be dereferenced with '->*' and '.*'
operators. Note, that '->*' and '.*' are completely independent
operators, different from '->' and '*'.

Pointers to members cannot be dereferenced "by themselves", since
conceptually they are not really pointing into any concrete locations.
Think of them as of "relative" pointers: they are relative to a concrete
object. (For example, in case of pointers to _data_ members, you can
think of them as _offsets_ from the beginning of the object to the
actual data member.)

So, in order to dereference a pointer of 'pointer-to-member' type you
have to supply an additional operand: a concrete object, relative to
which this pointer-to-member is supposed to be used. The object is
specified on the left-hand side of '->*'/'.*' operators.

This is what you did in your second method: you used '->*' operator with
'this' on the left-hand side. So, the pointer 'ptr' is used in
conjunction with '*this' object. Note, again, that this has nothing to
do with 'ptr' being a member of class 'Sample'. You can easily make
'ptr' a local variable

for (int i = 0; i < 3; ++i)
{

void (Sample::*ptr)(string) = funPtrs[str[i]];
(this->*ptr)("hello");
}

or you can do it without any additional variables at all

for (int i = 0; i < 3; ++i)

(this->*funPtrs[str[i]])("hello");

(This is how your "Method 1" should have looked from the very beginning).

--
Best regards,
Andrey Tarasevich

0 new messages