2017-07-11 11 views
6

私はここ数日間、C++で関数ポインタ用の汎用ラッパーを作成しようとしていましたが、私はほぼすべての単一の問題バーを解決することができました。私の主な目標は、関数ポインタを内部に格納した関数として単にオブジェクトを呼び出すことでした。ポインタがどこかを指していたなら、それは通常通り呼び出すが、ヌルポインタは関数を呼び出さないだけで何も起こらないかのように続行する。私はこれを主にコールバック関数の目的に使用するつもりです。関数が呼び出されたかどうかは気にせず、アクションを実行するだけです。それは次のようにほぼ完璧に動作します:Cの '...'パラメータパックをC++のテンプレート値として使用していますか?

template<typename T> 
class Action; 

template<typename TReturn, typename ... TArgs> 
class Action<TReturn(TArgs...)> { 
public: 
    //! Define a type for the Action object 
    typedef TReturn(*signature)(TArgs...); 

    //! Constructors 
    inline Action(const signature& pFunc = nullptr) : mPointer(pFunc) {} 
    inline Action(const Action& pCopy) : mPointer(pCopy.mPointer) {} 

    //! Operator Call 
    inline bool operator() (TReturn& pReturn, TArgs ... pArgs) const { if (!mPointer) return false; pReturn = mPointer(pArgs...); return true; } 

    //! Operators 
    inline Action& operator=(const Action& pCopy) { mPointer = pCopy.mPointer; return *this; } 
    inline Action& operator=(const signature& pFunc) { mPointer = pFunc; return *this; } 
    inline operator bool() const { return (mPointer != nullptr); } 

private: 
    //! Store a pointer to the callback function 
    signature mPointer; 
}; 

template<typename ... TArgs> 
class Action<void(TArgs...)> { 
public: 
    //! Define a type for the Action object 
    typedef void(*signature)(TArgs...); 

    //! Constructors 
    inline Action(const signature& pFunc = nullptr) : mPointer(pFunc) {} 
    inline Action(const Action& pCopy) : mPointer(pCopy.mPointer) {} 

    //! Operator Call 
    inline bool operator() (TArgs ... pArgs) const { if (!mPointer) return false; mPointer(pArgs...); return true; } 

    //! Operators 
    inline Action& operator=(const Action& pCopy) { mPointer = pCopy.mPointer; return *this; } 
    inline Action& operator=(const signature& pFunc) { mPointer = pFunc; return *this; } 
    inline operator bool() const { return (mPointer != nullptr); } 

private: 
    //! Store a pointer to the callback function 
    signature mPointer; 
}; 

しかし、私が最も可能性が高い。このラッパーはデバッグ情報やフォーマットされたテキストの出力で使用するような状況のように感じます。これは、ユーザー定義の関数やprintfなどの組み込み関数を介して行うことができます。 printf関数の署名と一致するアクションは次のように作成されます:

Action<int(const char*, ...)> callback = printf; 

、他のどのアクションが振る舞うだろうと同じように動作することができるだろう。私が見つけている問題は、 '...'は、テンプレートの署名を特殊化のいずれかに揃えず、その代わりに最初のプロトタイプだけにすることです。

これはなぜ動作しないのか、そしてコンパイラが必要なクラスの生成を処理できない理由を完全に理解できますが、私は誰かがこれを達成するための卑劣なやり方を知っていることを期待していました。あなたの質問の言及下のコメントとして

#include <utility> 
#include <cstdio> 
#include <cmath> 

template<typename Fn> 
class Action { 
    Fn* function_ptr;   
public: 
    Action() noexcept : function_ptr(nullptr) {}  
    Action(std::nullptr_t) noexcept : function_ptr(nullptr) {}  
    Action(const Action& other) : function_ptr(other.function_ptr) {}  
    Action(Fn f) : function_ptr(f) {} 

