C++ Factory using variadic template issues - c++

I'm just trying something out - I wanted to create a generic factory which will returns shared_ptr to a type.
I have a derived class which uses static methods to return a shared_ptr to the base class. The idea is that I'd like to be able to register these methods with a generic factory, but it can't determine which method to register at compile time. Perhaps there is a way of achieving this using SFINAE, but I'm just beginning to understand its complexities.
Apologies for the rather long code sample, also available at http://coliru.stacked-crooked.com/a/331e08de86004592
Enabling more than one of the factory methods in 'DerivedA' will cause a compile error.
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <memory>
// Factory which returns a shared_ptr of type T.
template<class T, class Tag, class... Args>
class NameFactory
{
public:
typedef std::function<std::shared_ptr<T>(Args...)> Function;
static NameFactory& instance();
void registerType(const std::string& type, const Function& createFunction);
std::shared_ptr<T> createObject(const std::string& type, Args&&... arguments);
private:
NameFactory() {}
std::unordered_map<std::string, Function> m_functionMap;
};
template<class T, class Tag, class... Args>
NameFactory<T, Tag, Args...>& NameFactory<T, Tag, Args...>::instance()
{
static NameFactory<T, Tag, Args...> m_instance;
return m_instance;
}
template<class T, class Tag, class... Args>
void NameFactory<T, Tag, Args...>::registerType(const std::string& type, const Function& createFunction)
{
m_functionMap[type] = createFunction;
}
template<class T, class Tag, class... Args>
std::shared_ptr<T> NameFactory<T, Tag, Args...>::createObject(const std::string& type, Args&&... arguments)
{
auto iter(m_functionMap.find(type));
if (iter != m_functionMap.end())
{
return (iter->second)(std::forward<Args>(arguments)...);
}
throw std::logic_error("Cannot find constructor for type '" + type + "'");
}
template<class T, class Tag, class... Args>
class NameFactoryRegistration
{
public:
typedef NameFactory<T, Tag, Args...> Factory;
NameFactoryRegistration(const std::string& type, const typename Factory::Function& createFunction)
{
Factory::instance().registerType(type, createFunction);
}
private:
};
class MyBase
{
public:
typedef std::shared_ptr<MyBase> SPtr;
};
class DerivedA : public MyBase
{
public:
static SPtr create()
{
return SPtr(new DerivedA);
}
// Enabling this factory method (and/or the two args method below causes an 'unresolved overloaded function type' error
//static SPtr create(const std::string& s)
//{
// return SPtr(new DerivedA(s));
//}
//static SPtr create(const std::string& s, double d)
//{
// return SPtr(new DerivedA(s,d));
//}
private:
DerivedA()
{
std::cout << "DerivedA - no args" << std::endl;
}
DerivedA(const std::string& s)
{
std::cout << "DerivedA - one arg: " << s << std::endl;
}
DerivedA(const std::string& s, double d)
{
std::cout << "DerivedA - two args: " << s << " : " << d << std::endl;
}
};
// Tags to help differentiate the factories
struct NoArgsReg;
struct SingleArgReg;
struct TwoArgReg;
typedef NameFactory<MyBase, NoArgsReg> NoArgsFactory;
typedef NameFactoryRegistration<MyBase, NoArgsReg> NoArgsRegistration;
typedef NameFactory<MyBase, SingleArgReg, const std::string&> SingleArgFactory;
typedef NameFactoryRegistration<MyBase, SingleArgReg, const std::string&> SingleArgRegistration;
typedef NameFactory<MyBase, TwoArgReg, const std::string&, double> TwoArgsFactory;
typedef NameFactoryRegistration<MyBase, TwoArgReg, const std::string&, double> TwoArgsRegistration;
// Register the factory methods into the NameFactory
NoArgsRegistration dAReg0("A", DerivedA::create);
//SingleArgRegistration dAReg1("A", DerivedA::create);
//TwoArgsRegistration dAReg2("A", DerivedA::create);
int main()
{
auto object0(NoArgsFactory::instance().createObject("A"));
// Not registered,
//auto object1(SingleArgFactory::instance().createObject("A","testString"));
//auto object2(TwoArgsFactory::instance().createObject("A","testString",3.142));
return 0;
}

The problem is that (before C++14) std::function<R(A...)> can be constructed from anything, not just from something which supports an R(A...) call. It should help if you add an overload of registerType which will take an R (&)(Args&&...) parameter.

