6

現在、unique_ptr< Interface >&&コンストラクターでを受け入れるテスト中のクラスがあり、インターフェイス実装の単一の所有権を取得したいことを表現しています。ただし、モックを使用してこのクラスをテストする場合は問題が発生しInterfaceます。モックフレームワーク(HippoMocks)は、私Interface*が所有していないものだけを提供するため、削除できません。

以前、const shared_ptr< Interface >&引数としてクラスをテストするときに同じ問題が発生しましたが、カスタムのno-opdeleterを提供することで問題を修正しました。

template< class T >
void NoDelete( T* )
{
}

  //create a shared_ptr without effective deleter
template< class T >
std::shared_ptr< T > mock_shared( T* t )
{
  return std::shared_ptr< T >( t, NoDelete< T > );
}

Interface* iface = mocks.GetMeAMock< Interface >();
DoStuffWithSharedPtrOfInterface( mock_shared< Interface >( iface ) );

削除者はテンプレート引数であるため、unique_ptrの同様の修正は実際には機能しません。

template< class T >
struct NoDelete
{
  void operator ()( T* )
  {
  }
};

  //oops this is totally useless since std::unique_ptr< T, NoDelete< T > >
  //is not quite the same type as std::unique_ptr< T >
template< class T >
std::unique_ptr< T, NoDelete< T > > mock_unique( T* t )
{
  return std::unique_ptr< T, NoDelete< T > >( t, NoDelete< T >() );
}

これに対する回避策はありますか?または、そもそもここでunique_ptrを使用するべきではありませんか?

更新 私はこれを試してみました。動作するはずですが、sizeof(ptr)は8になり、どのような影響があるかを判断するのは困難です。

  //use CustomUniquePtr::type instead of uniqe_ptr
template< class T >
struct CustomUniquePtr
{
  typedef typename std::unique_ptr< T, void (*) ( T* ) > type;
}

  //use everywhere
template< class T >
CustomUniquePtr< T >::type make_unique( T* p )
{
  return CustomUniquePtr< T >::type( p, Delete< T > );
}

  //use when mocking, doe not delete p!
template< class T >
CustomUniquePtr< T >::type mock_unique( T* p )
{
  return CustomUniquePtr< T >::type( p, NoDelete< T > );
}
4

4 に答える 4

7

shared_ptrその削除機能を他の簿記データ(refcountなど)とともにヒープに格納します。unique_ptrヒープオーバーヘッドがないため、削除機能はオブジェクトに格納する必要があり、型の一部になります。

コンストラクターをテンプレート化しDeleter、をに変換しunique_ptrて、shared_ptr削除タイプを消去できます。

(インターフェースのサイズに応じて)より良いのはInterface、モックに転送するプロキシオブジェクトを提供することInterface *です。

于 2012-06-14T17:05:25.793 に答える
3

私はいくつかのオプションを、順不同で考えることができます。

  • テストコードでは(そしてテストコードでのみ、アプリケーションでこれを望まない)、default_delete<Interface>実際には何も削除しないように特化しています。これは、所有しているオブジェクトを削除しないことを意味します。これは、テストでも所有しているオブジェクトを削除する必要があるオブジェクトがあるunique_ptr<Interface>場合は望ましくない場合があります。unique_ptr<Interface>

  • Interfaceコンストラクターに提供されたインスタンスにすべてを転送する実装を作成します。次に、モックフレームワークが提供するインターフェイスに転送するインスタンスをテストで動的に割り当てることができます。

  • モックフレームワークを変更して、インターフェイスを動的に割り当て、削除できるようにします。

  • std::shared_ptrカスタム削除機能を渡すことができるように、を使用するようにコードを変更します。ただし、これにより「一意の所有権」プロパティが失われます。

  • 構築パラメーターに応じて削除するかどうかに関係なく、カスタムスマートポインターを使用するようにコードを変更します。unique_ptrこれは、「削除するかどうか」フラグが付いた、のラッパーである可能性があります。割り当て/破棄呼び出しで、フラグが「削除しない」に設定されている場合は、オブジェクトの削除を許可するのではなくrelease()、ラップされたものを呼び出すだけです。unique_ptrカスタムポインタタイプは、標準のポインタタイプよりもユーザーに馴染みがなく、フラグがスペースを占有します。

  • 「更新」にあるような型ジェネレーターを使用して、どこでもと言うようCustomUniquePtr<T>::typeにしてから、型ジェネレーターに削除機能を追加してもらいます。ここでの欠点は、生のポインタからタイプの新しいインスタンスを作成するために、ユーザーコードがデリータについて知っている必要があることです。つまり、ユーザーコードはInterface、削除機能についても知らなければ、(呼び出しをログに記録して転送するだけの単純なものでも)実装を簡単に作成することはできません。

もちろん、他のオプションがあるかもしれません。

于 2012-06-20T07:04:32.930 に答える
2

Hippomockはすでにこの問題の解決策を提供しています。仮想デストラクタとのインターフェイスがある場合は、デストラクタの期待値を登録するだけです。モックはモックデストラクタであるため、デストラクタの呼び出しによって破棄されることはありませんが、デストラクタへの呼び出しに対する期待値を設定する必要があります。

MockRepository mocks;
// create the mock
std::unique_ptr<IFoo> foo( mocks.Mock<IFoo>() );

// register the expectation for the destructor
mocks.ExpectCallDestructor( foo.get() );

// call to mocks destructor ok, mock not destroyed
foo.reset( nullptr );
于 2013-02-08T09:47:22.517 に答える
0

回答に示されているすべてのオプションの中で、最も適切なものは、モックフレームワークを確実に変更することです。これにより、プレーンを引き続き使用できるようになりますunique_ptr。モックインスタンスを動的に割り当てる方法がわかりませんでしたが、実際にはそれdelete自体をモックにすることができます。かなりハッキーですが、問題なく動作するようです。

基本的に、hippomocksはvtableを直接変更し、エントリが期待値などをチェックする関数を指すようにします。MSVCで私が知る限り、仮想デストラクタを持つクラスのvtableの最初のエントリは、スカラー削除デストラクタです。を呼び出すときに呼び出されますdelete ptr。そして、これは通常コンパイラによって生成され、デストラクタを呼び出します。したがって、vtableを変更し、最初のエントリポイントを他の何か(no-op)にすると、この問題は解決します。

これはMockRepositoryに追加されたコードであり、RegisterExpect_変更された関数の1つのコピーであり、常にvoid(void*)署名を使用します。

template< int X >
void NoOp( void* )
{
}

  //make first vtable entry point to NoOp
template< int X, typename Z2 >
void DeleteExpect( Z2 *mck )
{
  const int funcIndex = 0; //index of scalar deleting destructor
  void (MockRepository::*mfp)(void*) = &MockRepository::NoOp< X >;
  BasicRegisterExpect( reinterpret_cast<mock<Z2> *>( mck ),
   funcIndex, reinterpret_cast<void (base_mock::*)()>( mfp ), X );
}

#define MockDelete( obj ) DeleteExpect< __COUNTER__ >( obj )

使用方法は次のとおりです。

MockRepository mocks;
Interface* p = mocks.InterfaceMock< Interface >();
mocks.MockDelete( p );

mocks.InterfaceMock< DataStitcher >( std::unique_ptr< Interface >( p ) );
于 2012-06-21T11:36:27.403 に答える