C++11 Variadic Class Handler Manager - c++

I'm trying to create a template class (event class manager) that will instantiate multiple event class objects. I'm able to get it work for one class, but not able to get it work in the variadic case. I'm trying to get the class_mgr class to take in a list of event classes. For each one, I would like an entry in the std::array.
#include <array>
class handler {
public:
void handle() {}
};
enum event_class_types {
eclass1 = 0,
eclass2,
num_classes,
};
class base {
};
template<typename handlerType>
class event_class1 : public base {
public:
event_class1(handlerType& handler) :
handler_(handler) {}
private:
handlerType& handler_;
};
template<typename handlerType>
class event_class2 : public base {
public:
event_class2(handlerType& handler) :
handler_(handler) {}
private:
handlerType& handler_;
};
namespace map {
template<typename handlerType>
struct event_class1 {
using type = ::event_class1<handlerType>;
static constexpr int id = eclass1;
};
template<typename handlerType>
struct event_class2 {
using type = ::event_class2<handlerType>;
static constexpr int id = eclass2;
};
}
//TODO: variadic
template<typename handlerType, template< typename T> class mapTypes>
class event_class_mgr
{
public:
event_class_mgr(handlerType& handler) :
handler_(handler)
{
add_class();
}
private:
void add_class( )
{
my_classes_[ mapTypes<handlerType>::id ] = new typename mapTypes<handlerType>::type(handler_);
}
handlerType handler_;
std::array< base*, event_class_types::num_classes> my_classes_{};
};
int main(int argc, char** argv) {
handler my_handler;
event_class_mgr<handler, map::event_class1> cm(my_handler);
//Trying to make this variadic, to pass multiple types to my handler
//class_mgr<handler, map::event_class1, map::event_class2> cm(my_handler);
//alternatively, i could just pass the enums?
//class_mgr<handler, event_eclass1, event_eclass2> cm(my_handler);
return 0;
};

If I got what you mean, it's straightforward.
Just turn the class template declaration to:
template<typename handlerType, template< typename T> class... mapTypes>
class event_class_mgr {
// ...
}
Then update the add_class member function:
void add_class( )
{
using accumulator_type = int[];
accumulator_type accumulator = { (my_classes_[ mapTypes<handlerType>::id ] = new typename mapTypes<handlerType>::type(handler_), 0)... };
(void)accumulator;
}
Using C++17 and thus fold expressions would have reduced the boilerplate, but you explicitly marked the question as C++11, so...
See it up and running on wandbox.
As a side note, in C++17 you can write add_class as:
void add_class( )
{
((my_classes_[ mapTypes<handlerType>::id ] = new typename mapTypes<handlerType>::type(handler_)), ...);
}

Related

C++ lambda as parameter with variable arguments

