来自空隙的多晶铸造*

Aye*_*ien 3 c++

有关详细信息,请参阅下面的代码,但基本方案如下.我有一个容器(会话),我可以放置对象和从中取出.

相近:

 std::shared_ptr<Tiger> t = ...;
 session.store("tigers/1", t);

 std::shared_ptr<Tiger> t2 = session.load<Tiger>("tigers/1");
Run Code Online (Sandbox Code Playgroud)

两个函数定义为:

 class Session { 
      template<class T> 
      void store(std::string id, std::shared_ptr<T> instance);

      template<class T> 
      std::shared_ptr<T> load(std::string id);
 }
Run Code Online (Sandbox Code Playgroud)

请注意,会话可以存储异构类型,但在时间store和地点load我静态地知道变量的类型是什么.

我的问题是我遇到了一个用户想要Tiger进入会话但是检查出基本类型的情况.例如:

  session.load<Animal>("tigers/1");
Run Code Online (Sandbox Code Playgroud)

现在,我有效地将数据存储在void*会话内部并用于reinterpret_cast将它们恢复到用户提供的类型.只要一切都是微不足道的,这就行......但是当我们遇到稍微复杂的情况时,我们会遇到问题.

以下是演示我的问题的完整代码:

struct Animal
{
    virtual void Pet() const = 0;
};

struct IJumpable
{
    virtual void Jump() const = 0;
};

struct Tiger : Animal, IJumpable
{
    void Pet() const override
    {
        std::cout << "Pet\n";
    }

    void Jump() const override
    {
        std::cout << "Jump\n";
    }
};

int main()
{
    auto cat = std::make_shared<Tiger>();

    // how the data is stored inside the session
    auto any_ptr = std::static_pointer_cast<void>(cat);
    // how we get the data out of the session
    auto namable = std::static_pointer_cast<IJumpable>(any_ptr);

    namable->Jump();
    std::cout << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

如果您运行此代码,您将看到它运行,但它不是调用Jump,而是调用Pet.我知道这是因为使用了错误的虚方法表,因为我实际上是在调用reinterpret_cast`void*.

我的问题是,是否有一种很好的方法来处理C++中的这种情况.我环顾四周,没有看到任何符合我需要的东西.

我发现的异构容器的所有内容总是假设一个共享的基类,我没有也不想要.这可能吗?

YSC*_*YSC 5

您可以让用户为您提供正确的投射热刺:

class Session { 
      template<class T> 
      void store(std::string id, std::shared_ptr<T> instance);

      template<class T> 
      std::shared_ptr<T> load(std::string id);

      template<class Stored, class Retrieved>
      std::shared_ptr<Retrieved> load_as(std::string id) {
          auto stored = load<Stored>(id);
          return std::static_pointer_cast<Retrieved>(stored);
      }
 }
Run Code Online (Sandbox Code Playgroud)

这会在调用者站点上使用混乱,但信息必须来自某个地方:

auto shere_khan = make_shared<Tiger>();
session.store("tigers/1", shere_khan);
auto bagheera = session.load_as<Tiger, IJumpable>("tigers/1");
Run Code Online (Sandbox Code Playgroud)


Aja*_*jai 3

解决方案由我的兄弟提供,他恰好是一位没有 stackoverflow 的 C++ 专家:)

这是一个void_ptr实现,它支持使用异常处理来发现类型的多态转换。性能应该接近于dynamic_cast。您应该能够使用std::type_index和缓存偏移量来优化上述内容。

#include <stdio.h>

class void_ptr {
  void* obj;
  void (*discover_type)(void*);

  template<typename T>
  static void throw_typed_object(void* obj)
  {
    T* t = static_cast<T*>(obj);
    throw t;
  }
public:

  void_ptr() : obj(0) {}

  template<typename T>
  void_ptr(T* t) : obj(t), discover_type(throw_typed_object<T>)
  {
  }

  template<typename T>
  T* cast() const
  {
    try {
      discover_type(obj);
    } catch(T* t) {
      return t;
    } catch(...) {
    }
    return 0;
  }
};

struct Animal {
  virtual ~Animal() {}
  virtual const char* name() { return "Animal"; }
};

struct Speaker {
  virtual ~Speaker() {}
  virtual const char* speak() { return "hello"; }
};

struct Lion : public Animal, public Speaker {
  virtual const char* name() { return "Lion"; }
  virtual const char* speak() { return "Roar"; }
};


int main()
{
  void_ptr ptr(new Lion());


  Animal* a = ptr.cast<Animal>();
  Speaker* s = ptr.cast<Speaker>();

  printf("%s\n", a->name());
  printf("%s\n", s->speak());
}
Run Code Online (Sandbox Code Playgroud)