The problem is that you cannot deduce the type in an overload set. Even if we simplify the example down to something that we could try to use SFINAE with, we're stuck:
#include <functional>
struct A {
static void create() { }
static void create(int ) { }
};
template <typename F,
typename = decltype(std::declval<F>()(std::declval<int>()))>
void foo(F ) { }
int main() {
foo(&A::create); // error, even in this case
}
You'd have to add explicit overloads for function pointers to handle this case, as there is an exception in the standard to allow for that:
void foo(void (*)(int)) { } // (1)
template <typename F,
typename = decltype(std::declval<F>()(std::declval<int>()))>
void foo(F ) { } // (2)
int main() {
foo(&A::create); // OK, calls (1)
}
In your specific example, that means adding two constructors:
// in Factory
using Function = std::function<std::shared_ptr<T>(Args...)>;
using FunctionPtr = std::shared_ptr<T>(*)(Args...);
// in Registration
using Function = typename Factory::Function;
using FunctionPtr = typename Factory::FunctionPtr;
NameFactoryRegistration(const std::string& type, const Function& createFunction) {
/* same as before */
}
NameFactoryRegistration(const std::string& type, FunctionPtr createFunction)
: NameFactoryRegistration(type, Function(createFunction))
{ }

Related

Forward a variadic instance method call via a pointer to member function in C++

