I'm trying to implement a generic input stream of objects. That is, an interface or a lightweight proxy to an implementation. Details of implementation are unknown, i.e. a user of my library can write its own stream of, say, protobuf messages, pass it to my library and get back, say, stream of strings or any other stream. I'd like to keep the interface of stream generic so that users can write their own transformations and build transformation pipelines.
The interface of a stream should look like this:
template <typename T>
class Stream {
public:
T* input();
}
On each call, input()
shall return a next object in the stream or a null pointer, if the stream is empty.
The problem is that I'd like Stream<T>
to be convertible to Stream<U>
if T*
is convertible to U*
.
My unsuccessful attempt was to use pointer to implementation like this:
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()); }
}
The constructor from StreamImpl<T>
guarantees that the void*
returned from input_raw()
was acquired by casting T
to void*
, therefore static_cast<T*>
is safe.
However, if I perform any conversion, this statement will not be true. That is, building Stream<T>
from StreamImpl<U>
is unsafe even if U*
is convertible to T*
.
So my question is, how do I handle this problem?
I see the next possibilities:
store a converter (e.g. std::function<T*(void*)>
) in the stream and update it on every cast. This seems unnecessarily expensive;
store the result of static_cast<U*>((T*)0)
and add this result to the pointer obtained from input_raw()
. This seems unnecessarily dangerous;
add the second template parameter OrigT
and store StreamImpl<OrigT>*
instead of storing StreamImplBase*
. This will limit possible applications of the class, which I'd like to avoid;
using dynamic_cast
is not an option because one can't dynamic_cast
from void*
.
Are there any other possibilities? How do others implement something like this?
Here's a usecase. Suppose we have a protobuf message X
. I'd like this to work:
Stream<X> stream = ...;
Stream<google::protobuf::Message> raw_stream = stream;
Again, I don't know how Stream<X>
is implemented. All I know is that it contains a shared pointer to some implementation which generates messages.
This:
template <typename T>
class Stream {
public:
T* input();
};
is an object with one operation, which takes 0 arguments and returns a T*
.
So is this:
std::function<T*()>
admittedly you invoke it like stream()
instead of stream.input()
.
With this second solution, if U
is a base of T
, then you can convert the above to std::function<U*()>
. Which solves your problem.
Personally I don't think typing .input
between the name of your stream and ()
is worth a lot of work.
Type erasure that someone else has already done is best type erasure.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With