C++: Templates and member function pointers

Programming, for all ages and all languages.
Post Reply
User avatar
Alboin
Member
Member
Posts: 1466
Joined: Thu Jan 04, 2007 3:29 pm
Location: Noricum and Pannonia

C++: Templates and member function pointers

Post by Alboin »

Hello,

I have class DB:

Code: Select all

class DB {
       protected:
             map<int, string> List;
       public:
             template <class C>
             void ForEach(void (C::*callback)(int id));
};
Now, this code has a list, or a database of sorts. When ForEach is called, the list needs to be shifted through and call the callback for each item. However, to use this throughout different classes, I should imagine I should have to use templates as to create typesafe callbacks. However, I am getting errors compiling this when called in main, as below:
(Note: MDB is a global instance of DB.)

Code: Select all

class test {
	public:
		void hi(int id) {
			cout << "Hello " << id << "!" << endl;
		}
		test() {
			MDB->ForEach<test>(hi);
		}
};

int main(int argc, char *argv[]) {
	test h;
}
Also, ForEach is defined as:

Code: Select all

void DB::ForEach(void (C::*callback)(int id)) {
	/*Do the loop, etc... */
}
Finally, the error from gcc (Using gcc 4.? on Linux) is

Code: Select all

main.cc: In constructor ‘test::test()’:
main.cc:55: error: no matching function for call to ‘DB::ForEach(<unresolved overloaded function type>)’
Any help would be nice....This is an annoying problem that is probably something obvious ....Thanks!
C8H10N4O2 | #446691 | Trust the nodes.
User avatar
Colonel Kernel
Member
Member
Posts: 1437
Joined: Tue Oct 17, 2006 6:06 pm
Location: Vancouver, BC, Canada
Contact:

Re: C++: Templates and member function pointers

Post by Colonel Kernel »

Well, for one thing you are trying to use a C++ pointer-to-member function like a C# delegate -- that doesn't work. In C++, a pointer-to-member-function is a weird construct. It isn't really an address per se -- it contains just enough information to select the right method to call when an instance of that class is provided. Note that you have to provide the instance yourself, explicitly. When you pass "hi" to ForEach, "this" is not passed implicitly for you.

I'm nowhere near a C++ compiler right now, otherwise I'd try out a possible solution. What springs to mind is wrapping the instance and method together in an STL functor. Look up mem_fun or whatever it's called in the STL docs. It's defined in the <functional> header. In case you haven't heard of this technique, the way it works is basically by taking an object and method call such as o.m( x, y, z ) and wrapping it into a function object that overloads operator() so that you can call it like f( x, y, z ). A pointer or reference to the object o is stored inside the function object f.

Sorry if this is vague... I hope it helps...
Top three reasons why my OS project died:
  1. Too much overtime at work
  2. Got married
  3. My brain got stuck in an infinite loop while trying to design the memory manager
Don't let this happen to you!
User avatar
Alboin
Member
Member
Posts: 1466
Joined: Thu Jan 04, 2007 3:29 pm
Location: Noricum and Pannonia

Post by Alboin »

mem_fun seems like it is what I need. However, I can't find anything about it other than how to use it with lists and such. (Even in Stroustrup's book.) Does anyone know how to incorporate it into my own functions and classes? Thanks!
C8H10N4O2 | #446691 | Trust the nodes.
User avatar
Colonel Kernel
Member
Member
Posts: 1437
Joined: Tue Oct 17, 2006 6:06 pm
Location: Vancouver, BC, Canada
Contact:

Post by Colonel Kernel »

Alboin wrote:mem_fun seems like it is what I need. However, I can't find anything about it other than how to use it with lists and such. (Even in Stroustrup's book.) Does anyone know how to incorporate it into my own functions and classes? Thanks!
Effective STL will be of some use. I don't know of a good online STL reference offhand... at least not one that actually explains how to use mem_fun (which is a bit arcane as it turns out).

You might also want to consider using boost::bind and/or boost::function (www.boost.org).
Top three reasons why my OS project died:
  1. Too much overtime at work
  2. Got married
  3. My brain got stuck in an infinite loop while trying to design the memory manager
Don't let this happen to you!
User avatar
Candy
Member
Member
Posts: 3882
Joined: Tue Oct 17, 2006 11:33 pm
Location: Eindhoven

