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
Related
I have a toy example that I'd like to modify architecturally to remove type dependency of Processor on EmitterT:
#include <iostream>
#include <utility>
using namespace std;
struct Emitter {
void e(int) { cout << "emitting int\n";}
void e(double) { cout << "emitting double\n";}
void e(char*) { cout << "emitting char*\n";}
void e(const char*) { cout << "emitting const char*\n";}
};
template <typename EmitterT>
struct Processor {
Processor(EmitterT e) : e_{e} {}
template <typename T>
void process(T&& value) {
cout << "some processing... ";
e_(std::forward<T>(value));
}
EmitterT e_;
};
template<typename Emitter_>
Processor<Emitter_> makeProcessor(Emitter_ e) { return Processor<Emitter_>(e);}
int main() {
Emitter em;
auto p = makeProcessor([&em](auto v){em.e(v);});
p.process(1);
p.process("lol");
return 0;
}
Motivation
I'd like to decouple part responsible for utilizing results of processing from the processing itself. The Emitter class structure is given to me, so I have to support overloaded functions.
I'd like to pass a lambda function to a processor that will use it. Kind of like a callback mechanism, however it must be a generic lambda, to support overloads.
What I've tried:
The example I wrote works, but it depends on Emitter type as a template parameter. I don't like Processor type to change based on Emitter. It's also contagious, I have a real Processor hierarchy and Emitter spread like const or worse.
After reading https://stackoverflow.com/a/17233649/1133179 I've tried playing with below struct as a member:
struct EmitterC {
template<typename T>
void operator()(T value) { }
};
But I cannot figure out a way to defer implementation of Emitter after Processor when using it as a normal parameter. It worked out with a forward declaration and a reference EmitterC& but it supports one only Emitter definition. The only way I could come up with was to drop lambda, and make virtual overloads in EmitterC for every type I expect in Emitter and use it as a base class.
So, Is there a way to pass the (generic) lambda as a parameter, so that Processor type doesn't depend on Emitter?
I am restricted to c++14, but I am interested in more modern standards as well if the have better support.
This simplest solution is to make Emitter a parameter to process:
struct Processor {
template <typename T, typename EmitterFn>
void process(T&& value, EmitterFn emit) {
cout << "some processing... ";
emit(std::forward<T>(value));
}
};
However, if it must be a member of Processor and you can enumerate the possible function signatures, you can use some kind of type erasure. std::function or the proposed std::function_ref won't work because they only allow a single function signature, but we can write our own overloaded_function_ref:
template <typename Derived, typename Sig>
class function_ref_impl;
template <typename Derived, typename R, typename... Args>
class function_ref_impl<Derived, R(Args...)> {
using fn_t = R(*)(void const*, Args...);
public:
auto operator()(Args... args) const -> R {
return fn(static_cast<Derived const&>(*this).object, std::forward<Args>(args)...);
}
protected:
template <typename F,
std::enable_if_t<!std::is_base_of<function_ref_impl, F>::value, int> = 0>
explicit function_ref_impl(F const& f)
: fn{[](void const* self, Args... args) -> R {
return (*static_cast<F const*>(self))(std::forward<Args>(args)...);
}}
{}
private:
fn_t fn;
};
template <typename... Sig>
class overloaded_function_ref
: public function_ref_impl<overloaded_function_ref<Sig...>, Sig>...
{
public:
template <typename F,
std::enable_if_t<!std::is_base_of<overloaded_function_ref, F>::value, int> = 0>
overloaded_function_ref(F const& f)
: function_ref_impl<overloaded_function_ref, Sig>(f)...
, object{std::addressof(f)}
{}
// Can be done pre-C++17, but it's not easy:
using function_ref_impl<overloaded_function_ref, Sig>::operator()...;
// This can be encapsulated with techniques such as the "passkey" idiom.
// Variadic friend expansion isn't a thing (`friend bases...`).
void const* object;
};
Live example
This does require C++17 for the using
/* base */::operator()..., but that can be emulated in C++14; see the paper that introduced this feature: [P0195], or perhaps Boost HOF's match can be massaged to do this. This is also just a function reference and not an owning function.
Then we can write:
struct Processor {
template <typename T>
void process(T&& value) {
cout << "some processing... ";
emit(std::forward<T>(value));
}
using emitter_t = overloaded_function_ref<
void(int),
void(double),
void(char*),
void(char const*)
>;
emitter_t emit;
};
Demo
IMHO: Inheritance is here for that.
#include <iostream>
#include <utility>
using namespace std;
struct BaseEmitter {
virtual void e(int) =0;
virtual void e(double)=0;
virtual void e(char*)=0;
virtual void e(const char*)=0;
};
struct Emitter :public BaseEmitter {
virtual void e(int) { cout << "emitting int\n";}
virtual void e(double) { cout << "emitting double\n";}
virtual void e(char*) { cout << "emitting char*\n";}
virtual void e(const char*) { cout << "emitting const char*\n";}
};
struct Processor {
BaseEmitter& e_;
Processor(BaseEmitter& e) : e_(e) {}
template <typename T>
void process(T&& value) {
cout << "some processing... ";
e_(std::forward<T>(value));
}
};
int main() {
Emitter em;
auto p = Processor(em);
p.process(1);
p.process("lol");
return 0;
}
You can do a mix in order to capture the lambda, just by inheritance in the interface:
struct bypass
{
virtual void operator()() = 0;
};
template<typename callable> struct capture: public bypass
{
callable& _ref;
capture(callable &ref)
: _ref(ref)
{;};
virtual void operator()()
{
_ref();
}
};
struct test
{
bypass *_c;
template<class T> test(T& callback)
: _c(nullptr)
{
_c = new capture<decltype(callback)>(callback);
};
virtual ~test()
{
delete _c;
};
void doit()
{
(*_c)();
}
};
int main(int argc, char* argv[])
{
auto lambda = [](){std::cout << "hello\n";};
test z=test(lambda);
z.doit();
return 0;
}
If you are willing to pay a high runtime cost in exchange for minimal constraints, you can use std::function with std::any (for C++14, use boost::any):
#include <iostream>
#include <utility>
#include <any>
#include <functional>
struct Processor {
Processor(std::function<void(std::any)> e) : e_{e} {}
template <typename T>
void process(T&& value) {
std::cout << "some processing... ";
e_(std::forward<T>(value));
}
std::function<void(std::any)> e_;
};
struct Emitter {
void e(int) { std::cout << "emitting int\n";}
void e(double) { std::cout << "emitting double\n";}
void e(char*) { std::cout << "emitting char*\n";}
void e(const char*) { std::cout << "emitting const char*\n";}
};
int main() {
Emitter em;
auto p = Processor(
[&em](std::any any){
// This if-else chain isn't that cheap, but it's about the best
// we can do. Alternatives include:
// - Hashmap from `std::type_index` (possibly using a perfect hash)
// to a function pointer that implements this.
// - Custom `any` implementation which allows "visitation":
//
// any.visit<int, double, char*, char const*>([&em] (auto it) {
// em.e(it);
// });
if (auto* i = std::any_cast<int>(&any)) {
em.e(*i);
} else if (auto* d = std::any_cast<double>(&any)) {
em.e(*d);
} else if (auto* cstr = std::any_cast<char*>(&any)) {
em.e(*cstr);
} else {
em.e(std::any_cast<char const*>(any));
}
}
);
p.process(1);
p.process("lol");
return 0;
}
std::any and std::function are both owning type erased wrappers. You may have heap allocations for this, or you might fit inside their small object optimization. You will have virtual function calls (or equivalent).
Compiler Explorer link
Is it possible to pass generic lambda as non-template argument
It is not possible to declare a non-template function that accepts a lambda as an argument. The type of a lambda is anonymous: It has no name. It is not possible to write a function declaration that accepts an argument of an anonymous type.
The type of the lambda can be deduced, which is why lambdas can be passed into function templates whose argument types are deduced.
While this answers the question, it does not offer a solution. I don't think a solution is going to be simple.
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();
}
I am trying to implement a composite pattern for std::functions with use of template classes, where each composite class processes the return values of its children.
So the pattern classes might look something like this:
class AbstractClass {
public:
virtual void process() = 0;
};
template<typename ReturnType>
class PrimitiveClass : public AbstractClass {
public:
ReturnType process() {
// please note, that the result is not returned by the return statement
return this->func(); //this is just for simplicity
}
private:
std::function<ReturnType()> func;
}
template<typename ReturnType, typename ...Args>
class CompositeClass : public AbstractClass {
public:
ReturnType process() {
// --> This is where I want to process all children first and then pass their return values to this->func
// the following code is kind of a pseudo code:
for(auto it = vector.begin(); it != vector.end(); ++it {
results.add((**it).process())
}
return this->func(results)
}
private:
std::function<ReturnType(Args...)> func;
std::vector<std::shared_ptr<AbstractClass>> children;
};
So for example, I have a CompositeClass with a std::function<int(int, double, bool) and the argument types of that function are also the ReturnTypes of its children. And I want to pass the return values of the children to above-mentioned std::function
Can anyone think of a way, how I can achieve this?
If I understand what you want (and if I'm not wrong)...
(1) to solve the problem of the no-covariant returned value from process() (see comment from Igor Tandetnik) you need a template abstract class to express the correct return value; by example
template <typename T>
struct abstClass
{ virtual T process() const = 0; };
(2) so your CompositeClass (renamed nodeClass, in my following example) inherit from abstClass<ReturnType>
(3) your PrimitiveClass is useless because you can manage the case (reference to a function without arguments) as a CompositeClass with zero Args
(4) you need a leafClass to handle basic values
(5) in CompositeClass (nodeClass), children, instead of a std::vector of shared_ptr<AbstractClass> (that can't do what do you want), can be a
std::tuple<std::shared_ptr<abstClass<Args>>...> children;
Given these points, I propose the following solution (that, unfortunately, is C++14 because use std::index_sequence and std::make_index_sequence that are available starting from C++14; but if you need a C++11 solution, isn't difficult write substitutes for they)
#include <tuple>
#include <memory>
#include <iostream>
#include <functional>
template <typename T>
struct abstClass
{ virtual T process() const = 0; };
template <typename T>
class leafClass : public abstClass<T>
{
private:
T value;
public:
leafClass (T && v0) : value { std::forward<T>(v0) }
{ }
T process () const
{ return value; };
};
template <typename RetT, typename ... ArgTs>
class nodeClass : public abstClass<RetT>
{
private:
using funcT = std::function<RetT(ArgTs...)>;
template <typename T>
using shrPAC = std::shared_ptr<abstClass<T>>;
funcT func;
std::tuple<shrPAC<ArgTs>...> childrens;
template <std::size_t ... Is>
RetT processH (std::index_sequence<Is...> const &) const
{ return func(std::get<Is>(childrens)->process()...); }
public:
nodeClass (funcT && f0, shrPAC<ArgTs> && ... as)
: func { std::forward<funcT>(f0) },
childrens { std::forward<shrPAC<ArgTs>>(as)... }
{ }
RetT process () const
{ return processH(std::make_index_sequence<sizeof...(ArgTs)>{}); }
};
int main ()
{
auto func0 = [](int i, double d, bool b) { return int( b ? i+d : i-d ); };
auto shpLci = std::make_shared<leafClass<int>>(1);
auto shpLcd = std::make_shared<leafClass<double>>(2.2);
auto shpNb = std::make_shared<nodeClass<bool>>([](){ return true; });
auto shpNc0 = std::make_shared<nodeClass<int, int, double, bool>>
(func0, shpLci, shpLcd, shpNb);
auto shpNc1 = std::make_shared<nodeClass<int, int, double, bool>>
(func0, shpNc0, shpLcd, shpNb);
auto shpNc2 = std::make_shared<nodeClass<int, int, double, bool>>
(func0, shpNc1, shpLcd, shpNb);
std::cout << shpNc0->process() << std::endl; // print 3
std::cout << shpNc1->process() << std::endl; // print 5
std::cout << shpNc2->process() << std::endl; // print 7
}
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;
}
I don't know if this can even be achivieable, but given these set of functions\class:
float plus1(float x) { return x+1; }
float div2(float x) { return x/2.0f; }
template <typename T>
class chain {
public:
chain(const T& val = T()) : val_(val) {}
chain& operator<<( std::function<float (float)> func ) {
val_ = func(val_);
return *this;
}
operator T() const {
return val_;
}
T val_;
};
I can chain functions operating on floats like this:
float x = chain<float>(3.0f) << div2 << plus1 << div2 << plus1;
However, I'd like to generalize\extend this to being able to convert between types and have functions with arguments. Unfortunately I'm not smart enough to figure out how, or if, this can be done.
Too be more specific I'd like to be able to do something like this (Where operator<< is just an arbitary choice, and preferably I dont even have to write "chain" at the beginning);
Also, these are just dummy examples, I do not intend to use it for arithmetics.
std::string str = chain<float>(3.0) << mul(2.0f) << sqrt << to_string << to_upper;
or
vec3d v = chain<vec3i>(vec3i(1,1,1)) << normalize << to_vec3<double>;
Any ideas?
I think i see why you want to do it. It's similar to the iostream manipulators.
You will always need to start with chain(...) (i.e you will never be able to magically do something like int x = 1 << plus(2) << times(2)), but you can overload the operator int, operator float, ... to allow for the implicit conversions.
You will also need to go back and define each type (like mul) and then implement the operator<< which takes a mul or a const mul, but as a whole it's doable (but a PITA)
A general and extendable solution using boost::proto :
#include <iostream>
#include <boost/proto/proto.hpp>
namespace bp = boost::proto;
// -----------------------------------------------------------------------------
// perform is a callable transform that take a function_ terminal and execute it
// -----------------------------------------------------------------------------
struct perform : bp::callable
{
template<class Sig> struct result;
template<class This, class Func, class In>
struct result<This(Func,In)>
: boost::result_of<typename boost::remove_reference<Func>::type(In)> {};
template<class Func, class In>
typename result<perform(Func &,In)>::type
operator()( Func& f, In& in ) const
{
return f(in);
}
};
// -----------------------------------------------------------------------------
// Grammar for chaining pipe of functions
// -----------------------------------------------------------------------------
struct pipeline_grammar
: bp::or_<
bp::when<
bp::bitwise_or<pipeline_grammar,pipeline_grammar>
, pipeline_grammar(
bp::_right
, pipeline_grammar(bp::_left,bp::_state)
)
>
, bp::when<
bp::terminal<bp::_>
, perform(bp::_value, bp::_state)
>
> {};
// -----------------------------------------------------------------------------
// Forward declaration of the pipeline domain
// -----------------------------------------------------------------------------
struct pipeline_domain;
// -----------------------------------------------------------------------------
// A pipeline is the top level DS entity
// -----------------------------------------------------------------------------
template<class Expr>
struct pipeline : bp::extends<Expr,pipeline<Expr>, pipeline_domain>
{
typedef bp::extends<Expr, pipeline<Expr>, pipeline_domain> base_type;
pipeline(Expr const &expr = Expr()) : base_type(expr) {}
// ---------------------------------------------------------------------------
// A pipeline is an unary callable object
// ---------------------------------------------------------------------------
template<class Input>
typename boost::result_of<pipeline_grammar(pipeline,Input)>::type
operator()(Input const& in) const
{
pipeline_grammar evaluator;
return evaluator(*this,in);
}
};
// -----------------------------------------------------------------------------
// the pipeline_domain make pipeline expression macthes pipeline_grammar
// -----------------------------------------------------------------------------
struct pipeline_domain
: bp::domain<bp::generator<pipeline>,pipeline_grammar>
{};
// -----------------------------------------------------------------------------
// Takes a PFO instance and make it a pipeline terminal
// -----------------------------------------------------------------------------
template<class Func>
typename bp::result_of::
make_expr<bp::tag::terminal, pipeline_domain,Func>::type
task( Func const& f )
{
return bp::make_expr<bp::tag::terminal,pipeline_domain>( f );
}
//--------------------------- Examples --------------------
struct return_value
{
template<class Sig> struct result;
template<class This, class T>
struct result<This(T)> : bp::detail::uncvref<T>
{};
return_value(int i = 1) : factor(i) {}
template<class T>
T operator()(T const& in) const
{
return in*factor;
}
int factor;
};
struct say_hi
{
typedef void result_type;
template<class T>
void operator()(T const& in) const
{
std::cout << "Hi from value = " << in << "\n";
}
};
int main()
{
return_value r1,r2(5);
(task(r1) | task(r2) | task(say_hi())) (7); // SHould print 35
float k = 10,r;
r = (task(r2) | task(r2) | task(r2) | task(r2))(k);
std::cout << r << "\n"; // Should print 6250
}
The basic idea is to wrap function objects as proto terminals, build a small | based grammar and let the proto system deals with the composition.
In order to get conversions between types you would want to have everything return a proxy object, that could convert to any type. Something based on boost::variant, perhaps.
You could also rewrite your operator << as a template function to make it a bit more generic:
template <class UnaryFunction>
chain& operator<<(UnaryFunction func) { _val = func(_val); return *this;}
That would allow you to use any kind of function object as an argument.
To use functions with multiple arguments, you can use the bind function. This was in boost prior to C++11, however now it is in the standard and should be available on any C++11 compatible compiler.
Here is my solution for C++17.
#include <type_traits>
#include <utility>
template <class F>
struct waterfall
{
waterfall(F&& f)
: fn(std::forward<F>(f))
{}
template <class... Args>
decltype(auto) operator()(Args&&... args) const {
return fn(std::forward<Args>(args)...);
}
template <class T>
auto then(T&& t) const & {
return then_impl(fn, std::forward<T>(t));
}
template <class T>
auto then(T&& t) const && {
return then_impl(std::move(fn), std::forward<T>(t));
}
private:
F fn;
template <class In, class Out>
static auto then_impl(In&& in, Out&& out)
{
auto fn = [in = std::forward<In>(in), out = std::forward<Out>(out)](auto&&... args)
{
using InRet = std::invoke_result_t<In, decltype(args)...>;
if constexpr (std::is_invocable_v<Out, InRet>) {
return out(in(std::forward<decltype(args)>(args)...));
}
else {
in(std::forward<decltype(args)>(args)...);
return out();
}
};
return waterfall<decltype(fn)>(std::move(fn));
}
};
And use it like this
int main()
{
// Create a chain
waterfall chain([](const char* s) {
return 42;
})
.then([](auto x) {
// x = 42 here
return x + 1;
})
.then([] {
// Ignoring value from previous function.
// Send double to next one.
return 3.14;
})
.then([](double value) {
// etc...
return true;
});
// chain signature is now bool(const char*)
// Now call our functions in chain
bool ret = chain("test");
}