Foreach
タプルのすべての要素を調べ、それぞれに対して同じ関数を呼び出すことは、おそらくタプルを使用するときに開発者が直面する最初のタスクです。 実装は非常に簡単です。
namespace tuple_utils { // 'callback' /* struct callback { template<std::size_t, class T> void operator()( T&& element ) { // do something } }; tupleForeach( callback(), myTuple ); */ template<class TCallback, class ...TParams> void tupleForeach( TCallback& callback, const std::tuple<TParams...>& tuple ); namespace { template<std::size_t Index, class TCallback, class ...TParams> struct _foreach_ { static void tupleForeach_( TCallback& callback, const std::tuple<TParams...>& tuple ) { // callback'a const std::size_t idx = sizeof...( TParams ) - Index; callback.operator()<idx>( std::get<idx>( tuple ) ); _foreach_<Index - 1, TCallback, TParams...>::tupleForeach_( callback, tuple ); } }; template<class TCallback, class ...TParams> struct _foreach_<0, TCallback, TParams...> { static void tupleForeach_( TCallback& /*callback*/, const std::tuple<TParams...>& /*tuple*/ ) {} }; } // template<class TCallback, class ...TParams> void tupleForeach( TCallback& callback, const std::tuple<TParams...>& tuple ) { _foreach_<sizeof...( TParams ), TCallback, TParams...>::tupleForeach_( callback, tuple ); } } // tuple_utils
ここでは、補助的な_foreach_構造が使用され、追加のテンプレートパラメーターとして別のタプルインデックスがあります。 唯一の静的メソッドtupleForeach_は、このインデックスを持つ要素のコールバックを介して指定された関数を呼び出し、その後、再帰的に呼び出されます。 ゼロに等しいインデックスに対するこの構造の部分的な特殊化は縮退であり、再帰の完了です。
例1.バナル
struct ForeachCallback { template<std::size_t Index, class T> void operator()( T&& element ) { std::cout << "( " << Index << " : " << element << " ) "; } }; void foo() { auto myTyple = std::make_tuple( 42, 3.14, "boo" ); tuple_utils::tupleForeach( ForeachCallback(), myTyple ); }
例2.ゲッターの確認
// getter' template<class TResult, class TOwner> using TGetter = TResult( TOwner::* )() const; // , getter' template<class TGetterOwner, class ...TParams> class MyGetterContainer { // getter' template<class TResult> using TMyGetter = TGetter<TResult, TGetterOwner>; ..... private: ..... // , getter' ( nullptr) void checkGetters(); // getter' (.. ) std::tuple<TMyGetter<TParams>...> m_getters; }; namespace { // callback, getter' template<class TGetterOwner> struct GetterCheckCallback { // , getter' // 'Index' template<std::size_t Index, class T> void operator()( const TGetter<T, TGetterOwner>& element ) { assert( element != nullptr ); } }; } // template<class TGetterOwner, class ...TParams> void MyGetterContainer<TGetterOwner, TParams...>::checkGetters() { // callback getter' tuple_utils::tupleForeach( GetterCheckCallback<TGetterOwner>(), m_getters ); }
地図
タプルの操作の利便性のために頭に浮かぶもう1つのタスクは、特定のタプルの要素に対する特定の関数の実行結果から新しいタプルを順番に作成する関数です。 関数型言語の非常に一般的なタスク。 その実装はおそらくもっと簡単です:
namespace tuple_utils { // ( ) 'callback' 'sourceTuple' /* struct callback { template<std::size_t, class R, class T> R operator()( T&& element ) { // do something } }; mapTuple( callback(), myTuple ); */ template<class TCallback, class TSourceTuple> auto mapTuple( TCallback& callback, const TSourceTuple& sourceTuple ); namespace { template<class TCallback, class TSourceTuple, std::size_t... Indices> auto mapTuple_( TCallback& callback, const TSourceTuple& sourceTuple, std::index_sequence<Indices...> ) { return std::make_tuple( callback.operator()<Indices>( std::get<Indices>( sourceTuple ) )... ); } } // template<class TCallback, class TSourceTuple> auto mapTuple( TCallback& callback, const TSourceTuple& sourceTuple ) { return mapTuple_( callback, sourceTuple, std::make_index_sequence<std::tuple_size<TSourceTuple>::value>() ); } } // tuple_utils
ここで、補助関数mapTuple_を使用します。これは、 index_sequenceを介して 、追加のパラメーター、すべてのタプルインデックスのセットを受け取ります。 結果タプルは、各インデックスの要素に対するコールバックを介して指定された関数の結果から形成されます。
例1.バナル
struct MapCallback { template<std::size_t Index, class T> std::string operator()( T&& element ) { std::stringstream ss; ss << "( " << Index << " : " << element << " )"; std::string result; result << ss; return result; } }; void foo() { auto sourceTyple = std::make_tuple( 42, 3.14, "boo" ); auto strTuple = tuple_utils::mapTuple( MapCallback(), sourceTyple ); }
例2.ゲッター値のタプルの形成
// getter' template<class TResult, class TOwner> using TGetter = TResult( TOwner::* )() const; // , getter' template<class TGetterOwner, class ...TParams> class MyGetterContainer { // getter' template<class TResult> using TMyGetter = TGetter<TResult, TGetterOwner>; ..... protected: ..... // getter' std::tuple<TParams...> getterValues() const; private: ..... // , getter' TGetterOwner& m_getterOwner; // getter' (.. ) std::tuple<TMyGetter<TParams>...> m_getters; }; namespace { // callback, getter' template<class TGetterOwner> struct GetterValuesCallback { public: // GetterValuer( const TGetterOwner& getterOwner ) : m_getterOwner( getterOwner ) { } // , getter'; getter' // 'Index' template<std::size_t Index, class T> T operator()( const TGetter<T, TGetterOwner>& oneGetter ) { return ( m_getterOwner.*oneGetter )(); } private: const TGetterOwner& m_getterOwner; }; } // template<class TGetterOwner, class ...TParams> std::tuple<TParams...> MyGetterContainer<TGetterOwner, TParams...>::getterValues() const { // callback getter' return tuple_utils::mapTuple( GetterValuesCallback<TGetterOwner>( m_getterOwner ), m_getters ); }
電話する
タプルで他にできることは、関数を呼び出すためのパラメーターとしてそのコンテンツを使用することです(当然、タプルの要素の順序とタイプに対応する引数の順序とタイプ)。 この関数の実装は、 map関数の実装に非常に似ています。
namespace tuple_utils { // 'callback', 'tuple' /* struct callback { template<class TResult, class ...TParams> TResult operator()( TParams... ) { // do something } }; callTuple( callback(), myTuple ); */ template<class TCallback, class ...TParams> auto callTuple( TCallback& callback, const std::tuple<TParams...>& tuple ); namespace { template<class TCallback, class TTuple, std::size_t... Indices> auto callTuple_( TCallback& callback, const TTuple& tuple, std::index_sequence<Indices...> ) { return callback( std::get<Indices>( tuple )... ); } } // template<class TCallback, class ...TParams> auto callTuple( TCallback& callback, const std::tuple<TParams...>& tuple ) { return callTuple_( callback, tuple, std::index_sequence_for<TParams...>() ); } } // tuple_utils
ここでは、 mapの場合と同様に、補助関数callTuple_が使用されます 。これは、 index_sequenceを介して、追加のパラメーター、すべてのタプルインデックスのセットを受け取ります。 callbackを介して指定された関数を呼び出し、インデックスに対応するすべてのタプル要素を渡します。 その実行の結果は、転送された関数の実行の結果です。
例1.バナル
bool checkSomething( int a, float b, const std::string& txt ); struct CallCallback { template<class TResult, class ...TParams> TResult operator()( TParams... params ) { return checkSomething( params... ); } }; void foo() { std::tuple<int, float, std::string> paramsTyple = std::make_tuple( 42, 3.14, "boo" ); bool isParamsValid = tuple_utils::callTuple( CallCallback(), paramsTyple ); }
例2. getterパラメーター値を使用してsetterを呼び出す
// , getter' template<class TGetterOwner, class ...TParams> class MyGetterContainer { ..... protected: ..... // getter' std::tuple<TParams...> getterValues() const; ..... }; // setter' void- c template<class TOwner, class ...TParams> using TSetter = void( TOwner::* )( TParams... ); // , setter getter' template<class TSetterOwner, class TGetterOwner, class ...TParams> class MySetterCaller : public MyGetterContainer<TGetterOwner, TParams...> { // getter' using TMySetter = TSetter<TSetterOwner, TParams...>; ..... public: ..... // setter getter' void callSetter(); private: ..... // , setter TSetterOwner& m_setterOwner; // setter TMySetter m_setter; }; namespace { // callback, setter' template<class TSetterOwner, class ...TParams> struct CallSetterCallback { public: // GetterPasser( TSetterOwner& setterOwner, TSetter<TSetterOwner, TParams...> setter ) : m_setterOwner( setterOwner ), m_setter( setter ) { } // , setter' void operator()( TParams... params ) { return ( m_setterOwner.*m_setter )( params... ); } private: TSetterOwner& m_setterOwner; TSetter<TSetterOwner, TParams...> m_setter; }; } // template<class TSetterOwner, class TGetterOwner, class ...TParams> void MySetterCaller<TSetterOwner, TGetterOwner, TParams...>::callSetter() { // getter' std::tuple<TParams...> _getterValues = getterValues(); // callback setter' tuple_utils::callTuple( CallSetterCallback( m_setterOwner, m_setter ), _getterValues ); }
PS C ++ 17では、 std :: applyが利用可能になり、同じ機能を実行します。
総論
- インデックスをコールバックに渡す
上記の実装では、 コールバックによって処理される要素のインデックスがcallbackに渡されます 。 さらに、引数としてではなく、 コールバック自体のパラメーターとして渡されました。 これは、機能の範囲を拡張するためだけに行われます。 このような転送により、 コールバック内でテンプレート関数を呼び出し(パラメーターとしてインデックスを使用)、一般にインデックスをパラメーターとして使用して、インデックスを関数に引数として渡すときに不可能なものをインスタンス化できます。 - コールバック送信
上記のすべての実装では、 コールバックは参照によって(値ではなく) 渡されます。 これは、呼び出し元が使用する特定のコールバック (コピーではなく) の有効期間を制御するために行われます 。
別の例。 メソッドハンドラーからファンクターハンドラーへの変換
// - void- template<class TObject, class TValue> using TMethodHandler = void( TObject::* )( const TValue& ); // , - template<class ...TValues> class MyHandlerContainer { public: // ; - MyHandlerContainer( const std::function<void( const TValues& )>... handlers ); ..... // - template<class TMethodOwner> static MyHandlerContainer<TValues...>* createFrom( TMethodOwner& methodOwner, TMethodHandler<TMethodOwner, TValues>... handlers ); ..... }; namespace { // callback - template<class TMethodOwner> struct CheckCallback { // CheckCallback() : IsValid( true ) { } // - template<std::size_t Index, class TValue> void operator()( const TMethodHandler<TMethodOwner, TValue>& oneMethodHandler ) { if( oneMethodHandler == nullptr ) IsValid = false; } bool IsValid; } // callback - - template<class TMethodOwner> struct FunctorHandlerCallback { public: // FunctorHandlerCallback( TMethodOwner& methodOwner ) : m_methodOwner( methodOwner ) { } // - - template<std::size_t Index, class TValue> std::function<void( const TValue& )> operator()( const TMethodHandler<TMethodOwner, TValue>& oneHandlers ) { return [ this, oneHandlers ]( const TValue& tValue ) { ( m_methodOwner.*oneHandlers )( tValue ); }; } private: TMethodOwner& m_methodOwner; }; // callback 'MyHandlerContainer' - template<class ...TValues> struct CreateCallback { // 'MyHandlerContainer' - auto operator()( std::function<void( const TValues& )>... handlers ) { return new MyHandlerContainer<TValues...>( handlers... ); } }; } // template<class ...TValues> template<class TMethodOwner> MyHandlerContainer<TValues...>* MyHandlerContainer<TValues...>::createFrom( TMethodOwner& methodOwner, TMethodHandler<TMethodOwner, TValues>... handlers ) { // - auto methodsTuple = std::make_tuple( handlers... ); // , CheckCallback checkCallback; tuple_utils::tupleForeach( checkCallback, methodsTuple ); // if( checkCallback.IsValid ) { // (, ) FunctorHandlerCallback<TMethodOwner>* functorHandlerCallback = new FunctorHandlerCallback<TMethodOwner>( methodHolder ); // - auto handlersTuple = tuple_utils::mapTuple( *functorHandlerCallback, methodsTuple ); // - 'MyHandlerContainer' MyHandlerContainer<TValues...>* result = tuple_utils::callTuple( CreateCallback<TValues...>( multiProperty ), handlersTuple ); return result; } // assert( false ); return nullptr; }
index_sequenceを使用しない実装
index_sequenceは、C ++ 14でのみ表示されます。 これらの関数をC ++ 11(タプルが現れた)で使用したい場合、何らかの他の理由でindex_sequenceを使用したくない場合、またはmapの実装を見てそれらなしで関数を呼び出すのが面白い場合、実装は次のとおりです。
地図
namespace tuple_utils { // tuple (tuple ) 'callback' tuple' /* struct callback { template<std::size_t, class R, class T> R operator()( T&& element ) { // do something } }; mapTuple( callback(), myTuple ); */ template<class TCallback, class TSourceTuple> auto mapTuple( TCallback& callback, const TSourceTuple& sourceTuple ); namespace { template<std::size_t Index, class TCallback, class TSourceTuple, std::size_t... Indices> struct _map_ { auto static mapTuple_( TCallback& callback, const TSourceTuple& sourceTuple ) { const std::size_t idx = std::tuple_size<TSourceTuple>::value - Index; return _map_<Index - 1, TCallback, TSourceTuple, Indices..., idx>::mapTuple_( callback, sourceTuple ); } }; template<class TCallback, class TSourceTuple, std::size_t... Indices> struct _map_<0, TCallback, TSourceTuple, Indices...> { auto static mapTuple_( TCallback& callback, const TSourceTuple& sourceTuple ) { return std::make_tuple( callback.operator()<Indices>( std::get<Indices>( sourceTuple ) )... ); } }; } // template<class TCallback, class TSourceTuple> auto mapTuple( TCallback& callback, const TSourceTuple& sourceTuple ) { return _map_<std::tuple_size<TSourceTuple>::value, TCallback, TSourceTuple>::mapTuple_( callback, sourceTuple ); } } // tuple_utils
電話する
namespace tuple_utils { // 'callback', tuple /* struct callback { template<class TResult, class ...TParams> TResult operator()( TParams... params ) { // do something } }; callTuple( callback(), myTuple ); */ template<class TCallback, class TResult, class ...TParams> TResult callTuple( TCallback& callback, const std::tuple<TParams...>& tuple ); namespace { template<std::size_t Index, class TCallback, class TResult, class TTuple, class ...TParams> struct _call_ { static TResult callTuple_( TCallback& callback, const TTuple& tuple, TParams... params ) { const std::size_t idx = std::tuple_size<TTuple>::value - Index; return _call_<Index - 1, TCallback, TResult, TTuple, TParams..., typename std::tuple_element<idx, TTuple>::type>::callTuple_( callback, tuple, params..., std::get<idx>( tuple ) ); } }; template<class TCallback, class TResult, class TTuple, class ...TParams> struct _call_<0, TCallback, TResult, TTuple, TParams...> { static TResult callTuple_( TCallback& callback, const TTuple& tuple, TParams... params ) { return callback( params... ); } }; } // template<class TCallback, class TResult, class ...TParams> TResult callTuple( TCallback& callback, const std::tuple<TParams...>& tuple ) { return _call_<sizeof...( TParams ), TCallback, TResult, std::tuple<TParams...>>::callTuple_( callback, tuple ); } } // tuple_utils
これらの関数の実装へのアプローチは同じです:インデックス( index_sequenceの代わりに)またはパラメーターを手動で「蓄積」し、再帰の最後に、既に取得したインデックス/パラメーターのセットで必要なアクションを実行します。 個人的には、インデックスを使用したアプローチはより普遍的です。
お時間をいただきありがとうございます!