Post by Candy »

Did you try taking the address of hi instead of just passing hi?
User avatar
Alboin
Member
Member
Posts: 1466
Joined: Thu Jan 04, 2007 3:29 pm
Location: Noricum and Pannonia

Post by Alboin »

Edit:Check below.
Last edited by Alboin on Fri Feb 16, 2007 5:46 pm, edited 1 time in total.
C8H10N4O2 | #446691 | Trust the nodes.
User avatar
Alboin
Member
Member
Posts: 1466
Joined: Thu Jan 04, 2007 3:29 pm
Location: Noricum and Pannonia

Post by Alboin »

Aye! I go it to compile.....Almost....It compiles, but does not yet link.....yup..

Anyway, this is what I've got now:

Code: Select all


class DB {
	public:
		template <class C>
		void ForEach(C *inst, void (C::*callback)(int id));
};

template <class C>
void DB::ForEach(C *inst, void (C::*callback)(int id)) {
}

...
test() {
	DB->ForEach<test>(this, &test::hi);
}
...
Yet, I get a link error....

Code: Select all

main.o: In function `test::test()':
main.cc:(.text._ZN4testC1Ev[test::test()]+0x31): undefined reference to `void DB::ForEach<test>(test*, void (test::*)(int))'
ForEach is clearly declared with templates and all; how is it not finding it? Any ideas?
C8H10N4O2 | #446691 | Trust the nodes.
User avatar
Candy
Member
Member
Posts: 3882
Joined: Tue Oct 17, 2006 11:33 pm
Location: Eindhoven

Post by Candy »

Code: Select all

class DB {
   public:
      template <class C>
      void ForEach(C *inst, void (C::*callback)(int id));
} MDB;

class test {
   void hi(int id) {}
public:
   test() {
        MDB.ForEach<test>(this, &test::hi);
   }
};

template <class C>
void DB::ForEach(C *inst, void (C::*callback)(int id)) {
}

int main() {
        test obj;
}
That compiles, links and works here. Is this like what you're trying to do? If not, could you paste the full source so I can wreck it to work? ;)

My guess is that the compiler can't instantiate the template because you don't specify the source for the function in the header file of the template. That's required (or you'd have to make it an export template which pretty few C++ compilers support).
User avatar
Alboin
Member
Member
Posts: 1466
Joined: Thu Jan 04, 2007 3:29 pm
Location: Noricum and Pannonia

Post by Alboin »

Thanks, I hadn't realized that I had to put the source definition right in the header, but apparently that works because it now compiles. However, how do I call the callback?

Code: Select all

inst->*callback(id);
Gives me this:

Code: Select all

DB.h:46: error: must use ‘.*’ or ‘->*’ to call pointer-to-member function in ‘callback (...)’
Here's the current function:

Code: Select all

void ForEach(C *inst, void (C::*callback)(int id))  {
	inst->*callback(id);
}
(This is why I tend to stick with just C.... :wink: )
C8H10N4O2 | #446691 | Trust the nodes.
User avatar
os64dev
Member
Member
Posts: 553
Joined: Sat Jan 27, 2007 3:21 pm
Location: Best, Netherlands

Post by os64dev »

loose the * in inst->*callback(id); inst->callback(id) should compile, i think
Author of COBOS
User avatar
Alboin
Member
Member
Posts: 1466
Joined: Thu Jan 04, 2007 3:29 pm
Location: Noricum and Pannonia

Post by Alboin »

Okay. Got it.

Code: Select all

(inst->*callback)(id);
Thanks for all your help!
C8H10N4O2 | #446691 | Trust the nodes.
User avatar
Candy
Member
Member
Posts: 3882
Joined: Tue Oct 17, 2006 11:33 pm
Location: Eindhoven

Post by Candy »

Alboin wrote:Okay. Got it.

Code: Select all

(inst->*callback)(id);
Thanks for all your help!
Somebody making the C++ spec got the associativity of ->* and .* and operator() (apologies for the two ands, but using a comma would be more confusing) wrong, so you always have to use brackets around ->* and .* operations to make them work without it trying to call () on the object first, then dereferencing the target and calling it subsequently (or some other really odd order of events).
Post Reply