Originally Posted by
nvoigt
We have an expression language for one of our applications, that has variables. The expression is parsed once, a tree is built and it can be evaluated many times while the variables in the expression change. Variables have a callback, so the evaluator can request the current value.
Code:
namespace ExpressionLanguage
{
class CSAZVariable : public CSAZNamedType
{
public:
typedef CSAZValue (*GET_VALUE_CALLBACK)( const CSAZVariable&, void* );
private:
GET_VALUE_CALLBACK m_CallBack;
void* m_pUserData;
public:
CSAZValue GetValue() const;
CSAZVariable( const wstring& name, ESAZDataType datatype, GET_VALUE_CALLBACK callback, void* userdata = NULL );
};
}
As you can see, it's a simple function pointer with userdata, so basically everybody can have a static function, cast "userdata" to whatever neccessary and read the current value from there. It works fine, however it strikes me as somewhat old fashioned. More "C with classes" than C++.
Can anyone point me to a more "C++"y way of doing callbacks or post a small example? Preferably without the use of Boost, just plain STL.
The basic idea of building generic callbacks (functors) is simply to link templates to the rest of your code using virtual inheritance. Unfortunately, implementing such a thing properly requires a good smart pointer - something the STL sorely lacks (even C++0X's shared_ptr is only a slightly better choice). So...I would recommend using a well-tested functor library, personally. Sure, Boost is a pain in the ass to install, but in the long run you'd be saving yourself a lot of trouble! Anyway, in case you're still curious:
Code:
#include <memory>
struct CSAZValue
{};
struct CSAZNamedType
{};
class CSAZVariable : public CSAZNamedType
{
public:
template <typename Callback>
CSAZVariable(Callback callback)
: m_CallBack(new CSAZFunctor<Callback>(callback))
{}
CSAZValue Get()
{
return m_CallBack->Get(*this);
}
private:
struct CSAZFunctorBase
{
virtual CSAZValue Get(const CSAZVariable&) = 0;
};
template <typename Callback>
struct CSAZFunctor : CSAZFunctorBase
{
CSAZFunctor(Callback callback)
: m_CallBack(callback)
{}
virtual CSAZValue Get(const CSAZVariable& csa)
{
return m_CallBack(csa);
}
Callback m_CallBack;
};
#ifdef USING_CPP0X
private:
std::shared_ptr<CSAZFunctorBase> m_CallBack;
#else
// ...yup, std::auto_ptr plain sucks!
public:
CSAZVariable(const CSAZVariable& goner)
: m_CallBack(const_cast<CSAZVariable&>(goner).m_CallBack)
{}
private:
std::auto_ptr<CSAZFunctorBase> m_CallBack;
#endif
};
The rest is pretty straightforward. You may have to write an adapter here or there to attend to those special cases, but for the most part nothing too complicated.
Code:
#include <iostream>
struct c_style_callback
{
typedef CSAZValue (*GET_VALUE_CALLBACK)(const CSAZVariable&, void*);
c_style_callback(GET_VALUE_CALLBACK callback, void* userdata)
: m_CallBack(callback), m_UserData(userdata)
{}
CSAZValue operator ()(const CSAZVariable& csa)
{
std::cout << "c_style_callback::operator(const CSAZVariable&)" << std::endl;
return m_CallBack(csa, m_UserData);
}
GET_VALUE_CALLBACK m_CallBack;
void* m_UserData;
};
CSAZValue global_get(const CSAZVariable&)
{
std::cout << "global_get(const CSAZVariable&)" << std::endl;
return CSAZValue();
}
struct functor_get
{
CSAZValue operator ()(const CSAZVariable&)
{
std::cout << "functor_get::operator(const CSAZVariable&)" << std::endl;
return CSAZValue();
}
};
CSAZValue static_get(const CSAZVariable&, void*)
{
std::cout << "static_get(const CSAZVariable&, void*)" << std::endl;
return CSAZValue();
}
int main(void)
{
CSAZVariable g = CSAZVariable(global_get);
CSAZVariable f = CSAZVariable(functor_get());
CSAZVariable s = CSAZVariable(c_style_callback(static_get, NULL));
g.Get();
f.Get();
s.Get();
}