我正在尝试实现一个通用的对象输入流。即,实现的接口(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()
.
使用第二种解决方案,如果 U
是 T
的基础, 然后你可以将上面的转换为 std::function<U*()>
.这可以解决您的问题。
我个人不认为输入 .input
在您的流名称和 ()
之间值得做很多工作。
其他人已经完成的类型删除是最好的类型删除。
关于c++ - 实现可以转换为 Stream<U> 的 Stream<T>,其中 U 是 T 的基数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50071350/