I want to create an event system that uses lambda functions as its subscribers/listeners, and an event type to assign them to the specific event that they should subscribe to. The lambdas should have variable arguments, as different kinds of events use different kinds of arguments/provide the subscribers with different kinds of data.
For my dispatcher, I have the following:
class EventDispatcher {
public:
static void subscribe(EventType event_type, std::function<void(...)> callback);
void queue_event(Event event);
void dispatch_queue();
private:
std::queue<Event*> event_queue;
std::map<EventType, std::function<void(...)>> event_subscribers;
};
No issues here, but when I go to implement the subscribe() function in my .cpp file, like this:
void EventDispatcher::subscribe(EventType event_type, std::function<void(...)> callback) {
... (nothing here yet)
}
The IDE shows me this:
Implicit instantiation of undefined template 'std::function<void (...)>'
Don't try to plop event callbacks with different parameter types directly into a single map.
Instead, create a template to store the callback (templated by the parameter types), and store pointers to its non-template base.
Here's how I would do it:
#include <functional>
#include <iostream>
#include <map>
#include <memory>
#include <queue>
#include <tuple>
#include <typeindex>
#include <typeinfo>
#include <type_traits>
#include <utility>
struct Event
{
virtual ~Event() = default;
};
struct Observer
{
virtual ~Observer() = default;
virtual void Observe(const Event &e) const = 0;
};
template <typename ...P>
struct BasicEvent : Event
{
std::tuple<P...> params;
BasicEvent(P ...params) : params(std::move(params)...) {}
struct EventObserver : Observer
{
std::function<void(P...)> func;
template <typename T>
EventObserver(T &&func) : func(std::forward<T>(func)) {}
void Observe(const Event &e) const override
{
std::apply(func, dynamic_cast<const BasicEvent &>(e).params);
}
};
// We need a protected destructor, but adding one silently removes the move operations.
// And adding the move operations removes the copy operations, so we add those too.
BasicEvent(const BasicEvent &) = default;
BasicEvent(BasicEvent &&) = default;
BasicEvent &operator=(const BasicEvent &) = default;
BasicEvent &operator=(BasicEvent &&) = default;
protected:
~BasicEvent() {}
};
class EventDispatcher
{
public:
template <typename E>
void Subscribe(typename E::EventObserver observer)
{
event_subscribers.insert_or_assign(typeid(E), std::make_unique<typename E::EventObserver>(std::move(observer)));
}
template <typename E>
void QueueEvent(E &&event)
{
event_queue.push(std::make_unique<std::remove_cvref_t<E>>(std::forward<E>(event)));
}
void DispatchQueue()
{
while (!event_queue.empty())
{
Event &event = *event_queue.front();
event_subscribers.at(typeid(event))->Observe(event);
event_queue.pop();
}
}
private:
std::queue<std::unique_ptr<Event>> event_queue;
std::map<std::type_index, std::unique_ptr<Observer>> event_subscribers;
};
struct EventA : BasicEvent<> {using BasicEvent::BasicEvent;};
struct EventB : BasicEvent<> {using BasicEvent::BasicEvent;};
struct EventC : BasicEvent<int, int> {using BasicEvent::BasicEvent;};
int main()
{
EventDispatcher dis;
dis.Subscribe<EventA>([]{std::cout << "Observing A!\n";});
dis.Subscribe<EventB>([]{std::cout << "Observing B!\n";});
dis.Subscribe<EventC>([](int x, int y){std::cout << "Observing C: " << x << ", " << y << "!\n";});
dis.QueueEvent(EventA());
dis.QueueEvent(EventB());
dis.QueueEvent(EventC(1, 2));
dis.DispatchQueue();
}
You could create your own version of std::function that accepts functions of any signature using type erasure. This will require some heavy lifting though. I will provide a solution for void functions which requires C++17, because we will use std::any.
I will walk you through the steps first and then provide a full solution in code.
First you create some function_traits that capture the number and type of arguments of any kind of function using template meta-programming. We can "borrow" from here.
Then you create a class VariadicVoidFunction that has a templated call operator.
This call operator creates a std::vector<std::any> and passes it to the invoke method of a member of VariadicVoidFunction, which is a (resource-owning smart) pointer of type VariadicVoidFunction::Concept.
VariadicVoidFunction::Concept is an abstract base class with a virtual invoke method that accepts std::vector<std::any>.
VariadicVoidFunction::Function is a class template, where the template parameter is a function. It stores this function as member and inherits VariadicVoidFunction::Concept. It implements the invoke method. Here, we can std::any_cast the vector elements back to the expected types, which we can extract using function_traits. This allows us to call the actual function with the correct argument types.
VariadicVoidFunction gets a templated constructor accepting any kind of function F. It creates an instance of type VariadicVoidFunction::Function<F> and stores it in an owning (smart) pointer.
#include <memory>
#include <vector>
#include <any>
// function_traits and specializations are needed to get arity of any function type
template<class F>
struct function_traits;
// ... function pointer
template<class R, class... Args>
struct function_traits<R(*)(Args...)> : public function_traits<R(Args...)>
{};
// ... normal function
template<class R, class... Args>
struct function_traits<R(Args...)>
{
static constexpr std::size_t arity = sizeof...(Args);
template <std::size_t N>
struct argument
{
static_assert(N < arity, "error: invalid parameter index.");
using type = typename std::tuple_element<N,std::tuple<Args...>>::type;
};
};
// ... non-const member function
template<class C, class R, class... Args>
struct function_traits<R(C::*)(Args...)> : public function_traits<R(C&,Args...)>
{};
// ... const member function
template<class C, class R, class... Args>
struct function_traits<R(C::*)(Args...) const> : public function_traits<R(C const&,Args...)>
{};
// ... functor (no overloads allowed)
template<class F>
struct function_traits
{
private:
using call_type = function_traits<decltype(&F::operator())>;
public:
static constexpr std::size_t arity = call_type::arity - 1;
template <std::size_t N>
struct argument
{
static_assert(N < arity, "error: invalid parameter index.");
using type = typename call_type::template argument<N+1>::type;
};
};
template<class F>
struct function_traits<F&> : public function_traits<F>
{};
template<class F>
struct function_traits<F&&> : public function_traits<F>
{};
// type erased void function taking any number of arguments
class VariadicVoidFunction
{
public:
template <typename F>
VariadicVoidFunction(F const& f)
: type_erased_function{std::make_shared<Function<F>>(f)} {}
template <typename... Args>
void operator()(Args&&... args){
return type_erased_function->invoke(std::vector<std::any>({args...}));
}
private:
struct Concept {
virtual ~Concept(){}
virtual void invoke(std::vector<std::any> const& args) = 0;
};
template <typename F>
class Function : public Concept
{
public:
Function(F const& f) : func{f} {}
void invoke(std::vector<std::any> const& args) override final
{
return invoke_impl(
args,
std::make_index_sequence<function_traits<F>::arity>()
);
}
private:
template <size_t... I>
void invoke_impl(std::vector<std::any> const& args, std::index_sequence<I...>)
{
return func(std::any_cast<typename function_traits<F>::template argument<I>::type>(args[I])...);
}
F func;
};
std::shared_ptr<Concept> type_erased_function;
};
You can use it like this:
#include <unordered_map>
#include <iostream>
int main()
{
VariadicVoidFunction([](){});
std::unordered_map<size_t, VariadicVoidFunction> map =
{
{0, VariadicVoidFunction{[](){ std::cout << "no argument\n"; }} },
{1, VariadicVoidFunction{[](int i){ std::cout << "one argument\n"; }} },
{2, VariadicVoidFunction{[](double j, const char* x){ std::cout<< "two arguments\n"; }} }
};
map.at(0)();
map.at(1)(42);
map.at(2)(1.23, "Hello World");
return 0;
}
no argument
one argument
two arguments
Demo on Godbolt Compiler explorer
Note that this is a prototypical solution to get you started. One downside is that all arguments will be copied into the std::any You could avoid this by passing pointers to std::any, but you have to be careful with lifetimes when you do this.
After the input from #joergbrech and #HolyBlackCat I made this
enum class EventType {
WindowClosed, WindowResized, WindowFocused, WindowLostFocus, WindowMoved,
AppTick, AppUpdate, AppRender,
KeyPressed, KeyRelease,
MouseButtonPressed, MouseButtonRelease, MouseMoved, MouseScrolled,
ControllerAxisChange, ControllerButtonPressed, ControllerConnected, ControllerDisconnected
};
class IEvent {
public:
IEvent(EventType event_type) {
this->event_type = event_type;
}
EventType get_event_type() {
return event_type;
}
private:
EventType event_type;
};
class IEventSubscriber {
public:
/**
* #param event The event that is passed to the subscriber by the publisher; should be cast to specific event
* */
virtual void on_event(IEvent *event) = 0;
EventType get_event_type() {
return event_type;
}
protected:
explicit IEventSubscriber(EventType event_type) {
this->event_type = event_type;
}
private:
EventType event_type;
};
class FORGE_API EventPublisher {
public:
static void subscribe(IEventSubscriber *subscriber);
static void queue_event(IEvent *event);
static void dispatch_queue();
private:
static std::queue<IEvent*> event_queue;
static std::set<IEventSubscriber*> event_subscribers;
};
I've tested it and I get the expected result from this solution. For the full code solution -> https://github.com/F4LS3/forge-engine
std::function has no specialization for variadic function types.
You likely want std::function<void()>.

