Jump to content
  • Advertisement
Sign in to follow this  
markgame66

Examples on C++ callbacks for a template class

This topic is 3170 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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

Share this post


Link to post
Share on other sites
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 anything
a=b;
a();// will call MyInstance.MyMethod();

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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!)

Share this post


Link to post
Share on other sites
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
}
}

Share this post


Link to post
Share on other sites
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();
}

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!