Examples on C++ callbacks for a template class

Started by
20 comments, last by markgame66 14 years, 5 months ago
Hi All, I've looked around on the net and whilst there are plenty of examples I can't find a page which shows clearly (to me at least) what I need to do to create a callback from my template class to another class. Basically what I have is a template class which holds generic data structures, and another class which has a specific instance of the template class. I want to be able to save the data and figured that if I can somehow create a call back from the template class that would work nicly. Just can't figure out how to do it. Any examples/help would be great. Thanks Mark
Advertisement
i can't really understand what you want to do.

do you want something like a class that can call any method from any instance of any type?

something like this??:
Callback a,b;b.set(MyInstance,&MyType::MyMethod);// MyInstance is of MyType which can anythinga=b;a();// will call MyInstance.MyMethod();
Sorry for the generic names, but it's hard to give concrete ones without more context.

This is how I'd do it:

#include <string>#include <iostream>template <typename T>class Holder {public:	class Callbackable {	public:		virtual void Callback(const T& t) = 0;	};	Holder(const T& t, Callbackable& callback) : t(t), callback(callback) {}	void DoCallback() {		callback.Callback(t);	}private:	T t;	Callbackable& callback;};class AnotherClass : public Holder<std::string>::Callbackable {public:	AnotherClass() : holder("Quack!", *this) {}	virtual void Callback(const std::string& t) {		std::cout << "AnotherClass::Callback: " << t << "\n";	}	void DemoTheCallback() {		holder.DoCallback();	}private:	Holder<std::string> holder;};int main() {	AnotherClass ac;	ac.DemoTheCallback();}


The class that wants to have the callback inherits from an interface (Holder<T>::Callbackable), and defines the callback. It can then pass *this into Holder<T>'s constructor as the class instance with the callback.
I have something like:

<code>
template <class T> class map
{

void forEachElement(ProcessMethod pPethod)
{
for (Map::iterator it = m_map.begin(); it != m_map.end(); it++)
{
pMethod(it->second);
}
}

};

</code>

and what I want is to be able to have a instance of map in another class say X, but be able to call forEachElement, passing it a method from X, which will be responsible for the specific processing type.

Thanks

Mark
Try a function pointer.

#include <string>#include <iostream>template <typename T>class map {public:	map(const T& a, const T& b, const T& c) : a(a), b(b), c(c) {}	template <typename C>	void forEachElement(C& inst, void (C::*callback)(const T&)) {		// example use of callback		(inst.*callback)(a);		(inst.*callback)(b);		(inst.*callback)(c);	}private:	T a, b, c;};class X {public:	X() : m("foo", "bar", "baz") {}	void callback(const std::string& t) {		std::cout << "X::callback: " << t << "\n";	}	void demoTheCallback() {		m.forEachElement(*this, &X::callback);	}private:	map<std::string> m;};int main() {	X x;	x.demoTheCallback();}


Alternatively, you could do it like the STL, and pass in a function object.

