Reputation: 215
This may sound stupid, but C++ and C++11 has surprised me before in terms of the magic it can achieve. Perhaps this is too far, but I prefer confirming my fears rather than assuming them.
Is it possible in any way to cast a std::future or std::future_shared object?
I find it usually helps if I describe the concrete problem I am having. I am essentially loading some audio and video asynchronously and although I've started using std::async which I find really useful, I haven't used futures before until now. It's essentially born out of me learning that futures seem to handle exceptions fairly well and I want to make my async loading a bit more robust. My crummy program will occasionally run out of memory but that won't occur to the program until the async call has been launched. Solving the memory issue is another issue entirely and not a viable solution currently.
Anyway - I have two separate objects that handle the loading of audio (AudioLibrary) and video (VideoLibrary), but since they share a number of commonalities they both inherit from the same base object (BaseLibrary).
The audio and video each of these respective libraries return come in their own containers for audio (AudioTrack) and video (VideoTrack), which also inherit from a common object (BaseTrack).
I'm sure you can see where this is going. I'd like some general exception handling to occur in the BaseLibrary which will have some virtual functions like loadMedia. These will be overwritten by the derived libraries. Thus the trouble begins. I read that pointer objects (like unique_ptr or shared_ptr) cannot be covariant and so just creating a virtual method doesn't quite solve it.
However, I was hoping via virtual functions I could still somehow achieve what I wanted.
Something along the lines of BaseLibrary implementing the following:
std::shared_future<BaseTrack> BaseLibrary::loadMedia()
std::shared_future<BaseTrack> BaseLibrary::loadMediaHelper()
and then AudioLibrary would implement
std::shared_future<AudioTrack> AudioLibrary::loadAudio()
where this function makes use of the functions in the BaseLibrary yet returns its own specific type of AudioTrack, rather than a BaseTrack.
Is this at all possible?
Update 1:
Thanks to the comment and answer, I see how it's possible to achieve what I want, but I have a few more unresolved questions. I think it'll be much easier to address those by just being very explicit. I'm actually working with shared_ptrs since a number of objects are making use of the loaded audio and video, so I have the following type defs:
typedef std::shared_ptr<BaseTrack> BaseTrackPtr;
typedef std::shared_ptr<AudioTrack> AudioTrackPtr;
AudioTrack inherits from BaseTrack of course. Following the given advice I have a compile-able (abbreviated) code structure which is as follows for the BaseLibrary:
class BaseLibrary {
virtual std::shared_future<BaseTrackPtr> loadMedia();
virtual std::shared_future<BaseTrackPtr> loadMediaHelper() = 0;
}
std::shared_future<BaseTrackPtr> BaseLibrary::loadMedia()
{
// Place code to catch exceptions coming through the std::future here.
// Call the loadMediaHelper via async - loadMediaHelper is overwritten in the inherited libraries.
}
And the AudioLibrary:
class AudioLibrary : public BaseLibrary {
public:
virtual std::shared_future<AudioTrackPtr> loadAudio();
protected:
virtual std::shared_future<BaseTrackPtr> loadMediaHelper();
}
std::shared_future<AudioTrackPtr> AudioLibrary::loadAudio()
{
std::shared_future<BaseTrackPtr> futureBaseTrackPtr = loadMedia();
return std::async( std::launch::deferred, [=]() {
return AudioTrackPtr( std::static_pointer_cast<AudioTrack>( futureBaseTrackPtr.get() ) );
} );
}
std::shared_future<BaseTrackPtr> AudioLibrary::loadMediaHelper()
{
// Place specific audio loading code here
}
This structure allows me to catch any video/audio loading exceptions in one place, and also return the proper Audio/Video Object rather than a base object that needs to be recast.
My two current questions are as follows:
Upvotes: 0
Views: 1726
Reputation: 217085
So you have something like:
class BaseLibrary
{
public:
virtual ~BaseLibrary() {}
virtual std::shared_future<std::unique_ptr<BaseTrack>> loadMedia() = 0;
};
class AudioLibrary : public BaseLibrary
{
public:
std::shared_future<AudioTrack> loadAudio();
std::shared_future<std::unique_ptr<BaseTrack>> loadMedia() override;
};
So you may implement loadMedia()
like that:
std::shared_future<std::unique_ptr<BaseTrack>> AudioLibrary::loadMedia()
{
auto futureAudioTrack = loadAudio();
return std::async(std::launch::deferred,
[=]{
std::unique_ptr<BaseTrack> res =
make_unique<AudioTrack>(futureAudioTrack.get());
return res;
});
}
Upvotes: 2