我正在尝试实现对象的通用输入流。也就是说,是实现的接口(interface)或轻量级代理。实现的细节是未知的,即我的库的用户可以编写自己的protobuf消息流,将其传递到我的库,然后返回字符串流或任何其他流。我想保持流通用的接口(interface),以便用户可以编写自己的转换并构建转换管道。

流的接口(interface)应如下所示:

template <typename T>
class Stream {
public:
    T* input();
}

在每次调用中,如果流为空,input()将返回流中的下一个对象或空指针。

问题是,如果Stream<T>可转换为Stream<U>,我希望T*可转换为U*

我的失败尝试是使用指向实现的指针,如下所示:
class StreamImplBase {
public:
    virtual void* input_raw() = 0;
}

template <typename T>
class StreamImpl: public StreamImplBase {
public:
    void* input_raw() final { return input(); }
    virtual T* input() = 0;
}

template <typename T>
class Stream {
    StreamImplBase* impl;
public:
    Stream(StreamImpl<T>* impl): impl(impl) {}
    T* input() { return static_cast<T*>(impl->input_raw()); }
}
StreamImpl<T>的构造函数保证从void*返回的input_raw()是通过将T转换为void*而获得的,因此static_cast<T*>是安全的。

但是,如果我执行任何转换,则此语句将不成立。也就是说,即使Stream<T>可转换为StreamImpl<U>,从U*构造T*也是不安全的。

所以我的问题是,我该如何处理这个问题?

我看到了下一个可能性:
  • 在流中存储一个转换器(例如std::function<T*(void*)>),并在每次转换时对其进行更新。这似乎不必要地昂贵;
  • 存储static_cast<U*>((T*)0)的结果,并将此结果添加到从input_raw()获得的指针中。这似乎是不必要的危险。
  • 添加第二个模板参数OrigT并存储StreamImpl<OrigT>*而不是存储StreamImplBase*。这将限制该类的可能应用程序,我想避免这种情况;
  • 不能使用dynamic_cast进行
  • ,因为不能从dynamic_cast进行void*

  • 还有其他可能性吗?别人如何实现这样的目标?

    这是一个用例。假设我们有一个protobuf消息X。我想要这个工作:
    Stream<X> stream = ...;
    Stream<google::protobuf::Message> raw_stream = stream;
    

    同样,我不知道Stream<X>是如何实现的。我所知道的是,它包含指向某些生成消息的实现的共享指针。

    最佳答案

    这:

    template <typename T>
    class Stream {
    public:
      T* input();
    };
    

    是具有一个操作的对象,该操作接受0个参数并返回T*

    这是这样的:
    std::function<T*()>
    

    诚然,您像stream()而不是stream.input()一样调用它。

    使用第二种解决方案,如果UT的基础,则可以将以上内容转换为std::function<U*()>。这可以解决您的问题。

    我个人不认为在流名称和.input之间输入()值得做很多工作。

    别人已经做过的类型擦除是最好的类型擦除。

    关于c++ - 实现Stream <T>,可以将其转换为Stream <U>,其中U是T的基数,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/50071350/

    10-11 22:39
    查看更多