2016-12-12 6 views
1

私は、関数ポインタをエンコードするクラステンプレートを作成しています。この関数は、結果の型と数/型のパラメータを持つことができます。これは私が持っているものです:別のテンプレート関数の定義における関数のパラメータタイプのアンパック

LPVOID EncodePtr(LPVOID ptr) { 
    // Encode... 
    return ptr; 
} 

LPVOID DecodePtr(LPVOID ptr) { 
    // Decode... 
    return ptr; 
} 

template<class T> 
class encoded_ptr { 
public: 

    typedef encoded_ptr<T> _Myt; 

    encoded_ptr() { 
    ptr_ = (T*)EncodePtr(nullptr); 
    } 

    // Irresponsible? 
    template<class _OtherType> 
    encoded_ptr(_OtherType ptr) { 
    ptr_ = (T*)DecodePtr((LPVOID)ptr); 
    } 

    ~encoded_ptr() { 
    ptr_ = (T*)EncodePtr(nullptr); 
    } 

    // Makes it possible to call the function directly 
    template<class... _Args> 
    typename std::result_of<T*(_Args...)>::type operator()(_Args... _Ax) { 
    T* fn = get(); 

    return fn(_Ax...); 
    } 

    T* get() const { 
    return (T*)DecodePtr((LPVOID)ptr_); 
    } 

    bool is_set() { 
    return (get() != nullptr); 
    } 

private: 
    T* ptr_; 
}; 

期待どおりに動作します。例えば:

encoded_ptr<decltype(MessageBoxA)> MsgBox; 
MsgBox = &MessageBoxA; // Could also initialize in the constructor 

// (HWND)0 is justified by the actual problem in the question 
MsgBox((HWND)0, "Test message!", "Test", 0); 

最初の問題括弧演算子()が宣言されている方法は、Visual StudioのIntelliSenseには、その魔法を作り、私は、関数のパラメータについてのヒントを与えることを許可していないということです。

template<class... _Args> 
typename std::result_of<T*(_Args...)>::type operator()(_Args... _Ax) { 
    T* fn = get(); 

    return fn(_Ax...); 
} 

(_Args... _Ax)を使用する代わりに、実際の関数パラメータを展開して、IntelliSenseがヒントを正しく与えることができるようにしたいと考えています。

現在の動作は次のとおりです。

Wrong

期待される動作は次のとおりです。

enter image description here

第二の問題は、このよう関数を呼び出し、コンパイラがないことです基本的なキャストを行い、私にキャストすることを余儀なくされるNULL(void*)NULL0(HWND)0など多くのパラメータを持つ関数を使用する場合、これは面倒です。

多分実装に間違いがありますが、私はテンプレートエキスパートではありません。また、質問のタイトルが適切かどうかはわかりません。

何か助けていただきありがとうございます。

EDIT:私は(OlegBogdanovの提案@)これまでに試した何

template<class T, class... Args> 
class encoded_ptr; 

template<class T, class... Args> 
class encoded_ptr<T(Args...)> { 
public: 

    typedef encoded_ptr<T> _Myt; 

    using Fptr = T(*)(Args...); 
    encoded_ptr(Fptr ptr) { 
    ptr_ = (Fptr)EncodePtr((LPVOID)ptr); 
    } 

    // Makes it possible to call the function directly 
    typename T operator()(Args... _Ax) { 
    Fptr fn = get(); 

    return fn(std::forward<Args>(_Ax)...); 
    } 

    Fptr get() const { 
    return (T*)DecodePtr((LPVOID)ptr_); 
    } 

    bool is_set() { 
    return (get() != nullptr); 
    } 

private: 
    Fptr ptr_; 
}; 

結果:Incomplete type is not allowed:コンストラクタを使用/インスタンス化できません。

EDIT:

右方向だった、問題は呼び出し規約ました。

は変更:class encoded_ptr<T(__stdcall)(Args...)>

class encoded_ptr<T(Args...)>、および

using Fptr = T(*)(Args...)using Fptr = T(__stdcall*)(Args...)

に私の代わりにハードコードされ、それを持つの呼び出し規約を検出しようとしています。

+0

によって店舗、単に戻り値の型では、あなたが効果的にIntelliSenseをしませ戦っているコードの問題? –

+0

私はIntelliSenseと戦っていません。それは逆です。私は提案を受け入れることをうれしく思います。 – karliwson

+0

しかし、コンパイルの問題やランタイムの問題はありませんが、IntelliSenseの説明に満足していないだけですか?これは物語の異なる種類です –

答えて

1

は私が

template<class... _Args> 
typename std::result_of<T*(_Args...)>::type operator()(_Args... _Ax) { 
    T* fn = get(); 

    return fn(_Ax...); 
} 

のあなたの期待が間違っていると思います。それは完全に(あなたが悪いマン型消去を使用している)および(I 'の前方という言葉を使用していたが、それが不正確になります)になりますどんな呼び出し側渡しのターゲット関数の引数リストを無視する。

MsgBox(0, "Test message!", "Test", 0); 

がこのように0にintと推測され、それをHWNDにキャストする必要があります。そうでなければ、コンパイラがそれを推測する方法はありません。

あなたが実際にやっていることは、std::functionまたはその上にある種類のラッパーを再発明していることです。

あなたは本当にあなたがその実装の部分をコピーする必要がありますstd::functionがニーズのために十分ではないと思われる場合、つまり、あなたはあなたのタイプに引数リストをキャプチャするために、少なくとも

template<class R, class... Args> 
    class encoded_ptr; // leaving this undefined 

    template<class R, class... Args> 
    class encoded_ptr<R(Args...)> { 

    using Fptr = R(*)(Args...); 
    encoded_ptr(Fptr ptr) { 
     ptr_ = (T*)DecodePtr((LPVOID)ptr); 
    } 
    ... 

を持っている必要があります

とコールオペレータは、ランダムに入力された渡される引数の代わりにそれを再使用します。

// this must not be here -> template<class... _Args> 
R operator()(Args... _Ax) { 
    T* fn = get() 

    return fn(std::forward<Args>(_Ax)...); 
} 

編集:

あなたが保存することはできませんが、T*はもう、TFptr

+0

クラスの目的は、後で呼び出すための関数を格納します。コード化されたラッパー/ポインタで、必要に応じて他のクラスに安全に格納できます。なぜこの安全が必要なのですか?また、「なぜMicrosoftが 'EncodePointer()'と 'DecodePointer()'を提供するのか? – karliwson

+0

'Encode/DecodePointer' APIは難読化の手段ですが、どのような安全性について別の見解がない限り、安全性とはあまり関係ありません:)しかし、' std :: functionあなたが望むものを達成する方法のアイデアを与える必要があります –

+0

目標はクラッカーの生活を少し難しくすることです。余分なレイヤーではなく、完璧なソリューションです。正しい単語が「安全」でないかもしれない。そして、はい、私はあなたの助けに感謝します。なぜなら、私はまだそれをやる方法を知らないからです。 – karliwson

関連する問題