I have an Event class that is written in half C and half C++11.
It currently does not work with lambdas nor std::functions, only free functions or member functions.
It is very hard to use properly (I've never gotten the plain Subscribe method to compile when used) and the use of void* and raw function pointers is just gross.
I'd like to get it up-to-date in terms of C++17 with proper variadic template types, working with lambdas and std::function's and hopefully only have one public set of subscribe/unsubscribe methods that just work with anything I give it.
Event.hpp
#pragma once
#include <vector>
template <typename... ARGS>
class Event {
public:
struct event_sub_t;
using cb_t = void(*)(event_sub_t*, ARGS...);
using cb_with_arg_t = void(*)(void*, ARGS...);
struct event_sub_t {
cb_t cb;
void *secondary_cb;
void *user_arg;
};
Event() = default;
~Event() = default;
void Subscribe(void *user_arg, cb_with_arg_t cb) {
event_sub_t sub;
sub.cb = FunctionWithArgumentCallback;
sub.secondary_cb = cb;
sub.user_arg = user_arg;
subscriptions.push_back(sub);
}
void Unsubscribe(void *user_arg, void* cb) {
subscriptions.erase(std::remove_if(std::begin(subscriptions),
std::end(subscriptions),
[&cb, &user_arg](const event_sub_t& sub) {
return (sub.secondary_cb == cb) && (sub.user_arg == user_arg);
}),
std::end(subscriptions));
}
void Unsubscribe_by_argument(void *user_arg) {
subscriptions.erase(std::remove_if(std::begin(subscriptions),
std::end(subscriptions),
[&user_arg](const event_sub_t& sub) {
return sub.user_arg == user_arg;
}),
std::end(subscriptions));
}
template <typename T>
void Subscribe_method(T *obj, void (T::*mcb)(ARGS...)) {
event_sub_t sub;
sub.cb = MethodCallback<T, decltype(mcb)>;
sub.secondary_cb = *(void**)(&mcb);
sub.user_arg = obj;
subscriptions.push_back(sub);
}
template <typename T>
void Unsubscribe_method(T *obj, void (T::*mcb)(ARGS...)) {
Unsubscribe(obj, *(void**)&mcb);
}
template <typename T>
void Unsubscribe_object(T *obj) {
Unsubscribe_by_argument(obj);
}
void Trigger(ARGS... args) {
for(auto& sub : subscriptions) {
sub.cb(&sub, std::forward<ARGS>(args)...);
}
}
private:
std::vector<event_sub_t> subscriptions;
static void FunctionWithArgumentCallback(event_sub_t *sub, ARGS... args);
template <typename T, typename MCB>
static void MethodCallback(event_sub_t *sub, ARGS... args);
};
template <typename ...ARGS>
void Event<ARGS...>::FunctionWithArgumentCallback(event_sub_t *sub, ARGS... args) {
cb_with_arg_t cb = (cb_with_arg_t)(sub->secondary_cb);
cb(sub->user_arg, std::forward<ARGS>(args)...);
}
template <typename ...ARGS>
template <typename T, typename MCB>
void Event<ARGS...>::MethodCallback(event_sub_t *sub, ARGS... args) {
MCB mcb = *(MCB*)&(sub->secondary_cb);
T *obj = (T*)(sub->user_arg);
(obj->*mcb)(std::forward<ARGS>(args)...);
}
Current Usage:
class Foo {
public:
//...
void Update() { OnEventFoo.Trigger(text); }
Event<const std::string&> OnEventFoo{};
private:
std::string text{};
};
//Foo::Update is called somewhere in other code...
//Bar subscribes/unsubscribes to Foo's event.
//Doesn't have to be RAII, can be as simple as putting
//the subscribe/unsubscribe calls before and after some other function call.
class Bar {
public:
std::string text{};
explicit Bar(Foo& foo)
: _foo(foo)
{
foo.OnEventFoo.Subscribe_method(this, &Bar::Thing2);
}
~Bar() {
foo.OnEventFoo.Unsubscribe_method(this, &Bar::Thing2);
}
void Thing2(const std::string& text) {
std::cout << "Calling " << __FUNCTION__ << " with " << text;
}
private:
Foo _foo{};
};
Intended Usage:
//...Foo and Bar classes and stuff
static auto bar_lambda = [bar](const std::string& text){ bar.Thing2(text) };
foo.Subscribe(bar_lambda, "Hello Bar!");
foo.Subscribe(Bar::Thing2, bar.text);
foo.Subscribe(FreeOrStdFunction, "Free Bar!");
//...
foo.Unsubscribe(Bar::Thing2);
foo.Unsubscribe(FreeFunction);
foo.Unsubscribe(bar_lambda);
Not sure to understand what do you need.
But seems to me that you need std::bind().
Anyway... if arguments for a single callable are passed in Subscribe(), it seems to me that Event doesn't needs to be a template class anymore and that the std::vector of std::function is something as follows
private:
std::vector<std::function<void()>> subsV;
I mean: a vector of std::function's of type void().
You can populate it through the following method
template <typename F, typename ... Args>
std::size_t Subscribe (F const & f, Args const & ... as)
{
subsV.emplace_back(std::bind(f, as...));
return subsV.size() - 1u;
}
Observe that with a simple callable (not non-static class/struct method) you have to call it passing first the callable and next the arguments
auto i1 = e.Subscribe(
[](int, long){ std::cout << "l1" << std::endl; }, 0, 1l);
but calling it with a non-static method you have to pass first the pointer to the method, second a object or a pointer to a object (works in both cases) to of the class and last the arguments for the method.
foo f;
// ...............................V works with objects
auto i2 = e.Subscribe(&foo::func, f, "string 1");
auto i3 = e.Subscribe(&foo::funv, &f, "string 2");
// ...............................^^ and works with pointers
For Unsuscribe() i suggest to pass the index of the subscription (returned by Subscribe()
void Unsubscribe (std::size_t idx)
{ subsV.at(idx) = nullptr; }
and the Trigger() simply become
void Trigger ()
{
for ( auto & sub : subsV )
if ( sub )
sub();
}
The following is a full compiling example (should works also with C++11)
#include <vector>
#include <iostream>
#include <functional>
class Event
{
private:
std::vector<std::function<void()>> subsV;
public:
Event() = default;
~Event() = default;
template <typename F, typename ... Args>
std::size_t Subscribe (F const & f, Args const & ... as)
{
subsV.emplace_back(std::bind(f, as...));
return subsV.size() - 1u;
}
void Unsubscribe (std::size_t idx)
{ subsV.at(idx) = nullptr; }
void Trigger ()
{
for ( auto & sub : subsV )
if ( sub )
sub();
}
};
struct foo
{
void func (std::string const & s)
{ std::cout << "foo::func(): " << s << std::endl; }
};
int main()
{
Event e;
foo f;
auto i1 = e.Subscribe(
[](int, long){ std::cout << "l1" << std::endl; }, 0, 1l);
auto i2 = e.Subscribe(&foo::func, f, "string 1");
auto i3 = e.Subscribe(&foo::func, &f, "string 2");
e.Trigger();
e.Unsubscribe(i2);
e.Trigger();
e.Unsubscribe(i1);
e.Trigger();
e.Unsubscribe(i3);
e.Trigger();
}
Related
I have a code that takes messages from flash player in a form of XML parse them into function and arguments and calls a registered callback for that function.
The piece of code that I want to replace is something nicely done (almost) generic Callback mechanism:
code for the generic callback implementation of flashSDK (ASInterface.inl).
The problem with it is that this code is written for flash and I want to replace the flash and use other service that will have the same interface. Is there any standard implementation of this callback mechanism (std? boost? something else open sourced?)?
This code implements generic callbacks mechanism that you can register function with number of arguments and types in a map:
void SomethingHappened(int a, int b) {print a + b;}
void SomethingElseHappened(string abcd) {print abcd;}
callbacks["SomethingHappened"] = &SomethingHappened;
callbacks["SomethingElseHappened"] = &SomethingElseHappened;
and than search for it and call with an array of arguments:
Callbacks::iterator itCallback = callbacks.find(functionName);
if (itCallback != callbacks.end())
{
HRESULT result = itCallback->second.Call(arguments, returnValue);
}
full usage example:
//init callbacks
std::map<std::wstring, Callback> callbacks;
void SomethingHappened(int a, int b) {print a + b;}
void SomethingElseHappened(string abcd) {print abcd;}
callbacks[functionName] = &SomethingHappened;
void MessageArrived(string xmlInput)
{
string functionName = parseFunctionName(xmlInput);
Callbacks::iterator itCallback = callbacks.find(functionName);
if (itCallback != callbacks.end())
{
//parse arguments
std::vector<std::wstring> args;
_Args::split(xml, args);
ASValue::Array arguments;
for (size_t i = 0, s = args.size(); i < s; ++i)
{
ASValue arg; arg.FromXML(args[i]);
arguments.push_back(arg);
}
ASValue returnValue;
//***this is where the magic happens: call the function***
HRESULT result = itCallback->second.Call(arguments, returnValue);
return result;
}
}
You probably need a wrapper around std::function, something like:
template <typename T> struct Tag{};
// Convert ASValue to expected type,
// Possibly throw for invalid arguments.
bool Convert(Tag<Bool>, AsValue val) { return (Boolean)val; }
int Convert(Tag<int>, AsValue val) { return (Number)val; }
// ...
struct Callback
{
private:
template <std::size_t ... Is, typename Ret, typename ... Ts>
static Ret call_impl(Ret(* func)(Ts...), std::index_sequence<Is...>)
{
if (arr.size() != sizeof...(Is)) throw std::invalid_argument{};
return func(Convert(tag<Ts>{}, arr[Is])...);
}
public:
template <typename Ret, typename ... Ts>
Callback(Ret(* func)(Ts...)) : Call{[func](ASValue::Array arr, ASValue& ret)
{
try
{
ret = Callback::call_impl(func, std::make_index_sequence<sizeof(...(Ts)>());
return S_OK;
} catch (...) {
return E_INVALIDARG;
}
}}
{}
std::function<HRESULT(ASValue::Array, ASValue&)> Call;
};
std::index_sequence is C++14, but you might find implementation on SO.
You could implement something like that.
A map of objects (GenericCallback here) containing std::function<R(Args...)> objects type-erased with std::any or std::variant.
You need to be careful in the way you call your function callbacks though.
E.g. I have to feed it a std::string("hello world") and not a simple C-string, otherwise the std::any_cast will throw (since a function<string(const char*)> is not a function<string(string)>).
#include <algorithm>
#include <any>
#include <functional>
#include <iostream>
#include <string>
#include <map>
#include <memory>
struct Caller {
virtual ~Caller() = default;
virtual std::any call(const std::vector<std::any>& args) = 0;
};
template<typename R, typename... A>
struct Caller_: Caller {
template <size_t... Is>
auto make_tuple_impl(const std::vector<std::any>& anyArgs, std::index_sequence<Is...> ) {
return std::make_tuple(std::any_cast<std::decay_t<decltype(std::get<Is>(args))>>(anyArgs.at(Is))...);
}
template <size_t N>
auto make_tuple(const std::vector<std::any>& anyArgs) {
return make_tuple_impl(anyArgs, std::make_index_sequence<N>{} );
}
std::any call(const std::vector<std::any>& anyArgs) override {
args = make_tuple<sizeof...(A)>(anyArgs);
ret = std::apply(func, args);
return {ret};
};
Caller_(std::function<R(A...)>& func_)
: func(func_)
{}
std::function<R(A...)>& func;
std::tuple<A...> args;
R ret;
};
struct GenericCallback {
template <class R, class... A>
GenericCallback& operator=(std::function<R(A...)>&& func_) {
func = std::move(func_);
caller = std::make_unique<Caller_<R, A...>>(std::any_cast<std::function<R(A...)>&>(func));
return *this;
}
template <class Func>
GenericCallback& operator=(Func&& func_) {
return *this = std::function(std::forward<Func>(func_));
}
std::any callAny(const std::vector<std::any>& args) {
return caller->call(args);
}
template <class R, class... Args>
R call(Args&&... args) {
auto& f = std::any_cast<std::function<R(Args...)>&>(func);
return f(std::forward<Args>(args)...);
}
std::any func;
std::unique_ptr<Caller> caller;
};
using namespace std;
//Global functions
int sub(int a, int b) { return a - b; }
std::function mul = [](int a, int b) { return a*b;};
std::string sortString(std::string str) {
std::sort(str.begin(), str.end());
return str;
}
int main()
{
std::map<std::string, GenericCallback> callbacks;
// Adding our callbacks
callbacks["add"] = [](int a, int b) { return a + b; };
callbacks["sub"] = sub;
callbacks["mul"] = std::move(mul);
callbacks["sortStr"] = sortString;
// Calling them (hardcoded params)
std::cout << callbacks["add"].call<int>(2, 3) << std::endl;
std::cout << callbacks["sub"].call<int>(4, 2) << std::endl;
std::cout << callbacks["mul"].call<int>(5, 6) << std::endl;
std::cout << callbacks["sortStr"].call<std::string>(std::string("hello world")) << std::endl;
// Calling "add" (vector of any params)
std::vector<std::any> args = { {1}, {2} };
std::any result = callbacks["add"].callAny(args);
std::cout << "result=" << std::any_cast<int>(result) << std::endl;
return 0;
}
https://godbolt.org/z/h63job
I want to create class which should containe map with function pointers (subscribers). But that functions can be with different signature. My code looks like this but it not completed and I am not sure if that is right. Can somebody help me please how to correct append pointers to map and invoke them in myMainClass::start()?
myMainClass.h
#pragma once
#include "iostream";
#include "mySubscriber.h"
struct myMainClass {
myMainClass() {}
~myMainClass() {}
bool callback1(int iData) {
std::cout << "callback 1 with iData " << iData << std::endl;
}
bool callback2(std::string sData) {
std::cout << "callback 2 with sData " << sData << std::endl;
}
bool callback3(int iData, std::string sData) {
std::cout << "callback 1 with iData " << iData << ", sData " << sData << std::endl;
}
// SHOULD BE SOMETHING LIKE THIS
bool start() {
mySubscriber ss;
ss.subscribe("callback1", callback1);
ss.subscribe("callback2", callback2);
ss.getSubscribe("callback1")(5);
ss.getSubscribe("callback2")("test");
}
};
mySubscriber.h
#pragma once
#include "map";
#include "string";
#include "functional";
class mySubscriber {
typedef std::function<void()> func;
std::map<std::string, func*> _subscribes;
public:
mySubscriber() : _subscribes{} {}
~mySubscriber() {
_subscribes.clear();
}
/*
* append or change function pointer
*/
void subscribe(std::string fName, func* f) {
auto find = _subscribes.find(fName);
if (find != _subscribes.end())
{
find->second = f;
}
else
{
_subscribes.emplace(fName, f);
}
}
/*
* get subscribe function
*/
func* getSubscribe(std::string fName) {
auto find = _subscribes.find(fName);
if (find != _subscribes.end())
{
return find->second;
}
return NULL;
}
};
At first some general hints:
Avoid raw pointer usage as far as possible, even for internals! Use std::unique_ptr or std::shared_ptr instead!
Reducing a data conglomerate to a standard container, indexing it via a dynamic data type like std::string and use it in a plain void std::function context results (almost?) always in type erasure and a loss of according type safe outer access. In fact, this even has nothing to do with further differences between plain functions and member methods in the first place.
A possible first solution approach:
This is a minimal working example that should cover your quite dynamic requirements. For me it compiles and runs well with MS VS 2017 (C++17). I tried to use your original structs as far as possible.
#include <variant>
#include <set>
#include <string>
#include <iostream>
struct myMainClass {
myMainClass() {}
~myMainClass() {}
bool callback1(int iData) {
std::cout << "callback 1 with iData " << iData << std::endl;
return true;
}
bool callback2(std::string sData) {
std::cout << "callback 2 with sData " << sData << std::endl;
return true;
}
bool callback3(int iData, std::string sData) {
std::cout << "callback 1 with iData " << iData << ", sData " << sData << std::endl;
return true;
}
template <typename T> class CallbackBaseTmpl;
template <typename Ret, typename ...Args>
class CallbackBaseTmpl<Ret(Args...)>
{
public:
using Signature = Ret(Args...);
CallbackBaseTmpl(const std::function<Signature>& func) : m_function(func) {}
CallbackBaseTmpl(std::function<Signature>&& func) :
m_function(std::move(func)) {}
inline Ret Func(Args&&... args) { return m_function(std::forward<Args>(args)...); }
private:
std::function<Signature> m_function;
};
class Callback1Type : public CallbackBaseTmpl<bool(int)>
{
using CallbackBaseTmpl::CallbackBaseTmpl;
};
class Callback2Type : public CallbackBaseTmpl<bool(std::string)>
{
using CallbackBaseTmpl::CallbackBaseTmpl;
};
class Callback3Type : public CallbackBaseTmpl<bool(int, std::string)>
{
using CallbackBaseTmpl::CallbackBaseTmpl;
};
using CompoundCallbackType = std::variant<Callback1Type, Callback2Type, Callback3Type>;
class CallbackHolder
{
public:
CallbackHolder(const CompoundCallbackType& callbackImpl) : m_callbacksImpl(callbackImpl) {}
inline auto getIndex() const { return m_callbacksImpl.index(); }
inline CompoundCallbackType& getImpl() const { return m_callbacksImpl; }
private:
mutable CompoundCallbackType m_callbacksImpl;
};
class CallbacksContainer
{
public:
template <typename VariantType>
bool subscribe(const VariantType& compoundType)
{
return subscribe(CallbackHolder(compoundType));
}
bool subscribe(const CallbackHolder& cHolder)
{
auto res = m_containerImpl.insert(cHolder);
return res.second;
}
template <typename CallbackType, typename... Args>
auto getSubscribe(Args&&... args)
{
// linear search - can be optimized
for (auto& implEntry : m_containerImpl)
{
bool isWanted = std::visit([&args...](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, CallbackType>)
return true;
else
return false;
}, implEntry.getImpl());
if (isWanted)
return std::get<CallbackType>(implEntry.getImpl()).Func(std::forward<Args>(args)...);
}
throw std::logic_error("Cannot access element");
}
private:
struct CustomComparer {
bool operator() (const CallbackHolder& lhs, const CallbackHolder& rhs) const
{
// Each variant entry allowed only once in the set
return lhs.getIndex() < rhs.getIndex();
}
};
std::set<CallbackHolder, CustomComparer> m_containerImpl;
};
bool start() {
CallbacksContainer ms;
ms.subscribe(Callback1Type(std::bind(&myMainClass::callback1, this, std::placeholders::_1)));
ms.subscribe(Callback2Type(std::bind(&myMainClass::callback2, this, std::placeholders::_1)));
ms.getSubscribe<Callback1Type>(5);
ms.getSubscribe<Callback2Type>("TEST");
ms.subscribe(Callback3Type(std::bind(&myMainClass::callback3, this, std::placeholders::_1, std::placeholders::_2)));
ms.getSubscribe<Callback3Type>(2, "");
return true;
}
};
Explanation: I replaced your original map with an std::set as a kind of registry container so there are still no duplicates allowed. Some efforts are required via Wrappers to achieve the desired final access scheme.
You can easily change the desired registered functions for a type in a dynamic but always very type safe way now. Feel free to extend this scheme for your own purposes. Likely, there are several parts that can be optimized, shortened or extended. Maybe there's also a nice way to avoid this mutable inside the CallbackHolder. The (non-grave for a few functions) linear search within the set can be avoided via an actual typeid sorting and specialized according finding for instance.
Update due to feedback:
If strings as keys are required and a maximum degree of freedom should be given, i.e. any callback type should be providable without the necessity for compile time registration, this solution might be an alternative:
#include <map>
#include <string>
#include <iostream>
#include <functional>
#include <memory>
struct myMainClass {
myMainClass() {}
~myMainClass() {}
bool callback1(int iData) {
std::cout << "callback 1 with iData " << iData << std::endl;
return true;
}
bool callback2(std::string sData) {
std::cout << "callback 2 with sData " << sData << std::endl;
return true;
}
bool callback3(int iData, std::string sData) {
std::cout << "callback 1 with iData " << iData << ", sData " << sData << std::endl;
return true;
}
class ICallback
{
public:
virtual ~ICallback() = default;
};
template <typename T> class TypedCallback;
template <typename Ret, typename ...Args>
class TypedCallback<Ret(Args...)> : public ICallback
{
public:
using Signature = Ret(Args...);
TypedCallback(const std::function<Signature>& func) : m_function(func) {}
TypedCallback(std::function<Signature>&& func) :
m_function(std::move(func)) {}
inline Ret Func(Args&&... args) { return m_function(std::forward<Args>(args)...); }
private:
std::function<Signature> m_function;
};
class CallbacksContainer
{
private:
template <typename T> struct CallTraits {};
template <typename C, typename Ret, typename... Args>
struct CallTraits<Ret(C::*)(Args...)>
{
using Signature = Ret(Args...);
using ReturnType = Ret;
};
template <typename C, typename Ret, typename... Args>
struct CallTraits<Ret(C::*)(Args...) const>
{
using Signature = Ret(Args...);
using ReturnType = Ret;
};
template <typename F>
struct FuncTraits
{
using FuncClass = std::decay_t<F>;
using OperatorSignature = decltype(&FuncClass::operator());
using signature = typename CallTraits<OperatorSignature>::Signature;
using returnType = typename CallTraits<OperatorSignature>::ReturnType;
};
template <typename Ret, typename... Args>
struct FuncTraits<Ret(Args...)>
{
using Signature = Ret(Args...);
using ReturnType = Ret;
};
template <typename Ret, typename... Args>
struct FuncTraits<Ret(*)(Args...)>
{
using Signature = Ret(Args...);
using ReturnType = Ret;
};
template <typename Ret, typename... Args>
struct FuncTraits<Ret(&)(Args...)>
{
using Signature = Ret(Args...);
using ReturnType = Ret;
};
public:
template <typename T>
bool subscribe(const std::string& key, T&& func)
{
auto res = m_subscriptions.try_emplace(
key, std::make_unique<TypedCallback<typename FuncTraits<T>::signature>>(std::forward<T>(func)));
return res.second;
}
template <typename Ret, typename... Args>
auto getSubscribe(const std::string& key, Args&&... args) const
{
using Signature = Ret(Args...);
const auto& entry = m_subscriptions.at(key);
auto rp = entry.get();
auto typedCB = dynamic_cast<TypedCallback<Signature>*>(rp);
if (typedCB == nullptr)
{
// TODO: Possible further check if functor can be used due to convertible types, for instance
// with an acyclic visitor?
std::logic_error("Wrong callback signature provided.");
}
return typedCB->Func(std::forward<Args>(args)...);
}
private:
std::map<std::string, std::unique_ptr<ICallback>> m_subscriptions;
};
bool start() {
CallbacksContainer ms;
// Usage with non static member methods
ms.subscribe("callback1", [this](int x) { return callback1(x); });
ms.subscribe("callback2", [this](std::string x) { return callback2(x); });
ms.subscribe("callback3", [this](int x, std::string str) { return callback3(x, str); });
// Usage with lambda
ms.subscribe("callback4", [](int y) { return y != 0; });
// Usage with std::function itself
ms.subscribe("callback5", std::function<bool(int)>([](int y) { return y != 0; }));
// Getters - Unfortunately, exact types are required. Maybe acyclic visitor could help here?
ms.getSubscribe<bool>("callback1", 1);
ms.getSubscribe<bool>("callback2", std::string("TEST"));
ms.getSubscribe<bool>("callback3", 1, std::string("TEST"));
ms.getSubscribe<bool>("callback4", 1);
return true;
}
};
PROs:
No static/compile time method signature registration required -> no variants
At least with C++20, method subscription will be an easy going here, added some helper traits to make things a bit easier here already
Only one underlying map used
CONs:
Less type-safety at some points and the dynamic_cast might be a bit slow but might be improved in terms of performance via a simple type index comparison
The getSubscribe() method has to be used with care. Exact types are required here (the former dynamically registered ones) and it doesn't unfortunately support common signature conversion ways. I see currently no way to get rid of this problem with pre C++20 features. Maybe some tricks with a generic acyclic visitor pattern or SFINAE magic + visitor might help here but that breaks the mould by far here I think. If that arises as a real issue, one can still use the chained parameter scheme in doubt, that guarantees type safety on its own.
You have to somehow turn the memberfunction pointers to regular old function pointers, in order to store them in the same container. You have three options that I can come up with:
#include <functional>
struct Foo {
void foo(int x, int y, int z) {}
/*
Putting the instance as the first parameter is crucial, because the
first argument to a member function call is an implicit this. If instance
is not the first parameter the compiler has to shift around the argument
list, otherwise it's a direct forwarding call.
*/
static void callback(void* instance, int x, int y, int z) {
return static_cast<Foo*>(instance)->foo(x, y, z);
}
};
int main() {
Foo foo;
void (*f0)(void*, int, int, int){&Foo::callback};
/*
Capturing lambda cannot decay to function pointer, have to use
std::function or smth. similar
*/
std::function<void(int, int, int)> f1{
[&](int x, int y, int z) { return foo.foo(x, y, z); }};
auto f2 = std::mem_fn(&Foo::foo);
f0(&foo, 1, 2, 3);
f1(1, 2, 3);
f2(&foo, 1, 2, 3);
}
Here's a godbolt with the generated assembly https://godbolt.org/z/K9eM4E
I want to save and pass list of template arguments to function.
Like std::thread passes arguments to a thread. Types of arguments are templated and arguments count is not static.
Example, how it will work:
class CallbackList {
public:
Callback(/* Type of list of template args */ args) {
this->saved_args = args;
}
void Call() {
this->callback(saved_args);
}
private:
/* Type of list of template args */ saved_args;
CallbackType callback;
}
Or how can I implement that:
template<typename ...Args>
class CallbackList {
public:
using CallbackPrototype = /* some prototype */;
void RegisterCallback(CallbackPrototype callback, Args... args) {
CallbackInfo callback_info;
callback_info.callback = callback;
callback_info.args = { args... };
this->callbacks.push_back(callback_info);
}
void Call() {
for (CallbackInfo& callback_info : this->callbacks)
callback_info.callback(callback_info.args);
}
private:
struct CallbackInfo {
CallbackPrototype callback;
/* what type should be here? tuple? args count are not static */ args;
};
std::vector<CallbackInfo> callbacks;
}
It is possible?
How can I implement it?
If you do not want your callback to depend on the types of the arguments you have to use some kind of type erasure. You can, for example, use std::function from <functional>:
#include <functional>
#include <iostream>
class Lazy_Callback
{
public:
template <typename F, typename ...Args>
Lazy_Callback(F && f, Args && ...args)
: _fun([=]() { return f(args...); })
{ }
void call() const
{
_fun();
}
protected:
private:
std::function<void()> _fun;
};
void print_int(int x)
{
std::cout << "x = " << x << "\n";
}
int main()
{
Lazy_Callback lc(print_int, 5);
lc.call();
}
If the callback can be templated then you can use std::tuple to store your arguments:
#include <tuple>
#include <iostream>
template <typename F, typename ...Args>
class Lazy_Callback
{
public:
template <typename ...Ts>
Lazy_Callback(F f, Ts && ...ts)
: _f(f), _args(ts...)
{ }
void call() const
{
return std::apply(_f, _args);
}
protected:
private:
F _f;
std::tuple<Args...> _args;
};
template <typename F, typename ...Ts>
Lazy_Callback<F, std::decay_t<Ts>...> make_callback(F && f, Ts && ...ts)
{
return { std::forward<F>(f), std::forward<Ts>(ts)... };
}
void print_int(int x)
{
std::cout << "x = " << x << "\n";
}
int main()
{
auto lc = make_callback(print_int, 5);
lc.call();
}
Are you looking for something like std::bind? Here is a simple example that you could probably expand on:
#include <iostream>
#include <functional>
template <typename T1, typename T2>
void printSum(const T1& a, const T2& b)
{
std::cout << a + b << std::endl;
}
int main()
{
const auto callback = std::bind(&printSum<int, int>, 1, 2);
// ...
callback();
}
I have the following code which allows me to instantiate and then call a list of void() functions.
(I am using https://github.com/philsquared/Catch for unit testing if you wish to compile and run this code).
#include "catch.hpp"
#include <functional>
#include <vector>
class ChainOfResponsibility : public std::vector<std::function<void()> >, public std::function<void()>
{
public:
void operator()() const
{
for(std::vector<std::function<void()> >::const_iterator it = begin(); it != end(); ++it) {
(*it)();
}
}
};
TEST_CASE("ChainOfResponsibility calls its members when invoked")
{
bool test_function_called = false;
std::function<void()> test_function = [&]()
{
test_function_called = true;
};
ChainOfResponsibility object_under_test;
object_under_test.push_back(test_function);
object_under_test();
REQUIRE(test_function_called);
}
My question is how do I template the ChainOfResponsibility class to accept functions with a different (but consistent) signature?
For example, consider a ChainOfResponsibility<void(int)> or a ChainOfResponsibility<ReturnClass(Argument1Class, Argument2Class)>.
For the sake of argument, lets say that the second example returns the value returned by the last member in the chain, or the default value for ReturnClass if the chain is empty.
Also, if the STL already contains a template class that achieves this, then I would prefer to use it over my home-grown class.
Your specific "discard all the intermediate results" is also fairly simple, but I think it's a bad idea.
template<typename Ret, typename ... Args>
class ChainOfResponsibility
{
std::vector<std::function<Ret(Args...)> > chain;
public:
Ret operator()(Args ... args) const
{
Ret value;
for(auto & func : chain) {
value = func(args...);
}
return value;
}
};
void has to be treated on it's own
template<typename ... Args>
class ChainOfResponsibility<void, Args...>
{
std::vector<std::function<void(Args...)> > chain;
public:
void operator()(Args ... args) const
{
for(auto & func : chain) {
func(args...);
}
}
};
Note that deriving from std:: types is a bad idea, especially std::function, which is a type-erasing callable, not "the base of all callables". You can simply provide an operator()
options for improving the non-void case:
// fold the results
template <typename BinaryFunction>
Ret operator()(Args ... args, BinaryFunction add, Ret init) const
{
for(auto & func : chain) {
init = add(init, func(args...));
}
return init;
}
// return a vector
template <typename BinaryFunction>
std::vector<Ret> operator()(Args ... args) const
{
std::vector<Ret> results(chain.size());
for(auto & func : chain) {
results.push_back(func(args...));
}
return results;
}
You don't need to use the std::function as a base class, using std::vector is sufficent. The template ChainOfResponsibility can use the same template paramter list as the std::function like follows:
#include <iostream>
#include <string>
#include <functional>
#include <vector>
template<typename>
class ChainOfResponsibility;
template<typename R, typename... Args>
class ChainOfResponsibility<R(Args...)> :
public std::vector<std::function<R(Args...)>> {
public:
R operator()(const Args&... args) {
R result {};
for(auto it = this->begin(); it != this->end(); ++it)
result = (*it)(args...);
return result;
}
};
int main() {
ChainOfResponsibility<std::string(int, int)> tester;
tester.push_back([](int a, int b)->std::string {
return std::to_string(a + b);
});
std::cout << tester(4, 2) << std::endl;
}
Anyway, using std::vector only is good enoug for the problem you described. If the content of the overloaded operator() is nothing special, you can change my example above as follows:
int main() {
std::vector<std::function<std::string(int, int)>> tester;
tester.push_back([](int a, int b)->std::string {
return std::to_string(a + b);
});
std::string result;
for(auto& test_fn : tester)
result = test_fn(4, 2);
std::cout << result << std::endl;
}
You also can write a function template instead of overloading the operator():
template<typename R, typename... Args>
R perform(const std::vector<std::function<R(Args...)>>& functions,
const Args&... args) {
R result {};
for(auto& test_fn : functions)
result = test_fn(4, 2);
return result;
}
Can I store in a container a list of member functions and then call them later, if they have different number of args.
I feel I'm just missing something small but this is how far I've got.
template<typename T>
class RPCServer
{
public:
RPCServer(const std::string host, const int port) {}
// Store the method pointers
template<typename F>
void register_method(const T discriminant, F func) {
m_callbacks.emplace_back(discriminant,func);
}
template<typename... Args>
void run(T subject, Args... args) {
auto func = std::find(std::begin(m_callbacks), std::end(m_callbacks), subject);
if (func != std::end(m_callbacks)) {
auto res = std::get<1>(*func)(args...); // This doesn't compile
}
}
~RPCServer() = default;
private:
// Store
std::vector<std::tuple<T, boost::any>> m_callbacks;
};
class Impl
{
public:
// RPC methods
void send_data(std::string data) {}
int get_details(int input) { return 0; }
};
Set up here
using namespace std::placeholders;
Impl impl;
RPCServer<std::string> server("localhost",1234);
server.register_method("foo", std::bind(&Impl::send_data, impl, _1));
server.register_method("bar", std::bind(&Impl::get_details, impl, _1));
server.run("foo", "blah"s); // This should call send_data with 'blah' as a arg
auto result = server.run("bar", 1); // Call get_details passing in 1
How do I store/retrieve a set of member functions type safely.
What about creating an adaptor template?
A proof-of-concept code:
#include <iostream>
#include <functional>
template<typename T0, typename... TS> struct FunCaller {
template<class F> FunCaller(F &&f): f(f) {}
template<typename... More> T0 operator()(TS &&... as, More &&...) {
return f(as...);
}
private:
std::function<T0(TS...)> f;
};
template<typename T0, typename... TS> inline FunCaller<T0, TS...> funCaller(T0(&&f)(TS...)) { return FunCaller<T0, TS...>(f); }
std::ostream &printSome(std::string const &s1, std::string const &s2) { return std::cout << s1 << ", " << s2 << std::endl; }
int main() {
auto omg = funCaller(printSome);
omg("Hello", "world!", "This", "is", "cocaine", "speaking");
}