That's really neat! I really like the idea of provoking the on-demand instantiation of the polymorphic dispatcher by passing it as a template template parameter.
That being said, I agrreagree with you that the dispatching syntax can be improved quite a bit.
#include <memory>
#include <iostream>
template <typename T, template<typename> typename DispatcherT>
class DynStorage {
using base_dispatcher = typename DispatcherT<T>::base_t;
struct StorageBase {
public:
virtual ~StorageBase() {}
virtual T* getData() = 0;
virtual base_dispatcher* getDispatcher() = 0;
};
template<typename U>
struct StorageImpl : public StorageBase {
U data_;
static DispatcherT<U> dispatcher_;
public:
base_dispatcher* getDispatcher() override {
return &dispatcher_;
}
T* getData() override {
return &data_;
}
};
std::unique_ptr<StorageBase> storage_;
public:
DynStorage(std::unique_ptr<StorageBase> s) : storage_(std::move(s)) {}
template<typename R, typename... argsT>
R dispatch(R(base_dispatcher::*func)(T const&, argsT...), argsT... args) const {
return (storage_->getDispatcher()->*func)(*storage_->getData(), args...);
}
std::unique_ptr<StorageBase> storage_;
template<typename U=T>
static DynStorage make() {return DynStorage(std::make_unique<StorageImpl<U>>()); }
};
template<typename T, template<typename> typename DispatcherT, typename CB_T, typename... argsT>
decltype(auto) visit(DynStorage<T, DispatcherT> const& x, CB_T cb, argsT... args ) {
return x.dispatch(cb, args...);
}
struct A {};
struct B : public A {};
struct MyFuncsBase {
// no need for a virtual destructor
virtual void foo(A const& val) = 0 ;
virtual int bar(A const& val, float v ) = 0;
};
template<typename T>
struct MyFuncs : public MyFuncsBase {
using base_t = MyFuncsBase;
void foo(A const& val) override {
std::cout << typeid(T).name() << std::endl;
}
int bar(A const& val, float v ) override {
return 0;
}
};
int main()
{
auto x = DynStorage<A, MyFuncs>::make();
auto y = DynStorage<A, MyFuncs>::make<B>();
visit(x, &MyFuncsBase::foo);
visit(y, &MyFuncsBase::foo);
// Ooooh, arguments and return type support too!
int res = visit(y, &MyFuncsBase::bar, 12.0f);
return 0;
}