(I hope you're not rewriting std::map!)
Here you go:

Callback.h:
class __iCallback	{	private:		inline __iCallback()		{			RefCount = 0;		}	private:		virtual void __CallMethod()=0;	private:		unsigned int RefCount;	private:		friend class Callback;		template <class T>			friend class __CallBack;	};class Callback 	{	public:		Callback();		~Callback();		Callback(const Callback &other);		Callback(__iCallback *prv);	public:		void operator = (const Callback &other);		void operator = (__iCallback *prv);		void Detach();		inline bool isEmpty()const		{			return (method == 0);		}	public:		inline void exec()		{			if(method)				method->__CallMethod();		}		inline void operator()()		{			if(method)				method->__CallMethod();		}		//------------------------------------------------	private:		__iCallback *method;	};	//------------------------------------------------	//------------------------------------------------	template <class T>	class __CallBack : public __iCallback	{	private:		typedef void (T::*CallProcedure)();	public:		__CallBack(T * s ,void (T::*m)())			:source(s),method(m)		{}	public:		virtual void __CallMethod()		{			(source->*method)();		}	private:		T * source;		CallProcedure method;	private:		friend class Callback;	};//----------------------------------template <typename OBJ>	inline __iCallback* callback(OBJ * what, void(OBJ::*method)())	{		return new __CallBack<OBJ>(what,&method);	}

Callback.cpp
Callback::Callback()	: method(0){}Callback::~Callback(){	if(method)	{		method->RefCount--;		if(method->RefCount==0)			delete method;	}}Callback::Callback(const Callback& other)	:method(other.method){	method->RefCount++;}Callback::Callback(__iCallback* prv){	method = prv;	method->RefCount++;}void Callback::Detach(){	if(method)	{		method->RefCount--;		if(method->RefCount==0)			delete method;	}	method = 0;}//---------------------------------------------void Callback::operator =(const Callback& other){	if(method)	{		method->RefCount--;		if(method->RefCount==0)			delete method;	}	method = other.method;	method->RefCount++;}void Callback::operator = (__iCallback* prv){	if(method)	{		method->RefCount--;		if(method->RefCount==0)			delete method;	}	method = prv;	method->RefCount++;}

Usage:
Callback m;// this object will call any function from any object
m = callback(InstanceOfObject,&TypeOfObject::MethodToCall);// this is how you associate m with an instance and a method
m() or m.exec()// is how you call the method, equivalent with InstanceOfObject->MethodToCall();

It is easy to modify this so that you can call methods with arguments.

Raxvan.

Edit:
your example now is:
X yourInstance;pPethod = callback(yourInstance,&X::Method);void forEachElement(Callback pPethod){for (Map::iterator it = m_map.begin(); it != m_map.end(); it++){pMethod(it->second);// suppose that you added support for arguments}}
mattd, I've had a go with your example in my app, getting a compilation error:

error C2784: 'void C3DHashtable<T>::forEachElement(C &,void (__thiscall C::* )(const T & ))' : could not deduce template argument for 'void (__thiscall T1::* )(const T & )' from 'void (__thiscall X::* )(const GridCell &)'
with
[
T=GridCell *
]
and
[
T=GridCell *
]


Can you see where I've got this wrong. An abridged version of my code below (I'm not 100% about the functionality, but is does at least exibit the same compilation problems.

Thanks

Mark


#include "stdafx.h"#include <string>#include <iostream>#include <map>#include <algorithm>#include <vector>using namespace std;static const C_numberOfDimensions = 3;class C3DHashtableKey	{		public:			C3DHashtableKey(const int iX, const int iY, const int iZ)			{				m_iCoord[0] = iX;				m_iCoord[1] = iY;				m_iCoord[2] = iZ;			}			bool operator<(const C3DHashtableKey & rhs) const			{				return std::lexicographical_compare(m_iCoord, m_iCoord + C_numberOfDimensions, rhs.m_iCoord, rhs.m_iCoord + C_numberOfDimensions);			}		/*	bool operator==(const C3DHashtableKey & rhs) const			{				return std::equal_range(m_iCoord, m_iCoord + C_numberOfDimensions, rhs.m_iCoord, rhs.m_iCoord + C_numberOfDimensions);			}		*/		private:			int m_iCoord[C_numberOfDimensions];	};template<class T> class C3DHashtable{private:	std::map<C3DHashtableKey, T>	m_map;public:	C3DHashtable(void)	{	}	~C3DHashtable(void)	{	}	void Insert(const int x, const int y, const int z, T value)	{		m_map.insert(std::make_pair(C3DHashtableKey(x, y, z), value));	}	template <class C>	void forEachElement(C& inst, void (C::*callback)(const T&))	{		for (std::map<C3DHashtableKey, T>::iterator it = m_map.begin(); it != m_map.end(); it++)		{			(inst.*callback)(<C3DHashtableKey, T>);		}			}	size_t getNoOfMapElements()	{		return m_map.size();	}	};struct GridCell   {	// Alternatice constructor for basic setting up of a cell for path finding	GridCell( double X,	double Y, long Z)	{		m_X = X;		m_Y = Y;		m_Z = Z;	}	double m_X;	double m_Y;	double m_Z;};template <typename T>class map {public:	map(const T& a, const T& b, const T& c) : a(a), b(b), c(c) {}	template <typename C>	void forEachElement(C& inst, void (C::*callback)(const T&)) {		// example use of callback		(inst.*callback)(a);		(inst.*callback)(b);		(inst.*callback)(c);	}private:	T a, b, c;};class X {public:	X()	{		Map2.Insert(1,2,3, new GridCell(1,2,3));		Map2.Insert(4,5,6, new GridCell(4,5,6));		Map2.Insert(7,8,9, new GridCell(7,8,9));	}		//: m("foo", "bar", "baz") {}	void callback(const GridCell& t) 	{	}	void demoTheCallback() 	{		Map2.forEachElement(*this, &X::callback);	}private:	C3DHashtable<GridCell*> Map2;};int main() {	X x;	x.demoTheCallback();}
This should help you
This helped me alot

http://www.newty.de/fpt/
You might also take a look at boost::function.

"I can't believe I'm defending logic to a turing machine." - Kent Woolworth [Other Space]

This topic is closed to new replies.

Advertisement