I'm working on a class representation utility that would work in a similar way to Java's Class class. That is, a mechanism that would emulate class reflection.
#include <map>
#include <stdexcept>
#include <string>
template<typename Class>
struct class_repr {
std::map<std::string, uintptr_t> fields;
std::map<std::string, void* (Class::*)(...)> methods;
void declare_field(const std::string& name, void* pointer) {
fields[name] = reinterpret_cast<uintptr_t>(pointer);
}
template<typename R, typename ...Params>
void declare_instance_method(const std::string& name, R (Class::* pointer)(Params...)) {
methods[name] = (void* (Class::*)(...)) pointer;
}
template<typename Tp>
Tp& get_field(void* object, const std::string& name) {
if (fields.count(name) == 0) throw std::invalid_argument("Field " + name + " not declared in the class descriptor");
return *reinterpret_cast<Tp*>(uintptr_t(object) + fields.at(name));
}
template<typename R, typename ...Params>
requires std::is_same_v<R, void>
void invoke_instance_method(void* object, const std::string& name, Params&& ... params) {
if (methods.count(name) == 0) throw std::invalid_argument("Method " + name + " not declared in the class descriptor");
(reinterpret_cast<Class*>(object)->*methods.at(name))(std::forward<Params>(params)...);
}
template<typename R, typename ...Params>
requires (not std::is_same_v<R, void>)
R invoke_instance_method(void* object, const std::string& name, Params&& ... params) {
if (methods.count(name) == 0) throw std::invalid_argument("Method " + name + " not declared in the class descriptor");
return *static_cast<R*>((reinterpret_cast<Class*>(object)->*methods.at(name))(std::forward<Params>(params)...));
}
};
And below is the class I'm testing it with:
#include <iostream>
class cat {
std::string name, color;
[[nodiscard]] const std::string& get_name() {
return name;
}
[[nodiscard]] const std::string& get_color() {
return color;
}
void say(std::string&& what) {
std::cout << "[" << name << "]: " << what << std::endl;
}
void meow() {
say("meow");
}
void say_color() {
say("my fur is " + color);
}
public:
cat(std::string name, std::string color) : name(std::move(name)), color(std::move(color)) {}
static class_repr<cat> get_representation() {
class_repr<cat> descriptor;
descriptor.declare_field("name", &(static_cast<cat*>(nullptr)->name));
descriptor.declare_field("color", &(static_cast<cat*>(nullptr)->color));
descriptor.declare_instance_method("get_name", &cat::get_name);
descriptor.declare_instance_method("get_color", &cat::get_color);
descriptor.declare_instance_method("say", &cat::say);
descriptor.declare_instance_method("meow", &cat::meow);
descriptor.declare_instance_method("say_color", &cat::say_color);
return descriptor;
}
};
This code works fine:
int main() {
cat kitty("marble", "white");
class_repr cat_class = cat::get_representation();
cat_class.get_field<std::string>(&kitty, "name") = "skittle";
cat_class.get_field<std::string>(&kitty, "color") = "gray";
cat_class.invoke_instance_method<void>(&kitty, "meow");
cat_class.invoke_instance_method<void>(&kitty, "say_color");
std::cout << cat_class.invoke_instance_method<std::string>(&kitty, "get_name") << "'s color is indeed "
<< cat_class.invoke_instance_method<std::string>(&kitty, "get_color") << std::endl;
return 0;
}
But when I try to call the say function, the code doesn't compile because non-primitive type objects cannot be passed through variadic method:
cat_class.invoke_instance_method<void, std::string&&>(&kitty, "say", "purr"); // error
Is there any way around making this work as intended (so that it calls an equivalent of kitty.say("purr"))?
You can create a class representing any member function using type erasure (modified from this SO answer). No void*, no C-stype ellipsis ....
#include <memory>
#include <any>
#include <vector>
#include <functional>
class MemberFunction
{
public:
template <typename R, typename C, typename... Args>
MemberFunction(R (C::* memfunptr)(Args...))
: type_erased_function{
std::make_shared<Function<R, C, Args...>>(memfunptr)
}
{}
template <typename R, typename C, typename... Args>
R invoke(C* obj, Args&&... args){
auto ret = type_erased_function->invoke(
std::any(obj),
std::vector<std::any>({std::forward<Args>(args)...})
);
if constexpr (!std::is_void_v<R>){
return std::any_cast<R>(ret);
}
}
private:
struct Concept {
virtual ~Concept(){}
virtual std::any invoke(std::any obj, std::vector<std::any> const& args) = 0;
};
template <typename R, typename C, typename... Args>
class Function : public Concept
{
public:
Function(R (C::* memfunptr)(Args...)) : func{memfunptr} {}
std::any invoke(std::any obj, std::vector<std::any> const& args) override final
{
return invoke_impl(
obj,
args,
std::make_index_sequence<sizeof...(Args)>()
);
}
private:
template <size_t I>
using Arg = std::tuple_element_t<I, std::tuple<Args...>>;
template <size_t... I>
std::any invoke_impl(std::any obj, std::vector<std::any> const& args, std::index_sequence<I...>)
{
auto invoke = [&]{
return std::invoke(func, std::any_cast<C*>(obj), std::any_cast<std::remove_reference_t<Arg<I>>>(args[I])...);
};
if constexpr (std::is_void_v<R>){
invoke();
return std::any();
}
else {
return invoke();
}
}
R (C::* func)(Args...);
};
std::shared_ptr<Concept> type_erased_function;
};
You store a std::map<std::string, MemberFunction> in your class_repr and change your declare_instance_method and invoke_instance_method like so:
template<typename R, typename ...Params>
void declare_instance_method(const std::string& name, R (Class::* pointer)(Params...)) {
methods.insert({name, MemberFunction(pointer)});
}
template<typename R, typename ...Params>
requires std::is_same_v<R, void>
void invoke_instance_method(Class* object, const std::string& name, Params&& ... params) {
if (methods.count(name) == 0) throw std::invalid_argument("Method " + name + " not declared in the class descriptor");
methods.at(name).invoke<void>(object, std::forward<Params>(params)...);
}
template<typename R, typename ...Params>
requires (not std::is_same_v<R, void>)
R invoke_instance_method(Class* object, const std::string& name, Params&& ... params) {
if (methods.count(name) == 0) throw std::invalid_argument("Method " + name + " not declared in the class descriptor");
return methods.at(name).invoke<R>(object, std::forward<Params>(params)...);
}
Live Demo
Note that this is a prototype. To make this generally applicable you still need to invest quite a bit of work: You have to consider const member functions and const arguments, member functions mutating inputs or returning references etc. Also note, that std::any stores by value, so you might create some unnecessary copies of the function arguments.

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()>.

Call functions with multiple args

