Pointers to member functions.

  • Thread starter Gregory L. Hansen
  • Start date
G

Gregory L. Hansen

I'm playing with a simulation where different parts of a system, like a
controller or a thermometer or a thermal mass, are represented as classes,
instantiated, and connected together like Tinkertoys. My thought at first
was that each class would have a member function like get_ouput(). When
some object like a thermal mass needs an input, like heat from a control
system, my thought was to add a pointer to that object to an internal
list, like

target.add_heat(&controller);

But I've discovered I want a little more flexibility to choose a member
function as the input. So I'd like to use something analogous to pointers
to regular functions, like

float (*pf)();
pf = controller.get_heat;
target.add_heat(pf);

But I've been playing with variations on that theme and I can't get it to
work.

Can I do this sort of thing in C++?
 
V

Victor Bazarov

Gregory said:
I'm playing with a simulation where different parts of a system, like
a controller or a thermometer or a thermal mass, are represented as
classes, instantiated, and connected together like Tinkertoys. My
thought at first was that each class would have a member function
like get_ouput(). When some object like a thermal mass needs an
input, like heat from a control system, my thought was to add a
pointer to that object to an internal list, like

target.add_heat(&controller);

But I've discovered I want a little more flexibility to choose a
member function as the input. So I'd like to use something analogous
to pointers to regular functions, like

float (*pf)();
pf = controller.get_heat;
target.add_heat(pf);

But I've been playing with variations on that theme and I can't get
it to work.

What specifically can't you get to work?
Can I do this sort of thing in C++?

It's a good question, but why are you asking us? The question is
essentially about *your* ability, so, can *you* do it? If you wanted
to ask whether it's *possible* to do in C++, then the answer is "yes".
C++ is a general purpose language, and there is almost nothing that
cannot be done with it.

V
 
L

Larry I Smith

Gregory said:
I'm playing with a simulation where different parts of a system, like a
controller or a thermometer or a thermal mass, are represented as classes,
instantiated, and connected together like Tinkertoys. My thought at first
was that each class would have a member function like get_ouput(). When
some object like a thermal mass needs an input, like heat from a control
system, my thought was to add a pointer to that object to an internal
list, like

target.add_heat(&controller);

But I've discovered I want a little more flexibility to choose a member
function as the input. So I'd like to use something analogous to pointers
to regular functions, like

float (*pf)();
pf = controller.get_heat;
target.add_heat(pf);

But I've been playing with variations on that theme and I can't get it to
work.

Can I do this sort of thing in C++?

I don't think so, unless the functions are
non-member functions.

You might read the book "Design Patterns, Elements of
Reusable Object-Oriented Software" by Gamma, Helm,
Johnson, and Vlissides.

Perhaps an MVC (Model/View/Controller) approach, or the
"Mediator" design pattern would best meet your needs.

To quote from the "Design Patterns" Mediator section:

<quote>
Define an object that encapsulates how a set of objects interact.
Mediator promotes loose coupling by keeping objects from referring
to each other explicitly, and it lets you vary their interaction
independently.
..
..
..
Colleagues send and receive requests from a Mediator object.
The mediator implements the cooperative behavior by routing
requests between the appropriate colleague(s).
</quote>

Regards,
Larry
 
A

Abecedarian

Gregory said:
But I've discovered I want a little more flexibility to choose a member
function as the input. So I'd like to use something analogous to pointers
to regular functions, like

float (*pf)();
pf = controller.get_heat;
target.add_heat(pf);

But I've been playing with variations on that theme and I can't get it to
work.
Can I do this sort of thing in C++?

Try these links:
http://www.parashift.com/c++-faq-lite/pointers-to-members.html
http://www.newty.de/fpt/index.html

::A::
 
G

Gregory L. Hansen


It confusing, I'm not sure I understand it. Looks like I need a static
wrapper for a non-static member. And if I have multiple possible
members in a single object, I need multiple static wrappers, and need to
select which one to call. So I think I would be no better off than
having a generic float output(int) member with a switch statement to
select one of several possible outputs.

It would be easier if I could do something like

class Class{
public:
int x;
int y;
};

int main()
{
Class a;
int * p;
p = &(a.x);
cout << "I just randomly decided to get " << *p << endl;
p = &(a.y);
cout << "Now the other one, " << *p << endl;
return 0;
}

But we're not supposed to make data members publicly accessible when we do
C++. We're supposed to do

class Class{
private:
int x;
public:
int get_x() {return x;}
};

I would be cast out as a pariah, but maybe I'll use public variables
anyway. At least I know how, and it saves on running code like

if (selection == kSelectX)
return x;
else if (selection == kSelectY)
return y;
else if...

again and again in loops.
 
K

Karl Heinz Buchegger

Gregory L. Hansen said:
It confusing, I'm not sure I understand it.

The main point is:
The function pointer alone is not sufficient.
You want to call a member function of an object. For this
you need 2 informations: a pointer to the object itself and
a pointer to the member function of that object.
Looks like I need a static
wrapper for a non-static member.

That's the usual way it is done, when the interface of the class where
you register the callback is already fixed and cannot be changed.
But that is not your situation. You can change that interface to pass
those 2 informations: the pointer to the object and the pointer to a member
function of that object.

Study the following example. It shows how to call a a function foo and pass
an object and a member function of that object to it:

#include <iostream>


class Class
{
public:
double twice( double Nr ) { return 2 * Nr; }
double triple( double Nr ) { return 3 * Nr; }
};

typedef double (Class::*CallBackFnct)( double );

void foo( Class* pObject, CallBackFnct Function )
{
std::cout << (pObject->*(Function))( 5 ) << std::endl;
}