Ignore template parameter

Is there a way in the code below to say that I don't care what the V parameter is in void watch(event<T, _> &e)
#include <stdlib.h>
#include <stdio.h>
enum events_a {
A_1,
A_2,
};
enum events_b {
B_1,
B_2,
};
template<typename T, T V>
class event {
public:
T id(void) {
return V;
}
};
template<typename T>
class event_loop {
public:
void watch(event<T, _> &e) {
};
};
class a1 : public event<enum events_a, A_2>
{
};
int
main (int argc, char *argv[])
{
a1 a;
printf("%d\n", a.id());
return 0;
}
What I'm trying to do is that users should be forced to subclass event<T,V> and be forced to give T and V.
However the subclass of event_loop<T>, specifically the method watch, should work with all event<T> regardless of their V.
I don't want to template just the watch function as I want the user to be forced to subclass event_loop.
Bonus achievement if I can somehow make sure all subclasses of event<T> have a unique V. Shooting for the stars... :D
Just create a template argument for it and ignore it.
template<typename T, typename Ignore>
class event_loop {
public:
void watch(event<T, Ignore> &e) {
};
};
You could also template just the function, if you want only the function to be templated
template<typename T>
class event_loop {
public:
template<typename Ignore>
void watch(event<T, Ignore> &e) {
};
};
You could specify a default argument for V and static assert like the way below:
enum events_a {
A_1 = 1,
A_2,
};
enum events_b {
B_1 = 1,
B_2,
};
template<typename T, T V = {}>
class event {
static_assert(V != 0, "You should Define a V");
public:
T id() {
return V;
}
};
template<typename T>
struct event_loop {
void watch(event<T> &e) {}
};
class a1 : public event<enum events_a, A_2> { };
Live Demo

