Assign any lambda-functor with any number of parameters to a callback
-
Hello; sorry that I use English on German forum.
I want an object to have a callback. So, I implemented class Callback which can do the following:
void simple_function(void) { std::cout << "Simple function also works" << std::endl; } int main(void) { Callback callback; callback = [](){}; //I really like this combination of braces callback(); //Will do nothing callback = [](){ std::cout << "1" << std::endl; }; callback(); //Will print "1" int const x = 2; callback = [&x](){ std::cout << "x = " << x << std::endl; }; callback(); //Will print "x = 2" Callback callback2(callback); callback2(); //Will print "x = 2" callback2 = simple_function; callback2(); //Will print "Simple function also works" return 0; }That is how I implemented the
Callbackclass (just for reference; no need to read this code):class CallbackBase { public: CallbackBase(void) { ; } virtual ~CallbackBase(void) { ; } virtual void operator()(void) const = 0; virtual CallbackBase *clone(void) const = 0; }; template<class FUNCTOR> class CallbackPolymorphic: public CallbackBase { public: CallbackPolymorphic(FUNCTOR const &functor): functor(functor) { ; } virtual void operator()(void) const { functor(); } virtual CallbackBase *clone(void) const { return new CallbackPolymorphic<FUNCTOR>(functor); } private: FUNCTOR const &functor; }; class Callback { public: inline Callback(void): ptr(nullptr) { ; } inline Callback(Callback const &callback): ptr( callback.ptr->clone() ) { ; } template<class FUNCTOR> inline Callback(FUNCTOR const &functor): ptr( new CallbackPolymorphic<FUNCTOR>(functor) ) { ; } inline ~Callback(void) { delete ptr; } inline Callback &operator=(Callback const &callback) { if(callback.ptr != ptr) { delete ptr; if (callback.ptr) ptr = callback.ptr->clone(); else ptr = nullptr; } return *this; } template<class FUNCTOR> inline Callback &operator=(FUNCTOR const &functor) { delete ptr; ptr = new CallbackPolymorphic<FUNCTOR>(functor); return *this; } inline void operator()(void) const { if(ptr) (*ptr)(); } inline bool empty(void) const { return !ptr; } inline void clear() { delete ptr; ptr = nullptr; } private: CallbackBase const *ptr; };So, the first question is whether I inventing the wheel and standard library has something like that? (I noticed std::function, but I do not know what is that).
The second question is how to do the trick with any number of parameters:
Callback callback; callback = [](int y){ std::cout << "y = " << y << std::endl; }; callback(9); //Want to print "y = 9"Or such things are impossible in C++ due to its staticaly-typed nature?
-
I noticed std::function, but I do not know what is that
Well, you'd be better off knowing what it is. It's not that complicated.
http://en.cppreference.com/w/cpp/utility/functional/function schrieb:
Class template std::function is a general-purpose polymorphic function wrapper. Instances of std::function can store, copy, and invoke any callable target -- functions, lambda expressions, bind expressions, or other function objects.
The second question is how to do the trick with any number of parameters:
Callback callback; callback = [](int y){ std::cout << "y = " << y << std::endl; }; callback(9); //Want to print "y = 9"Or such things are impossible in C++ due to its staticaly-typed nature?
That is possible. But it is kinda dangerous. It works through slicing and RTTI. Have a look:
#include <iostream> #include <memory> #include <cassert> struct FunctionBase { virtual ~FunctionBase() {} }; template<typename T, typename ... args> struct Function : FunctionBase {}; class Callback { void* mFunctionPtr; std::unique_ptr<FunctionBase> mSignatureCheck; public: template<typename ... argTs> void set( void(*ptr)(argTs...) ) { mFunctionPtr = reinterpret_cast<void*>(ptr); mSignatureCheck.reset( new Function<void, argTs...>() ); } template<typename ... argTs> void operator()( argTs... args ) { bool isValid = dynamic_cast<Function<void, argTs...>*>(mSignatureCheck.get()); assert( isValid && "The call is not valid - the saved function has a different signature!" ); return reinterpret_cast<void(*)(argTs...)>(mFunctionPtr)(args...); } }; void foo( int y ) { std::cout << y; } int main() { Callback c; c.set( foo ); // doesn't work for lambdas - though there surely is a way c(7); c("asdf"); // assertion }Btw, why for gods sake do you use
clone()functions when you implement an assignment-operator?
Edit: The Callback class i made has a few imperfections, for example - it has a certain runtime cost (yes,
dynamic_castis not very fast, looking up the RTTI table takes a some cycles!).Moreover, it can only save functionp pointers, not every single callable object. It does not work for lambdas like this. Although you might - as i said - surely find a way
Furthermore, the types of the arguments have to be exactly the same as the saved function signature check object. You could solve this in different ways:
- Deactivate the checks. This makes it really dangerous.
- Cast all the arguments. Unhandy.
- Built in a special way to check whether the types can be implicitely converted to each other, maybe over
std::common_typeor the like...
-
SAn schrieb:
Hello; sorry that I use English on German forum.
I want an object to have a callback. So, I implemented class Callback which can do the following:
That is how I implemented the
Callbackclass (just for reference; no need to read this code):Your implementation causes undefined behavior. The closure object generated by a lambda expressions is a temporary.
SAn schrieb:
So, the first question is whether I inventing the wheel and standard library has something like that? (I noticed std::function, but I do not know what is that).
yes.
SAn schrieb:
The second question is how to do the trick with any number of parameters:
Callback callback; callback = [](int y){ std::cout << "y = " << y << std::endl; }; callback(9); //Want to print "y = 9"Or such things are impossible in C++ due to its staticaly-typed nature?
I assume the question is to be understood such that the callback object should retain its capability to store functions without parameters.
The most general case (function objects with an arbitrary number of overloads of the function call operator) cannot be done without some kind of reflection. If you narrow the problem to one with a limited set of parameter types it's solvable with some overhead (read: code bloat). If the problem is reduced to one where the supplied arguments called with the function wrapper must match exactly with the parameter types of the stored function object its relatively simple. I can't think of a proper use case though...
-
Sone schrieb:
Edit: The Callback class i made has a few imperfections, for example - it has a certain runtime cost (yes,
dynamic_castis not very fast, looking up the RTTI table takes a some cycles!).Moreover, it can only save functionp pointers, not every single callable object. It does not work for lambdas like this. Although you might - as i said - surely find a way
Furthermore, the types of the arguments have to be exactly the same as the saved function signature check object. You could solve this in different ways:
- Deactivate the checks. This makes it really dangerous.
- Cast all the arguments. Unhandy.
- Built in a special way to check whether the types can be implicitely converted to each other, maybe over
std::common_typeor the like...
Imho it's sufficient to do the checks in debug builds. Just use them in conjunction with an assert statement and you'll be fine.
-
SAn schrieb:
Hello; sorry that I use English on German forum.
Just out of curiosity: Why do you keep doing that? IS the feedback you get here any better than, say, here?