Can I store in a container a list of member functions and then call them later, if they have different number of args.
I feel I'm just missing something small but this is how far I've got.
template<typename T>
class RPCServer
{
public:
RPCServer(const std::string host, const int port) {}
// Store the method pointers
template<typename F>
void register_method(const T discriminant, F func) {
m_callbacks.emplace_back(discriminant,func);
}
template<typename... Args>
void run(T subject, Args... args) {
auto func = std::find(std::begin(m_callbacks), std::end(m_callbacks), subject);
if (func != std::end(m_callbacks)) {
auto res = std::get<1>(*func)(args...); // This doesn't compile
}
}
~RPCServer() = default;
private:
// Store
std::vector<std::tuple<T, boost::any>> m_callbacks;
};
class Impl
{
public:
// RPC methods
void send_data(std::string data) {}
int get_details(int input) { return 0; }
};
Set up here
using namespace std::placeholders;
Impl impl;
RPCServer<std::string> server("localhost",1234);
server.register_method("foo", std::bind(&Impl::send_data, impl, _1));
server.register_method("bar", std::bind(&Impl::get_details, impl, _1));
server.run("foo", "blah"s); // This should call send_data with 'blah' as a arg
auto result = server.run("bar", 1); // Call get_details passing in 1
How do I store/retrieve a set of member functions type safely.
What about creating an adaptor template?
A proof-of-concept code:
#include <iostream>
#include <functional>
template<typename T0, typename... TS> struct FunCaller {
template<class F> FunCaller(F &&f): f(f) {}
template<typename... More> T0 operator()(TS &&... as, More &&...) {
return f(as...);
}
private:
std::function<T0(TS...)> f;
};
template<typename T0, typename... TS> inline FunCaller<T0, TS...> funCaller(T0(&&f)(TS...)) { return FunCaller<T0, TS...>(f); }
std::ostream &printSome(std::string const &s1, std::string const &s2) { return std::cout << s1 << ", " << s2 << std::endl; }
int main() {
auto omg = funCaller(printSome);
omg("Hello", "world!", "This", "is", "cocaine", "speaking");
}

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.

scripting library and function templating