aliassing a template parameter pack

I would like to use a using alias for a parameter pack such that the templates can be used elsewhere in the code base. In the following code I commented the lines where I would use the types:
template <typename FirstArg, typename ... OtherArgs>
struct Base {
using UsableFirstArg = FirstArg;
// this would be needed later
// using UsableOtherArgs... = OtherArgs...;
virtual OtherClass method(OtherArgs... a) = 0
};
template <typename DerivedOfBaseInstantiation>
struct CallerStructure {
// to be used here
// OtherClass unknownCall(typename DerivedOfBaseInstantiation::UsableOtherArgs... args) {
// DerivedOfBaseInstantiation instance;
// return instance.method(args...);
// }
}
At code write time of CallerStructure, the arguments of unknownCall are not known and are determined by the instantiation of CallerStructure where DerivedOfBaseInstantiation is a type that's derived of Base. In a more complete example that would look like that:
class OtherClass {
};
template <typename FirstArg, typename ... OtherArgs>
struct Base {
using UsableFirstArg = FirstArg;
using UsableOtherArgs... = OtherArgs...;
virtual OtherClass method(OtherArgs... a) = 0;
};
struct Derived_df : Base<int, double, float> {
OtherClass someMethod(Base::UsableFirstArg); // int
OtherClass method(double, float) override ;
};
template <typename DerivedOfBaseInstantiation>
struct CallerStructure {
OtherClass knownCall(typename DerivedOfBaseInstantiation::UsableFirstArg a) {
DerivedOfBaseInstantiation instance;
return instance.someMethod(a);
}
OtherClass unknownCall(typename DerivedOfBaseInstantiation::UsableOtherArgs... args) {
DerivedOfBaseInstantiation instance;
return instance.method(args...);
}
};
void instantiations() {
CallerStructure<Derived_df> c;
[[maybe_unused]] auto a = c.knownCall(42);
[[maybe_unused]] auto b = c.unknownCall(23., 11.f);
}
Any hints of how to access the variadic templates of Base for the method interface in CallerStructure?
mandatory compiler-explorer link
You cannot alias variadic template argument. You can wrap them in std::tuple:
template <typename FirstArg, typename ... OtherArgs>
struct Base {
using UsableFirstArg = FirstArg;
using UsableOtherArgs = std::tuple<OtherArgs...>;
virtual OtherClass method(OtherArgs... a) = 0;
};
Unwrapping need some helpers:
template <typename DerivedOfBaseInstantiation,
typename = typename DerivedOfBaseInstantiation::UsableOtherArgs>
struct CallerStructure;
template <typename DerivedOfBaseInstantiation, typename ... OtherArgs>
struct CallerStructure<DerivedOfBaseInstantiation, std::tuple<OthersArgs...>> {
OtherClass knownCall(typename DerivedOfBaseInstantiation::UsableFirstArg a) {
DerivedOfBaseInstantiation instance;
return instance.someMethod(a);
}
OtherClass unknownCall(OtherArgs... args) {
DerivedOfBaseInstantiation instance;
return instance.method(args...);
}
};

