Skip to main content
added 10 characters in body
Source Link
user128454
user128454

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

That's really neat! I really like the idea of provoking the instantiation of the polymorphic dispatcher by passing it as a template template parameter.

That being said, I agrre 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_;
      }
  };

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

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 agree 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...);
  }

  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;
}
added 576 characters in body
Source Link
user128454
user128454

You are also coding way too defensively for my taste. all of these can_static_cast and can_dynamic_cast are all redundant with the checks that the compiler performs when assigning values to ptr.

If you want to provide users with clearer errors when they mess up, then a simple std::is_base_of<> will suffice.

similarly, the following is perfectly fine for the users to do:

void call_foo(BaseT *base) override
{
    foo(static_cast<DerivedT*>(base));
}

So that derived<>() member function seems like extra api surface for no reason.

You are also coding way too defensively for my taste. all of these can_static_cast and can_dynamic_cast are all redundant with the checks that the compiler performs when assigning values to ptr.

If you want to provide users with clearer errors when they mess up, then a simple std::is_base_of<> will suffice.

similarly, the following is perfectly fine for the users to do:

void call_foo(BaseT *base) override
{
    foo(static_cast<DerivedT*>(base));
}

So that derived<>() member function seems like extra api surface for no reason.

added 86 characters in body
Source Link
user128454
user128454

That being said, I thinkagrre with you that the dispatching syntax can be improved quite a bit.

That being said, I think the dispatching syntax can be improved quite a bit.

That being said, I agrre with you that the dispatching syntax can be improved quite a bit.

added 86 characters in body
Source Link
user128454
user128454
Loading
added 86 characters in body
Source Link
user128454
user128454
Loading
added 2 characters in body
Source Link
user128454
user128454
Loading
Source Link
user128454
user128454
Loading