实现可以<T>转换为 Stream 的 Stream,<U>其中 U 是 T 的基数
Implementing Stream<T> which can be converted to Stream<U> where U is base of T
我正在尝试实现对象的通用输入流。 即,实现的接口或轻量级代理。实现的细节是未知的,即我的库的用户可以编写自己的 protobuf 消息流,将其传递给我的库并返回,比如字符串流或任何其他流。我想保留流泛型的接口,以便用户可以编写自己的转换并构建转换管道。
流的接口应如下所示:
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>
的构造函数保证从input_raw()
返回的void*
是通过向void*
T
转换获得的,因此static_cast<T*>
是安全的。
但是,如果我执行任何转换,此语句将不正确。也就是说,即使U*
可转换为T*
,从StreamImpl<U>
构建Stream<T>
也是不安全的。
所以我的问题是,我如何处理这个问题?
我看到了下一个可能性:
存储转换器(例如
std::function<T*(void*)>
),并在每次投射时更新它。这似乎不必要地昂贵;存储
static_cast<U*>((T*)0)
的结果,并将此结果添加到从input_raw()
获取的指针中。这似乎不必要地危险;添加第二个模板参数
OrigT
并存储StreamImpl<OrigT>*
而不是存储StreamImplBase*
。这将限制该类的可能应用,我想避免;使用
dynamic_cast
不是一种选择,因为无法从void*
dynamic_cast
。
还有其他可能性吗?其他人如何实现这样的事情?
这是一个用例。假设我们有一个 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++功能,当两个类在同一函数中未知时,允许从派生类转换为基类:异常。当然,这是一种丑陋、丑陋的虐待,但它有效:
#include <type_traits>
#include <stdexcept>
class StreamImplBase {
public:
virtual void toss_input() = 0;
};
template <typename T>
class StreamImpl : public StreamImplBase {
public:
virtual T* input() = 0;
void toss_input() override
{ throw input(); }
};
template <typename T>
class Stream {
StreamImplBase* impl;
public:
template <typename U,
std::enable_if_t<std::is_convertible<U*, T*>::value>* = nullptr>
explicit Stream(StreamImpl<U>* impl) : impl(impl) {}
template <typename U,
std::enable_if_t<std::is_convertible<U*, T*>::value>* = nullptr>
Stream(const Stream<U>& str) : impl(str.impl) {}
T* input() const
{
try {
impl->toss_input();
} catch (T* ptr) {
return ptr;
}
throw std::logic_error("Stream logic is broken?");
}
};
查看在大肠杆菌上使用的完整示例。这可以通过使用std::shared_ptr<StreamImpl<U>>
和std::shared_ptr<StreamImplBase>
来改进。
- EASTL矢量<向量<int>>连续的
- 为什么stream::忽略未按预期工作
- OpenCV RTP-Stream with FFMPEG
- 确切地说,如何解释 std::getline(stream, string) 函数在C++中填充的字符串
- 在 Stream C++ 文本之前有一个额外的换行符
- 在 c++ 中,发生故障后是否需要 stream.clear()?
- 测试"stream >> char"有什么作用?
- 为什么Boost Variant使用模板构造函数而不是Boost::beast::websocket::stream的移
- cv::cuda::Stream选择线程默认流
- 如何从 boost::asio::ssl::stream<boost::asio::ip::tcp::socket> 获取本机套接字文件描述器?
- 什么是"Reading unbounded stream from standard input (Memory Management)"的例子
- Boost::Beast Websocket Bidirection Stream (C++)
- 实现可以<T>转换为 Stream 的 Stream,<U>其中 U 是 T 的基数
- C - 创建矢量&lt; vector&lt; double&gt;&gt;矩阵具有分配而不是inizializ
- C 字符串比较“祝您好运”&gt;“再见”
- AWS CPP TransferManager vs GetObjectRequest Stream to File f
- 为什么将此对向量&lt; map&lt; int,int&gt;&gt;中的地图进行更新.失败
- '&'在"std::vector<byte>& stream;"中代表什么
- 由mpglib输出的"hip: Can't rewind stream by 74 bits"到底意味着什么?
- Using boost::asio::async_wait_until with boost::asio::stream