在 c++11 中转换 std::future 或 std::shared_future

Posted

技术标签:

【中文标题】在 c++11 中转换 std::future 或 std::shared_future【英文标题】:Casting std::future or std::shared_future in c++11 【发布时间】:2014-05-29 12:30:41 【问题描述】:

这听起来可能很愚蠢,但 C++ 和 C++11 之前的神奇之处让我感到惊讶。也许这太过分了,但我更愿意确认我的恐惧而不是假设它们。

是否可以以任何方式强制转换 std::future 或 std::future_shared 对象?

我发现如果我描述我遇到的具体问题通常会有所帮助。我实际上是在异步加载一些音频和视频,虽然我已经开始使用我觉得非常有用的 std::async,但直到现在我还没有使用过 futures。它本质上源于我了解到 futures 似乎可以很好地处理异常,并且我想让我的异步加载更加健壮。我的糟糕程序偶尔会耗尽内存,但在启动异步调用之前,程序不会发生这种情况。解决内存问题完全是另一个问题,目前还不是可行的解决方案。

无论如何 - 我有两个单独的对象来处理音频 (AudioLibrary) 和视频 (VideoLibrary) 的加载,但由于它们有许多共同点,它们都继承自同一个基础对象 (BaseLibrary)。

每个库返回的音频和视频都来自各自的音频 (AudioTrack) 和视频 (VideoTrack) 容器,它们也继承自一个公共对象 (BaseTrack)。

我相信你可以看到这是怎么回事。我希望在 BaseLibrary 中进行一些一般的异常处理,它将具有一些虚拟功能,如 loadMedia。这些将被派生库覆盖。于是麻烦就开始了。我读到指针对象(如 unique_ptr 或 shared_ptr)不能是协变的,因此仅创建一个虚拟方法并不能完全解决它。

但是,我希望通过虚函数我仍然能够以某种方式实现我想要的。

类似于 BaseLibrary 的东西实现了以下内容:

std::shared_future<BaseTrack> BaseLibrary::loadMedia()
std::shared_future<BaseTrack> BaseLibrary::loadMediaHelper()

然后 AudioLibrary 将实现

std::shared_future<AudioTrack> AudioLibrary::loadAudio()

此函数使用 BaseLibrary 中的函数但返回其自身特定类型的 AudioTrack,而不是 BaseTrack。

这可能吗?

更新 1:

感谢评论和回答,我知道如何实现我想要的,但我还有一些未解决的问题。我认为通过非常明确来解决这些问题会容易得多。我实际上正在使用 shared_ptrs,因为许多对象正在使用加载的音频和视频,所以我有以下类型 defs:

typedef std::shared_ptr<BaseTrack> BaseTrackPtr;
typedef std::shared_ptr<AudioTrack> AudioTrackPtr;

AudioTrack 当然继承自 BaseTrack。按照给定的建议,我有一个可编译(缩写)的代码结构,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.

还有音频库:

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

这种结构允许我在一个地方捕获任何视频/音频加载异常,并返回正确的音频/视频对象,而不是需要重铸的基础对象。

我目前的两个问题如下:

最好让 BaseLibrary 中 loadMedia 中的异步调用为 std::launch::deferred,然后让 loadAudio(或 loadVideo)中的异步调用为 std::launch::async 吗?我本质上希望加载立即开始,但还不如等到外部异步调用执行......?这有意义吗? 最后,这丑得可怕吗?我的一部分感觉就像我正确地利用了 C++11 所提供的所有优点,shared_ptr's,futures 等等。但我对期货也很陌生,所以......我不知道将共享指针放在共享未来是否......很奇怪?

【问题讨论】:

与creating-a-future-from-intermediate-futures相关 您可以在 Load 方法中公开策略以获得所需的灵活性(在***别只能使用一个 std::launch::async)。 我认为将智能指针放在future 中没有任何问题。 【参考方案1】:

所以你有类似的东西:

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;
;

所以你可以像这样实现loadMedia()

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;
        );

【讨论】:

以上是关于在 c++11 中转换 std::future 或 std::shared_future的主要内容,如果未能解决你的问题,请参考以下文章

C++11之std::future对象的基本用法

[C++11 多线程异步] --- std::promise/std::future

[C++11 多线程异步] --- std::promise/std::future

[C++11 多线程异步] --- std::promise/std::future

[C++11 多线程异步] --- std::promise/std::future

C++11promise和future介绍