C++11スマートポインタのshared_ptr<void>

一緒に書く習慣をつけましょう!「ナゲッツデイリーニュープラン・4月アップデートチャレンジ」に参加して3日目です。クリックしてイベントの詳細をご覧ください

1.背景

Alexaベースのフルリンクインテリジェント音声SDKは、C ++に基づくクロスプラットフォーム機能を実装し、Android、Mac、Linux、およびその他のデバイスで実行されます。iOSと互換性がある場合、iOSはオーディオ用のC++インターフェイスを提供しないことがわかります。キャプチャして再生するため、SDKを変更して許可する必要があります。SDKの初期化時に、外部コレクターとプレーヤーの実装クラスが挿入されます。同時に、SDKのAndroidプレーヤーはffmpegデコード+openslに基づいて実装されます。ただし、パッケージサイズの問題を考慮し、このinterface.playerをベースにAndroidベースの外部ハードウェアデコードを実装する予定です。

2.実装のアイデア

2つのインターフェイス、ExternalMediaPlayerInterfaceとExternalMicrophoneInterfaceがSDK内で定義されています。これらの2つのオブジェクトは、SDKが初期化されるときに渡されます。

int create_and_run_home_speech_core_engine(std::string& configFiles, \
                                           std::string& configJsonData, \
                                           std::shared_ptr<HomeSpeech::engine_result_t> engineResult, \
                                           const std::string pathToKWDInputFolder = "",     \
                                           const std::string& logLevel = "",
										   std::shared_ptr<HomeSpeech::ExternalMicrophoneInterface> externalMicWrapper = nullptr,
										   std::function<std::shared_ptr<HomeSpeech::ExternalMediaPlayerInterface>(std::shared_ptr<alexaClientSDK::avsCommon::sdkInterfaces::HTTPContentFetcherInterfaceFactoryInterface> contentFetcherFactory,
																												   bool enableEqualizer,
																												   const std::string& name)> createExternalMediaPlayerCallback = nullptr);
复制代码

2つのインターフェイスはSDK内のAudioInputStreamデータ構造に依存しているため、ここではコールバック関数を使用し、SDK内でこのメソッドを呼び出し、SDK外でメソッドを実装して特定のプレーヤーを作成します。

3.AndroidとiOSのインターフェース実装の違い

元々、この実装で十分ですが、Androidの取得と再生に同じopenslオブジェクトが使用され、オブジェクトはSDK内で作成されます。再利用する場合は、SDK内でメソッドを呼び出してopenslオブジェクトをに設定する必要があります。プレーヤー、しかしこのオブジェクトiOSは必要ありません、それを行う方法は?

純粋なCポインターの考え方によれば、インターフェイスは1つを設定するように定義されてvoid *おり、C ++ではrawポインターが許可されているため、rawポインター間の変換方法はC言語ポインターの変換方法と同じですが、プロジェクト全体はCに基づいています++ 11スマートポインター、スマートポインターを変換する方法ウールの布?まず、C++11スマートポインターを確認しましょう。

3.1 std::shared_ptr型キャストstd::dynamic_pointer_cast

C++11中引入了智能指针std::shared_ptr等,智能指针转换不能通过C方式进行强转,必须通过库提供转换函数进行转换。 C++11的方法是:std::dynamic_pointer_cast,如下代码所示:

#include <memory>
#include <iostream>

class A {
    public:
   AA(){}
    virtual ~A() {}
};

class B : public A {
    public:
    B(){}
    virtual ~B() {}
};

int main()
{
    // derived class to A class
    B* d1 = new B();
    A* b1 = d1;
    //
    std::shared_ptr<B> d2 = std::make_shared<B>();
    std::shared_ptr<A> b2 = d2;
    /*
     * dynamic cast maybe failed. and return null;
     * 
     */
    B* d11 = dynamic_cast<B*>(b1); //succ
    B* d12 = static_cast<B*>(b1);  //succ
    
    typedef std::shared_ptr<B> d_ptr;
    // std::shared_ptr<B> d21 = dynamic_cast<d_ptr>(b2); //compile error
    std::shared_ptr<B> d22 = std::dynamic_pointer_cast<B>(b2);
    return 0;
}
复制代码

