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.
Related
Please, before marking this as a duplicate of This question read the entirety of the post
This piece of code fails to compile, with a template deduction error:
#include <iostream>
#include <type_traits>
template<typename T = float, int N>
class MyClass
{
public:
template<typename DATA_TYPE>
using MyType = std::conditional_t<(N>0), DATA_TYPE, double>;
MyType<T> Var;
void Foo()
{
Bar(Var);
}
template<typename TYPE>
void Bar(MyType<TYPE> Input)
{
std::cout << typeid(Input).name() << std::endl;
}
};
int main()
{
MyClass<float, 1> c;
c.Foo();
return 0;
}
I understand the point that was made in the question i linked above, which is that "the condition which allows to choose the type to be deduced depends on the type itself", however, why would the compiler fail in the specific case i provided as the condition here seems to be fully independent from the type, or is there something i'm missing?
I would be more than happy if someone could refer to a section of the c++ standard that would allow me to fully understand this behaviour.
As the linked question, TYPE is non deducible. MyType<TYPE> is actually XXX<TYPE>::type.
You have several alternatives, from your code, I would say one of
Bar no longer template:
template<typename T = float, int N>
class MyClass
{
public:
template<typename DATA_TYPE>
using MyType = std::conditional_t<(N>0), DATA_TYPE, double>;
MyType<T> Var;
void Foo()
{
Bar(Var);
}
void Bar(MyType<T> Input)
{
std::cout << typeid(Input).name() << std::endl;
}
};
requires (or SFINAE/specialization for pre-c++20):
template<typename T = float, int N>
class MyClass
{
public:
template<typename DATA_TYPE>
using MyType = std::conditional_t<(N>0), DATA_TYPE, double>;
MyType<T> Var;
void Foo()
{
Bar(Var);
}
template<typename TYPE>
void Bar(TYPE Input) requires(N > 0)
{
std::cout << typeid(Input).name() << std::endl;
}
void Bar(double Input) requires(N <= 0)
{
std::cout << typeid(Input).name() << std::endl;
}
};
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 have a following code:
#include <iostream>
class A
{};
class B
{};
template<typename T>
void Do(T data)
{
std::cout << "Do() default\n";
}
template<>
void Do(A* data)
{
std::cout << "Do(A*)\n";
}
template<>
void Do(B* data)
{
std::cout << "Do(B*)\n";
}
int main(int argc, char* argv[])
{
A* a = nullptr;
B* b = nullptr;
const A* aConst = nullptr;
const B* bConst = nullptr;
Do(a);
Do(aConst);
Do(b);
Do(bConst);
return 0;
}
which outputs:
Do(A*)
Do() default
Do(B*)
Do() default
How should I rewrite the code to share template specialization for const & non-const type without copy pasting the specialization with const keyword specifier so it produces output:
Do(A*)
Do(A*)
Do(B*)
Do(B*)
Instead of specializing, you can overload. Using
template<typename T, std::enable_if_t<std::is_same_v<std::decay_t<T>, A>, bool> = true>
void Do(T* data)
{
std::cout << "Do(A*)\n";
}
template<typename T, std::enable_if_t<std::is_same_v<std::decay_t<T>, B>, bool> = true>
void Do(T* data)
{
std::cout << "Do(B*)\n";
}
These functions will be called when you pass const A*/A*/const B*/B* since they are a better match then the generic template. The reason they are a better match is because T is more constrained. It is considered more specialized and so it will win in a tie breaker with the generic template in overload resolution.
C++14
I upvoted this answer which is a good showcase for SFINAE. To extend on it, you can simplify the SFINAE expression with std::is_convertible. This is more analogous how overload resolution would work (adding const qualifier).
template<typename T, std::enable_if_t<std::is_convertible_v<T*, const A*>, int> = 0>
void Do(T* data)
{
std::cout << "Do(A*)\n";
}
template<typename T, std::enable_if_t<std::is_convertible_v<T*, const B*>, int> = 0>
void Do(T* data)
{
std::cout << "Do(B*)\n";
}
C++17
As a bonus, in C++17 with constexpr if, you can use a single function for all cases:
template<typename T>
void Do(T data)
{
if constexpr (std::is_convertible_v<T, const A*>)
std::cout << "Do(A*)\n";
else if constexpr (std::is_convertible_v<T, const B*>)
std::cout << "Do(B*)\n";
else
std::cout << "Do() default\n";
}
You could use this pattern, if you are willing to write a small stub:
template<>
void Do(const A* data)
{
std::cout << "Do(A*)\n";
}
template<>
void Do(A* data)
{
Do((const A*)data);
}
The main code you don't want to duplicate uses const A*, because, well, you want it to work on const data too. The non-const one simply forward to it.
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.
I am trying to implement a vector that can take elements of several types, and can apply a function on all of them. This is easily done with a base class, virtual functions and inheritance, but I explicity do not want to use it. Here is how far I am so far:
#include <iostream>
#include <vector>
#include <tuple>
// this will be my new polymorphic vector;
template<typename... Ts>
class myvector {
std::tuple<std::vector<Ts>...> vectors;
template <template<typename> class funtype>
void for_each() {
}
template <template<typename> class funtype, typename X, typename... Xs>
void for_each() {
std::vector<X>& vector = std::get<std::vector<X>>(vectors);
for ( X& x : vector ) {
funtype<X> fun;
fun(x);
}
for_each<funtype, Xs...>();
}
public:
template <typename T>
void push_back(const T& t) {
std::vector<T>& vector = std::get<std::vector<T>>(vectors);
vector.push_back(t);
}
template <typename T>
void pop_back() {
std::vector<T>& vector = std::get<std::vector<T>>(vectors);
vector.pop_back();
}
/* here I would like to pass a function, or function object that
* can be expanded to all underlying types. I would prefer to just
* give a function name, that has an implementation to all types in Ts
*/
template <template<typename> class funtype>
void ForEach() {
for_each<funtype,Ts...>();
}
};
struct foo {
};
struct bar {
};
template <typename T>
void method(T& t);
template<>
void method(foo& b) {
std::cout << "foo" << std::endl;
}
template<>
void method(bar& b) {
std::cout << "bar" << std::endl;
}
int main()
{
myvector<foo,bar> mv;
mv.push_back( foo{} );
mv.push_back( bar{} );
mv.ForEach<method>();
}
at the moment I am kind of stuck, I hope you can give me some advise on how to go further.
A common solution is to use a function object with a set of operator():
struct my_fun_type
{
void operator()(foo&) const
{ std::cout << "foo\n"; }
void operator()(bar&) const
{ std::cout << "bar\n"; }
};
This allows to pass a "set" of overloaded functions to an algorithm, state, and is rather convenient to use:
my_algorithm(my_fun_type{});
If we want to add support for such function objects, we could define ForEach as follows:
template <typename Elem, typename Fun>
void for_each(Fun&& fun) {
std::vector<Elem>& vector = std::get<std::vector<Elem>>(vectors);
for ( Elem& e : vector ) {
fun(x);
}
}
template <typename Fun>
void ForEach(Fun&& fun) {
int dummy[] = { 0, (for_each<Ts>(fun), 0)... };
(void)dummy;
}
That dummy is a trick to call for_each for all types in Ts. The (void)dummy is intended to suppress a compiler warning (dummy is never read from).
You can learn more about this technique in other Q&As, such as that one.
The Fun&& is not an rvalue reference, but a universal reference.
Note that the above example differs from many Standard Library algorithms, which take the function object by value:
template <typename Elem, typename Fun>
void for_each(Fun fun) {
std::vector<Elem>& vector = std::get<std::vector<Elem>>(vectors);
std::for_each(vector.begin(), vector.end(), std::move(fun));
}
template <typename Fun>
void ForEach(Fun fun) {
int dummy[] = { 0, (for_each<Ts>(fun), 0)... };
(void)dummy;
}
To pass a set of overloaded free functions, we can wrap them in a function object (thank #Yakk for the suggestion):
struct method_t
{
template<class... Ts>
void operator()(Ts&&... ts) const
{ method( std::forward<Ts>(ts)... ); }
};
In C++1y, such a function object type can be created with less boilerplate using a polymorphic lambda:
[](auto&&... pp)
{ method( std::forward<decltype(pp)>(pp)... ); }