Composite pattern of std::functions - c++

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
}

Related

How to store function pointers in map and invoke them?

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

Polymorphic visitor with lambdas

I want to implement a polymorphic visitor using lambdas without implementing a class. I already have a foundation but am struggling with the type deduction for the parameters of my lambdas.
Let's say I have some legacy code base that decided to use type tags for a polymorphic type like so:
enum class ClassType
{
BaseType = 0, TypeA, TypeB
};
class BaseType
{
public:
virtual ~BaseType() {}
ClassType getType() const
{ return type; }
protected:
ClassType type;
};
class TypeA : public BaseType
{
public:
static const ClassType Type = ClassType::TypeA;
explicit TypeA(int val) : val(val)
{ type = ClassType::TypeA; }
virtual ~TypeA() {}
int val;
};
class TypeB : public BaseType
{
public:
static const ClassType Type = ClassType::TypeB;
explicit TypeB(std::string s) : s(s)
{ type = ClassType::TypeB; }
virtual ~TypeB() {}
std::string s;
};
What I want to achieve is a visitor similar to the std::variant visitors that would then look like this:
std::vector<BaseType*> elements;
elements.emplace_back(new TypeA(1));
elements.emplace_back(new TypeB("hello"));
for (auto elem : elements)
{
visit(elem,
[](TypeA* typeA) {
std::cout << "Found TypeA element, val=" << typeA->val << std::endl;
},
[](TypeB* typeB) {
std::cout << "Found TypeB element, s=" << typeB->s << std::endl;
}
);
}
My so far failing approach for implementing such a visit<>() function was the following code:
template <typename T>
struct identity
{
typedef T type;
};
template <typename T>
void apply_(BaseType* b, typename identity<std::function<void(T*)>&>::type visitor)
{
if (b->getType() != T::Type)
return;
T* t = dynamic_cast<T*>(b);
if (t) visitor(t);
}
template <typename... Ts>
void visit(BaseType* b, Ts... visitors) {
std::initializer_list<int>{ (apply_(b, visitors), 0)... };
}
The compiler complains that it cannot deduce the template parameter T for my apply_ function.
How can I declare the correct template and function signature of apply_ to correctly capture lambdas and maybe even other callables? Or is something like this even possible at all?
Here's an (incomplete) solution that works with any function object that has an unary, non-overloaded, non-templated operator(). Firstly, let's create an helper type alias to retrieve the type of the first argument:
template <typename>
struct deduce_arg_type;
template <typename Return, typename X, typename T>
struct deduce_arg_type<Return(X::*)(T) const>
{
using type = T;
};
template <typename F>
using arg_type = typename deduce_arg_type<decltype(&F::operator())>::type;
Then, we can use a fold expression in a variadic template to call any function object for which dynamic_cast succeeds:
template <typename Base, typename... Fs>
void visit(Base* ptr, Fs&&... fs)
{
const auto attempt = [&](auto&& f)
{
using f_type = std::decay_t<decltype(f)>;
using p_type = arg_type<f_type>;
if(auto cp = dynamic_cast<p_type>(ptr); cp != nullptr)
{
std::forward<decltype(f)>(f)(cp);
}
};
(attempt(std::forward<Fs>(fs)), ...);
}
Usage example:
int main()
{
std::vector<std::unique_ptr<Base>> v;
v.emplace_back(std::make_unique<A>());
v.emplace_back(std::make_unique<B>());
v.emplace_back(std::make_unique<C>());
for(const auto& p : v)
{
visit(p.get(), [](const A*){ std::cout << "A"; },
[](const B*){ std::cout << "B"; },
[](const C*){ std::cout << "C"; });
}
}
ABC
live example on wandbox
Assuming that you cannot change the virtual classes, you may do the following:
template <typename F>
decltype(auto) visitBaseType(BaseType& base, F&& f)
{
switch (base.getType())
{
case ClassType::BaseType: return f(base);
case ClassType::TypeA: return f(dynamic_cast<TypeA&>(base));
case ClassType::TypeB: return f(dynamic_cast<TypeB&>(base));
}
throw std::runtime_error("Bad type");
}
template<class... Ts> struct overloaded : Ts... {
using Ts::operator()...;
overloaded(Ts... ts) : Ts(ts)... {}
};
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
template <typename ... Fs>
decltype(auto) visit(BaseType& base, Fs&&... fs)
{
return visitBaseType(base, overloaded(fs...));
}
Demo
I don't always say this, but this may be a job for the Boost.Preprocessor. You have a list of class types that corresponds to a list of enums, each instance identifies itself via getType(). So we can use that:
#include <boost/preprocessor/seq/for_each.hpp>
#define CLASS_LIST (TypeA) (TypeB)
// just take one visitor
template <class Visitor>
void visit(Base* ptr, Visitor f) {
switch (ptr->getType()) {
#define CASE_ST(r, data, elem) case elem: f(static_cast<elem*>(ptr)); break;
BOOST_PP_SEQ_FOR_EACH(CASE_ST, ~, CLASS_LIST)
#undef CASE_ST
default: f(ptr); // in case you want an "else"
// this is optional
}
}
That will preprocess into:
switch (ptr->getType()) {
case TypeA: f(static_cast<TypeA*>(ptr)); break;
case TypeB: f(static_cast<TypeB*>(ptr)); break;
default: f(ptr);
}