    Action& operator=(const Action& other) { 
     return (function_ptr = other.function_ptr, *this); 
    }    
    Action& operator=(std::nullptr_t) { 
     return (function_ptr = nullptr, *this); 
    }  
    Action& operator=(Fn f) { 
     return (function_ptr = f, *this); 
    } 

    template<typename... Params> 
    auto operator()(Params&&... params) { 
     return function_ptr(std::forward<Params>(params)...); 
    } 
}; 

Live Demo

:すべてのヘルプは非常に感謝:)

+7

'std :: function'を使わないのはなぜですか? – StoryTeller

+0

「アクション」の目的は何ですか? ['std :: function'](http://en.cppreference.com/w/cpp/utility/functional/function)を使用できないという要件はありますか?どのような問題を 'アクション'は 'std :: function'が解決しないのですか? –

+1

https://stackoverflow.com/questions/18370396/why-cant-stdfunction-bind-to-c-style-variadic-functionsは、これを達成する方法についていくつかの解説をしました。私は各バリデーショナルな関数について、あなたを助ける具体的なクラスが必要であると思いますし、あなたが呼び出すことができる関数のv *バリエーションがあることを願っています。 – mksteve

答えて

1

次の例は、無キャプチャして、すべての機能タイプのために働く、とラムダ、高く評価されるだろうstd::functionを使用すると、関数ポインタ用のラッパーを記述するよりも優れています。 std::functionの唯一の問題は、省略記号を含む関数シグネチャでは使用できないことです。明示的に署名を指定すると、たとえば署名を保存できます。 printfは、次のように:あなたがC++ 17やブーストを使用している場合

std::function<int(const char*, int, double, double)> fn = printf; 

は、次のようstd::anyまたはstd::functionに割り当てることができBoost.Anyを使用して、あなたはprintf様の機能を所有して実装できます。

#include <iostream> 
#include <string> 
#include <vector> 
#include <any> 
#include <functional> 

using namespace std; 

void any_printf(string&& format, vector<any>&& args) { 
    int arg_index = 0; enum { NORMAL, CONVERT } mode; 

    for(auto& chr : format) { 
     if(mode == CONVERT) { 
      switch(chr) { 
      case 'd': cout << any_cast<int>(args[arg_index++]); break; 
      case 'f': cout << any_cast<float>(args[arg_index++]); break; 
      case 's': cout << any_cast<string>(args[arg_index++]); break; 
      /* ... */ 
      default: cout << chr; 
      }; 
      mode = NORMAL; 
     } 
     else { 
      chr == '%' ? (mode = CONVERT, 0) : (cout << chr, 0); 
     } 
    } 
} 

int main() { 
    using namespace string_literals; 
    function<void(string&&, vector<any>&&)> f_ptr { any_printf }; 

    f_ptr("Cuboid: %smm x %dmm x %fmm.\n", { any("3"s), any(4), any(6.67f) }); 
    return 0; 
} 
+0

それはそれを働かせることについて行く興味深い方法です。私の唯一の問題は、テンプレートの署名を取得するためには値が存在する必要があるということです。状況に合うように少なくとも1つの関数を念頭に置くことなく、目的の関数コールバックを単純に減速させるのは簡単ではありません。私は、私が不可能ではないにしても、近くにあった文法の正確なタイプを推測していました。クイック返信ありがとう:) – MitchellCroft

+1

@MitchellCroft、コールバックを呼び出す関数を書くときには、そのシグネチャを定義する必要があります。可変数のデータと変数タイプ**( 'printf'のように呼び出すことができます)で**そのコールバックを呼び出すことを希望する場合は、 [std :: any'](http://en.cppreference.com/w/cpp/utility/any)型の 'std :: vector'です。これはC++ 17の機能ですが、[Boost.Any](http://www.boost.org/doc/libs/1_64_0/doc/html/any.html)も代用として使用できます。 – Akira

+0

@MitchellCroft、私は私が前のコメントで述べた 'std :: any'アプローチで私の答えを編集しました。 – Akira

関連する問題