creates a tuple contains different drive class elements whose constructor received an int type, determined by its index in the tuple

I have a base class whose constructor receives an int type named id, and several different derive class, with the same form of constructor as the base class.
Now I want to make a tuple that contains each of these elements, with its constructor receives an id determined by its index in this tuple. Like what the following dumb function does:
class Base(){
Base(int id){}
}
class Derive1, Derived2...Derivedn : public Base(){
Derive(int id):Base(id){}
}
auto make_derives_tuple()->decltype(...){
//manually write each elements' index in the tuple seems too ugly and unnecessary
return std::make_tuple(Derive1(0),Derived2(1),Derived3(2)...);
}
if the num of derived class is three:
struct Base{
Base(int id){
id_=id;
}
int id_;
};
struct Derive:public Base{
Derive(int id):Base(id){
}
};
struct Derive2:public Base{
Derive2(int id):Base(id){
}
};
auto make_derive_tuple()->decltype (std::make_tuple(Derive(0),Derive2(1),Derive3(2))){
//I want the int passed to the derived class's construor automatically generated according to it's position in the tuple
return std::make_tuple(Derive(0),Derive2(1),Derive3(2));
}
But manually write each elements's index in the tuple to pass to the constructor seems too ugly and unnecessary. Is there any elegant way of achieving this? Like using variadic template class or functions.
I don't see an elegant way to iterate over simply classes as Derived1, Derived2, Derived3, etc.
But is different if you can templatize your derive classes, adding a template index, as follows or in a similar way
template <std::size_t>
struct Derived : public Base
{ Derived (int id) : Base{id} {} };
If you can also use C++14, you can use std::make_index_sequence/std::index_sequence as follows
template <std::size_t ... Is>
auto make_helper (std::index_sequence<Is...> const &)
{ return std::make_tuple(Derived<Is+1u>{Is}...); }
template <std::size_t N>
auto make_derives_tuple ()
{ return make_helper(std::make_index_sequence<N>{}); }
The following is a full compiling example
#include <tuple>
#include <utility>
#include <type_traits>
struct Base
{ Base (int) {} };
template <std::size_t>
struct Derived : public Base
{ Derived (int id) : Base{id} {} };
template <std::size_t ... Is>
auto make_helper (std::index_sequence<Is...> const &)
{ return std::make_tuple(Derived<Is+1u>{Is}...); }
template <std::size_t N>
auto make_derives_tuple ()
{ return make_helper(std::make_index_sequence<N>{}); }
int main()
{
auto t = make_derives_tuple<3u>();
using T0 = decltype(t);
using T1 = std::tuple<Derived<1u>, Derived<2u>, Derived<3u>>;
static_assert( std::is_same<T0, T1>::value, "!" );
}
If you can't templatize (adding an index) the derived classes, the best I can imagine is pass the required derived classes as template variadic list to make_derived_tuple().
The solution become
template <typename ... Ts, std::size_t ... Is>
auto make_helper (std::index_sequence<Is...> const &)
{ return std::make_tuple(Ts{Is}...); }
template <typename ... Ts>
auto make_derives_tuple ()
{ return make_helper<Ts...>(std::index_sequence_for<Ts...>{}); }
The following is a full compiling example (where I've renamed A, B, C and D the derived classes
#include <tuple>
#include <utility>
#include <type_traits>
struct Base
{ Base (int) {} };
struct A : public Base
{ A (int id) : Base{id} {} };
struct B : public Base
{ B (int id) : Base{id} {} };
struct C : public Base
{ C (int id) : Base{id} {} };
struct D : public Base
{ D (int id) : Base{id} {} };
template <typename ... Ts, std::size_t ... Is>
auto make_helper (std::index_sequence<Is...> const &)
{ return std::make_tuple(Ts{Is}...); }
template <typename ... Ts>
auto make_derives_tuple ()
{ return make_helper<Ts...>(std::index_sequence_for<Ts...>{}); }
int main()
{
auto t = make_derives_tuple<A, B, C, D>();
using T0 = decltype(t);
using T1 = std::tuple<A, B, C, D>;
static_assert( std::is_same<T0, T1>::value, "!" );
}

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.