int main()
{
Class a;

foo( &a, Class::twice );
foo( &a, Class::triple );
}
 
G

Gregory L. Hansen

The main point is:
The function pointer alone is not sufficient.
You want to call a member function of an object. For this
you need 2 informations: a pointer to the object itself and
a pointer to the member function of that object.


That's the usual way it is done, when the interface of the class where
you register the callback is already fixed and cannot be changed.
But that is not your situation. You can change that interface to pass
those 2 informations: the pointer to the object and the pointer to a member
function of that object.

Study the following example. It shows how to call a a function foo and pass
an object and a member function of that object to it:

Looks like a clear example, but I need to spend some time with it to see
if I understand it. And, well, there seems to be little point in
discussing it before I do.

Thanks, I appreciate it.
 
G

Gregory L. Hansen

"Gregory L. Hansen" wrote:
Study the following example. It shows how to call a a function foo and pass
an object and a member function of that object to it:

Cool, it does work for me! I made my own function to pass things to. And
reducing it to the most basic level looks like

#include <iostream>

class Class
{
public:
double twice( double Nr ) { return 2 * Nr; }
double triple( double Nr ) { return 3 * Nr; }
};

int main()
{
Class a;
Class *c;
c = &a;
double (Class::* f)(double);
f = Class::twice;
cout << "Defined my own pointer, giving " << (a.*(f))(3) << endl;
f = Class::triple;
cout << "And then " << (c->*(f))(3) << endl;
}

And no static wrapper functions. That actually looks very much like
the sort of thing I wanted to do, except I didn't know the syntax.

Thanks!
 
G

Gregory L. Hansen

Cool, it does work for me! I made my own function to pass things to. And
reducing it to the most basic level looks like

Except that my compiler keeps complaining about illegal implicit
typecasting, giving Warnings but not compilation-stopping Errors. What's
up with that?
 
K

Karl Heinz Buchegger

Gregory L. Hansen said:
Except that my compiler keeps complaining about illegal implicit
typecasting, giving Warnings but not compilation-stopping Errors. What's
up with that?

Rule number one:

if( you don't show us the actual code you have &&
don't tell us the exact error message &&
which line it refers to ) {
Nobody is able to help you
but the error is in line 42, as usual
}
else {
Someone can look at your code and try to figure out
what the problem could be
}
 
G

Gregory L. Hansen

Rule number one:

if( you don't show us the actual code you have &&
don't tell us the exact error message &&
which line it refers to ) {
Nobody is able to help you
but the error is in line 42, as usual
}
else {
Someone can look at your code and try to figure out
what the problem could be
}

In the code you gave two messages ago, in main(). Specifically

int main()
{
Class a;

foo( &a, Class::twice );
foo( &a, Class::triple );
}

gcc won't compile it, and says--

karl.cpp: In function `int main()':
karl.cpp:22: assuming pointer to member `double Class::twice(double)'
karl.cpp:22: (a pointer to member can only be formed with `&
Class::twice(double)')
karl.cpp:23: assuming pointer to member `double Class::triple(double)'

CodeWarrior will compile and run it, but warns "illegal implicit member
pointer conversion" on those two lines.
 
K

Karl Heinz Buchegger

Gregory L. Hansen said:
In the code you gave two messages ago, in main(). Specifically

int main()
{
Class a;

foo( &a, Class::twice );
foo( &a, Class::triple );
}

gcc won't compile it, and says--

karl.cpp: In function `int main()':
karl.cpp:22: assuming pointer to member `double Class::twice(double)'

Here it says: I assume that with "Class::twice" you mean a pointer
to the member function "double Class::twice(double)"
Please check, that indeed this is what you ment.
karl.cpp:22: (a pointer to member can only be formed with `&

And here it tells you what to do about it: use an '&' to explicitely
form the pointer:

foo( &a, &Class::twice );

What's complicated about that? The compiler even tells you what to do.
 
G

Gregory L. Hansen

Here it says: I assume that with "Class::twice" you mean a pointer
to the member function "double Class::twice(double)"
Please check, that indeed this is what you ment.


And here it tells you what to do about it: use an '&' to explicitely
form the pointer:

foo( &a, &Class::twice );

What's complicated about that? The compiler even tells you what to do.

Well, I tried that with

foo(&a, &(Class::twice));

and was told

karl.cpp: In function `int main()':
karl.cpp:22: Internal compiler error in resolve_offset_ref, at
cp/init.c:1886
Please submit a full bug report,
with preprocessed source if appropriate.
See <URL:http://bugzilla.redhat.com/bugzilla/> for instructions.

When I try it without the parentheses on my shell account I get a lot of
error messages related to std. There must be a library not installed or a
path not set or something. When I remove the output I get

//#include <iostream>
//using namespace std;

class Class
{
public:
double twice( double Nr ) { return 2 * Nr; }
double triple( double Nr ) { return 3 * Nr; }
};

typedef double (Class::*CallBackFnct)( double );

void foo( Class* pObject, CallBackFnct Function )
{
// cout << (pObject->*(Function))( 5 ) << endl;
double x = (pObject->*(Function))(5);
}

int main()
{
Class a;

foo( &a, &Class::twice );
foo( &a, &Class::triple );
}

(e-mail address removed): $ gcc karl.cpp
/tmp/cc8RybIb.o(.eh_frame+0x11): undefined reference to
`__gxx_personality_v0'
collect2: ld returned 1 exit status
(e-mail address removed): $


I don't understand the error message. At that point I figured something
was wrong, and didn't bother trying it with CodeWarrior. But I tried it
just now, with the ampersand and without the parentheses, and it ran just
fine.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top