How do I generalize calling a list of functions in C++?

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;
}

Call templated method on each element in a vector of templated objects

I have a sequence of task objects that I'd like to chain together such that the output from one task is the input to the next.
template <typename Input, typename Output> class Task {
public:
std::function<Output(Input)> func;
Task(std::function<Output(Input)> func) : func(func){};
Output run(const Input& input) { return func(input); }
};
Great, now I can chain some tasks together.
#include <iostream>
#include "Task.h"
int main() {
auto func1 = [](int i) {
return i + 1;
};
auto func2 = [](std::string i) {
return stoi(i);
};
Task<int, int> task1(func1);
Task<std::string, int> task2(func2);
std::cout << task1.run(task2.run("1"));
return 0;
}
But now, I'd like to put tasks in a TaskList container so I don't need to hardcode these chains of tasks. The API might look as follows:
class TaskList {
public:
std::vector<ITask> tasks;
void append(ITask task) { tasks.push_back(task); }
void run() { // Run all tasks }
};
Where ITask is a base class for the various Task<Input, Output> types. How might I write such ITask and TaskList classes? There are many ways to shuffle where the types get introduced (e.g., Task constructor, run method), but I haven't found a way to generalize to TaskList.
Is this approach totally wrong? Or is what I'm trying to do bad design?
What about using recursion and template specialization, instead of an std::vector ?
The following should be a working example
#include <functional>
#include <iostream>
template <typename, typename ...>
class TaskList;
template <typename First>
class TaskList<First>
{
public:
TaskList ()
{ }
First run (First const & firstVal) const
{ return firstVal; }
};
template <typename Output, typename Input, typename ... Prevs>
class TaskList<Output, Input, Prevs...>
{
private:
std::function<Output(Input)> const head;
TaskList<Input, Prevs...> const tail;
public:
template <typename ... Funcs>
TaskList (std::function<Output(Input)> const & f0,
Funcs const & ... funcsPre)
: head {f0}, tail {funcsPre...}
{ }
template <typename T>
Output run (T const & firstArg) const
{ return head(tail.run(firstArg)); }
};
int main ()
{
auto func0 = [](int i) { return long(i << 1); };
auto func1 = [](int i) { return i + 1; };
auto func2 = [](std::string i) { return stoi(i); };
TaskList<long, int, int, std::string> taskL012(func0, func1, func2);
std::cout << taskL012.run("1") << std::endl; // print 4
}

Polymorphism, variadic template inheritance, slicing, boost::any type cast

This program compiles, but the boost::any cast fails. I suspect that slicing a template class this way confuses pointer arithmetic. The idea is that what is stored in the container
std::vector<boost::any> pressures;
are of different types, for example
Pressure<Printer>, or Pressure<Printer, Printer> etc.
Since I lose the type by storing it in a boost::any, I need to call Change without having to know the actual number of observers there are on a given pressure. I tried to solve it through polymorphism and virtual methods, but at least this attempt doesn't work.
Any suggestions?
#include <utility>
#include <tuple>
#include <iostream>
enum class EventType {UNKNOWN};
// Note: All Observers must implement OnNotify for any subject types they wish to observe
// Any unimplemented subject types that are used will result in a compiler error
template <typename Base> class Observer
{
public:
Observer() : obsID_(obsIDTracker_++) {}
template <typename T> void OnNotifyImpl(T &subject, EventType event)
{
static_cast<Base *>(this)->OnNotify(subject, event);
}
int GetID() const
{
return obsID_;
}
private:
int obsID_;
static int obsIDTracker_;
};
template <typename base> int Observer<base>::obsIDTracker_ = 0;
// Recursive helper structs for implementing calls to all observers held within subjects
template <int N, typename T, typename... Args> struct NotifyHelper
{
static void NotifyImpl(T &subject, EventType event,
std::tuple<Args...> &obs)
{
std::get<sizeof...(Args) - N>(obs).OnNotifyImpl(subject, event);
NotifyHelper<N - 1, T, Args...>::NotifyImpl(subject, event, obs);
}
};
template <typename T, typename... Args> struct NotifyHelper<0, T, Args...>
{
static void NotifyImpl(T &subject, EventType event,
std::tuple<Args...> &obs) {}
};
// See MakeSubject function for instance usage
template <typename T, typename... Obs> class Subject
{
public:
static const int NumberOfObservers = sizeof...(Obs);
Subject(std::tuple<Obs &...> &&obs) : observers(obs) {}
void NotifyAll(EventType event)
{
NotifyHelper<NumberOfObservers, T, Obs &...>::NotifyImpl(
*static_cast<T *>(this), event, observers);
}
private:
std::tuple<Obs &...> observers;
};
class PressureInterface
{
public:
virtual ~PressureInterface() {}
virtual void Change(int value) {}
};
// CRTP Abstract Base class for implementing static subject.
// Example Subclass Usage -- Pressure Sensor:
template <typename... Obs>
class Pressure : public PressureInterface, public Subject<Pressure<Obs...>, Obs...>
{
public:
typedef Subject<Pressure<Obs...>, Obs...> BaseType;
Pressure(std::tuple<Obs &...> &&observers, int pressure)
: BaseType(std::move(observers)), pressure_(pressure) {}
virtual void Change(int value)
{
pressure_ = value;
this->NotifyAll(EventType::UNKNOWN);
}
int GetPressure() const
{
return pressure_;
}
private:
int pressure_;
};
// CRTP Abstract Base class for implementing static subject.
// Example Subclass Usage -- Printing Observer:
class Printer : public Observer<Printer>
{
public:
Printer() : timesTriggered_(0) {}
template <typename... Args>
void OnNotify(Pressure<Args...> &subject, EventType event)
{
std::cout << "Observer ID: " << this->GetID() << std::endl;
switch (event)
{
case EventType::UNKNOWN:
{
std::cout << "Unknown Event -- Event #" << timesTriggered_++
<< std::endl;
std::cout << "Pressure: " << subject.GetPressure() << std::endl;
break;
}
default:
{
break;
}
}
}
private:
int timesTriggered_;
};
// Binding function for use with MakeSubject
// Arguments: observer objects to observe subject notifications
// Return: tuple of references to observers
template <typename... Obs> std::tuple<Obs &...> BindObservers(Obs &... obs)
{
return std::tuple<Obs &...>(obs...);
}
// Creator to ease subject creation
// Template Arguments: Subject subclass type
// Arguments: Result from BindObservers
// Any constructor arguments for Subject subclass
// Return: Subject subclass
// Example Usage:
// auto pressure = MakeSubject<Pressure>(BindObservers(printerObs), initialPressure);
template <template <typename...> class T, typename... Args, typename... Obs>
T<Obs...> MakeSubject(std::tuple<Obs &...> &&obs, Args &&... args)
{
return T<Obs...>(std::move(obs), args...);
}
#include <boost/any.hpp>
int main()
{
std::vector<boost::any> pressures;
Printer printerObs1;
Printer printerObs2;
const int initialPressure = 1;
auto pressure = MakeSubject<Pressure>(
BindObservers(printerObs1, printerObs2), initialPressure);
pressures.push_back(pressure);
pressure.Change(12);
decltype(pressure) *p = boost::any_cast<decltype(pressure)>(&pressures[0]);
p->Change(1999);
PressureInterface *qip = boost::any_cast<PressureInterface>(&pressures[0]); //This cast returns nullptr
std::cout << "The cast works\n";
if(nullptr != qip)
qip->Change(2001);
}
Edit
My first attempt at storing the address of the Change function:
std::vector<std::function<boost::any *>> pressures;
How do I push_back the address of the function? This doesn't work:
pressures.push_back(std::function<decltype(&pressure.Change>);
/home/idf/Documents/OrigObserverExam/ObserverExample.cpp|157|error: ISO C++ forbids taking the address of a bound member function to form a pointer to member function. Say '&Pressure<Printer, Printer>::Change' [-fpermissive]|
and then how do I extract it?
std::function<void(int)> *qip = boost::any_cast<std::function<void(int)>*>(&(pressures[0].Change));
std::cout << "The cast works\n";
if(nullptr != qip)
*qip(2001);
Edit 2
When I add the code suggested, I get an error:
/home/idf/Documents/OrigObserverExam/ObserverExample.cpp|167|error: 'decay_t' is not a member of 'std'|
#include <type_traits>
#include <boost/any.hpp>
struct changable {
boost::any data;
using do_change = void(*)(boost::any*, int);
do_change f = nullptr;
void change(int x) {
if (f) f(&data, x);
}
template<class T>
static do_change type_erase_change() {
return [](boost::any* a, int x){
T* t = boost::any_cast<T>(a);
if (t) t->Change(x);
};
}
template<class T>
changable( T&& t ):
data(std::forward<T>(t)),
f( type_erase_change<std::decay_t<T>>() )
{}
changable(changable const&)=default;
changable(changable &&)=default;
changable()=default;
};
Edit 3 C++14 installed:
How do I use this struct? I am able to say:
std::vector<changable> pressures;
and I am able to push_back a pressure
pressures.push_back(pressure);
However, I am uncertain how to call say pressures[0].Change(1999). If I say I get the error given:
pressures[0].Change(2000);
ObserverExample.cpp|199|error: '__gnu_cxx::__alloc_traits<std::allocator<changable> >::value_type' has no member named 'Change'
boost::any allows you to type cast back to the exact same type you put in. Not a parent type, the same type.
If you want to type erase invoking a method, try std::function<void()> or std::function<void(boost::any*)>.
Here is a type eraser of change(int) and a boost::any bundled together:
struct changable {
boost::any data;
using do_change = void(*)(boost::any*, int);
do_change f = nullptr;
void change(int x) {
if (f) f(&data, x);
}
template<class T>
static do_change type_erase_change() {
return [](boost::any* a, int x){
T* t = boost::any_cast<T>(a);
if (t) t->Change(x);
};
}
template<class T>
changable( T&& t ):
data(std::forward<T>(t)),
f( type_erase_change<std::decay_t<T>>() )
{}
changable(changable const&)=default;
changable(changable &&)=default;
changable()=default;
};
there is no need for an interface class that exposes Change. So long as the type passed to the above type-eraser has a Change(int) method, all is good.