我们看看dynamic_pointer_cast与dynamic_cast的区别

dynamic_cast

将一个基类对象指针(或引用)cast到继承类指针,dynamic_cast会根据基类指针是否真正指向继承类指针来做相应处理。

主要用途:将基类的指针或引用安全地转换成派生类的指针或引用,并用派生类的指针或引用调用非虚函数。如果是基类指针或引用调用的是虚函数无需转换就能在运行时调用派生类的虚函数。

转换方式:

  • dynamic_cast< type* >(e) type必须是一个类类型且必须是一个有效的指针
  • dynamic_cast< type& >(e) type必须是一个类类型且必须是一个左值
  • dynamic_cast< type&& >(e) type必须是一个类类型且必须是一个右值

e的类型必须符合以下三个条件中的任何一个:

  1. e的类型是目标类型type的公有派生类
  2. e的类型是目标type的共有基类
  3. e的类型就是目标type的类型。

如果一条dynamic_cast语句的转换目标是指针类型并且转换失败了,会返回一个空指针,则判断条件为0,即为false;如果转换成功,指针为非空,则判断条件为非零,即true。

dynamic_pointer_cast与dynamic_cast用法类似,当指针是智能指针时候,向下转换,用dynamic_Cast 则编译不能通过,此时需要使用dynamic_pointer_cast。

3.2 std::shared_ptr<void>

类似于void *想到了std::shared_ptr,了解了一下还真有。先看看直接使用void*有哪些弊端:

  1. void*void *型安全性の保証はありません。それが指すオブジェクトが実際にクラスPeopleであるかどうかに関係なく、People*にを割り当てることができます。
  2. void *ライフサイクルはスマートポインタのように管理できないため、関連するデータのライフサイクルを手動で管理する必要があります。これにより、メモリリークが発生しやすくなります。
  3. ライブラリはvoid *、オブジェクトのタイプを認識していないため、ポイントされたオブジェクトをコピーできません

shared_ptr<void>replaceを使用void*すると、宣言サイクル管理の問題を解決できます。shared_ptrには、それが指すオブジェクトを適切に破棄する方法を知るのに十分な型情報があります。しかし、void*のようなstd:: shared_ptrは、型の安全性の問題を解決しません。

最後shared_ptr<void>に、SDK内で強力な型変換を使用するとエラーが報告されます。

/Library/android-ndk-r17c/sources/cxx-stl/llvm-libc++/include/memory:4851:16: error: 'void' is not a class
    Tp* __p = dynamic_cast<Tp*>(r.get());
               ^                  ~ (~)~
/xxx/src/main/cpp/AndroidMediaPlayer.cpp:494:19: note: in instantiation of function template specialization 'std::ndk1::dynamic_pointer_cast<alexaClientSDK::applicationUtilities::androidUtilities::AndroidSLESEngine, void>' requested here
  m_engine = std::dynamic_pointer_castalexaClientSDK::applicationUtilities::androidUtilities::AndroidSLESEngine(engine);
                  ^
1 error generated.
复制代码

3.3 std :: any

std :: anyなどのタイプを見つけることについても学びましたが、c++17でのみ使用できます。

anyヘッダーファイルで定義されている:#include <any>は、任意のタイプの単一の値に使用できるコンテナ类型安全です。std:anyは、タイプセーフでありながらタイプを変更できる値タイプです。つまり、オブジェクトは任意のタイプの値を保持できますが、現在保持されている値のタイプはわかっています。このタイプのオブジェクトを宣言する場合、可能なタイプを指定する必要はありません値は、any_cast<値のタイプ>を使用して取得できます。

最後に、AndoridExternalMediaplayerInterfaceがSDK内に実装され、Androidプラットフォームに適応します。

4.まとめ

この記事では、実際のプロジェクトに基づいて、C++11スマートポインターの型変換std::dynamic_pointer_castと、C++17によって提供される特別なスマートポインターstd::shared_ptrおよびstd::any型を紹介します。

おすすめ

転載: juejin.im/post/7082640093717987365