Context
I'm currently working on my own library for loading custom script inside c++ applications.
Here's some sample code for explaining what it's doing:
script part:
test.ctv
script
{
object player = access("player");
player.posX = 1 + 2;
access("map").load("map.txt");
}
C++ part:
test.cpp
class Player : public Loadable{
private:
friend class cTVScript::scriptExecutor;
primaryLoadable<int> posX;
stringLoadable name;
public:
Player() : Loadable(&posX, "posX", &name, "name");
}
class Map : public Loadable{
private:
friend class cTVScript::scriptExecutor;
std::string mapName;
public:
void load(std::string map) {
mapName = map;
}
Map() : Loadable(&load, "load") {}
}
int main() {
Player *p = new Player();
Map *m = new Map();
cTVScript::LoadScript("test.ctv");
cTVScript::AddObject(p, "player");
cTVScript::AddObject(m, "map");
std::cout << player.posX.get() << std::endl; // for example purpose we just assume that posX are public
std::cout << player.mapName.get() << std::endl; // same for mapName
}
Problem
Variable accessing and using by the cTVScript::scriptExecutor is quite simple,
but my main problem is elsewhere:
How, in c++, can I save and call method/functions with differents prototype?
Some trick with the compiler could make it easier? (like knowing the type and numbers of arguments?)
Current Work-Around
Make the user define a sub-fonction like AccessibleLoad:
class Map{
[...]
public:
void load(std::string map) {
mapName = map;
}
static void AccessibleLoad(cTVScript::CallingPack& pack) {
/* arguments */
std::string map;
pack.loadArguments(map); // here the user ask for each arguments
/*calling object */
Map* _this;
pack.loadCallingObject(_this); // and here he ask for the calling object
_this->load(map);
}
Map() : Loadable(&AccessibleLoad, "load") {}
}
So!
Is there a trick or some way i could make it more easy for using functions/methodes in my library? (like constructing these functions with the compiler? (don't think so but better to ask))
Edit
There's news! I got my own answer, and i'll post it (but it's a bit long)
(by the way, english is not my native language so if i made an error, said me so, i'll edit)
doing the C++ -> your script call. This is c++11 by the way
You will need some form of packer that can take a type and add it in.
class SomeClassYouCanCallAScriptFunction {
// the order of these templates matter or else
// the one bellow will not be able to find the one higher
template<class T, class... Args>
callFunction(string name){
// run your code to call your scripted function
// arguments has the arguments array
}
template<class T, class... Args>
callFunction(string name, T var){
// last one
// either this
arguments.pack<T>(var);
// or this
arguments.pack(to_string(var));
// or the like
// now do the next one
callFunction(name);
}
template<class T, class... Args>
callFunction(string name, T var, Args... args){
// either this
arguments.pack<T>(var);
// or this
arguments.pack(to_string(var));
// or the like
// now do the next one
callFunction(name, args...);
}
}
someClass.callFunction("scriptFunc", "ya", 42, someVectMaybe);
The otherway around the best you can do is provide a arguments variable and let the user get a argument passed in like arguments.get<T>(index)
Detail On the Context
For more Understanding (and if one want to re-use my solution), I'l detail the context of what i'm doing:
There's a top level (accessible only by the cTVScript::Executor)
And a low level (Visible (or almost) by the user)
top Level (or Non-typed part)
/*
* Non-Typed Part
*/
class Loadable{
public:
virtual std::string getAsString() { return ""; }
};
struct parametersPack{
public:
Loadable* returnValue;
std::vector<Loadable*> arguments;
};
Low Level (or typed-part)
class StringLoadable : public Loadable{
private:
std::string value;
public:
StringLoadable(std::string _s) : value(_s) {}
virtual std::string getAsString() { return value; }
virtual std::string get() { return value; }
virtual std::string& getRef() { return value; }
};
template<typename type>
class primaryLoadable : public Loadable{
private:
type value;
public:
primaryLoadable(type _v) : value(_v) {}
virtual std::string getAsString() { return std::to_string(value); }
type get() {return value;}
type& getRef() {return value;}
};
Save Function with non-matching prototype
A Parent Function Class (for stocking them):
class functionLoadable : public Loadable{
public:
virtual void call(parametersPack& pack) = 0;
};
And Sub-Function (one with void return and other with typed-Return)
/*
* Static Loadable Function
*/
template <typename Return, typename... Arguments>
class StaticLoadableFunction : public functionLoadable{
private:
Return (*fn)(Arguments...);
public:
Return calling(Arguments... args) {
Return value = fn(args...);
return (value);
}
virtual void call(parametersPack& pack) {
Unpacker::applyFunc(pack.arguments, fn);
}
StaticLoadableFunction(Return (*_fn)(Arguments...)) : fn(_fn){}
};
template <typename... Arguments>
class StaticLoadableFunction<void, Arguments...> : public functionLoadable{
private:
void (*fn)(Arguments...);
public:
void calling(Arguments... args) {
fn(args...);
}
virtual void call(parametersPack& pack) {
Unpacker::applyFunc(pack.arguments, fn);
}
StaticLoadableFunction(void (*_fn)(Arguments...)) : fn(_fn){}
};
Now the work of the unpacker
First I need to unpack my arguments from my std::vector
/*
* Unpacking all arguments
*/
template<unsigned int N>
struct helper;
template<unsigned int N>
struct helper{
template <typename ReturnType, typename... Arguments, typename ...final>
static ReturnType applyFunc(std::vector<Loadable*> parameters, ReturnType (*fn)(Arguments...), final&&... args) {
return (helper<N - 1>::applyFunc
(parameters, fn,
convertLoadableTo< typename parametersType<N - 1, Arguments...>::type >
::transform(parameters[N-1]),
args...));
}
};
template<>
struct helper<0>{
template <typename ReturnType, typename ...Arguments, typename ...final>
static ReturnType applyFunc(std::vector<Loadable*> parameters, ReturnType (*fn)(Arguments...), final&&... args) {
return (fn( args... ));
}
};
template <typename ReturnType, typename ...Arguments>
ReturnType applyFunc(std::vector<Loadable*> args, ReturnType (*fn)(Arguments...)) {
return (helper<sizeof...(Arguments)>::applyFunc(args, fn));
}
I know want to know wich type are in each recursion:
/*
* Getting Parameters type N in variadic Templates
*/
template <int N, typename... T>
struct parametersType;
template <typename T0, typename... T>
struct parametersType<0, T0, T...> {
typedef T0 type;
};
template <int N, typename T0, typename... T>
struct parametersType<N, T0, T...> {
typedef typename parametersType<N-1, T...>::type type;
};
And then downcast my Loadable* object into primaryLoadable<> or StringLoadable
/*
* Treat For Each Type
*/
template <typename arg>
struct convertLoadableTo;
template <typename arg>
struct convertLoadableTo{ // int, double...etc
static arg transform(Loadable* l) {
primaryLoadable<arg>* _l =
dynamic_cast< primaryLoadable<arg>* >(l);
if (!_l)
throw;
return (_l->get());
}
};
template <typename arg>
struct convertLoadableTo<arg&>{ // int&, double&...etc
static arg& transform(Loadable* l) {
primaryLoadable<arg>* _l =
dynamic_cast< primaryLoadable<arg>* >(l);
if (!_l)
throw;
return (_l->getRef());
}
};
template <>
struct convertLoadableTo<std::string>{ // int&, double&...etc
static std::string transform(Loadable* l) {
StringLoadable* _l =
dynamic_cast< StringLoadable* >(l);
if (!_l)
throw;
return (_l->get());
}
};
template <>
struct convertLoadableTo<std::string&>{ // int&, double&...etc
static std::string& transform(Loadable* l) {
StringLoadable* _l =
dynamic_cast< StringLoadable* >(l);
if (!_l)
throw;
return (_l->getRef());
}
};
That's the end!
If you wanna know more detail please mp me!