2012-03-08 6 views
10

私が選択したときにエラーをチェックするためにWindows API関数をラップしようとしています。以前のSOの質問で分かったように、テンプレート関数を使用してAPI関数を呼び出し、GetLastError()を呼び出して設定したエラーを取得することができます。私はこのエラーを私のErrorクラスに渡して、私に知らせてもらうことができました。これは素晴らしい作品テンプレート引数がvoidの場合はコンパイル時にチェック

int WINAPI someFunc (int param1, BOOL param2); //body not accessible 

int main() 
{ 
    int ret = someFunc (5, true); //works normally 
    int ret2 = Wrap (someFunc, 5, true); //same as above, but I'll get a message if there's an error 
} 

を次のように、この私は、コードを持って使用することで、

template<typename TRet, typename... TArgs> 
TRet Wrap(TRet(WINAPI *api)(TArgs...), TArgs... args) 
{ 
    TRet ret = api(args...); 
    //check for errors 
    return ret; 
} 

はここでテンプレート関数のコードです。しかし、1つの可能性のある問題があります。テンプレート関数にこれを下塗りすると、次のように見え、機能

void WINAPI someFunc(); 

してください:

void Wrap(void(WINAPI *api)()) 
{ 
    void ret = api(); //<-- ahem! Can't declare a variable of type void... 
    //check for errors 
    return ret; //<-- Can't return a value for void either 
} 

これを回避するために、私は私がvoidTRetを置き換えテンプレートのバージョンを作成してみました。残念なことに、これは実際にどちらを使用するかのあいまいさを引き起こします。さておき、私は

if (strcmp (typeid (TRet).name(), "v") != 0) //typeid(void).name() == "v" 
{ 
    //do stuff with variable to return 
} 

else 
{ 
    //do stuff without returning anything 
} 

を使用してみましたが、typeidは、実行時の比較なので、コードはまだ原因も、それは決してならば、ボイド変数を宣言しようとしているにコンパイルされません

次に、typeidの代わりにstd::is_same <TRet, void>::valueを使用してみましたが、実行時の比較でもあることが判明しました。

この時点で、私は次に何を試すべきか分かりません。コンパイラが私がやっていることがうまく動作すると知っていると信じる可能性はありますか?私はWrapに余分な議論を付けても構いませんが、実際には何も得られませんでした。

私はCode :: BlocksをGNU G ++ 4.6.1、Windows XP、Windows 7と一緒に使用しています。助けてくれてありがとうございます。Wrapを使用しないで終わらなければならないと言っていますvoidを返す関数の場合

+1

私は戻り値の型をvoidに特化した理由を理解しています(戻り値の型を明確にすることはできません)。void **と**特殊なパラメータを追加すると何が起こるのでしょうか?明示的な型で呼び出す必要があるかもしれません。 – Tod

+1

興味深い質問です。サイドノート:静的にチェックされないことに加えて、 'typeid'ソリューションは移植性がありません。標準では 'type_info :: name()'によって返される文字列について保証していません。 – Thomas

+0

私は2つを組み合わせることを考えなかった。やってみます。 typeidの問題についてのヘッドアップにも感謝します。 – chris

答えて

8

あなたは微調整の専門にヘルパークラスを使用することができます。

template <typename Res, typename... Args> 
Res Wrap(Res (WINAPI *f)(Args...), Args&& args...) 
{ 
    return wrapper<Res(Args...)>::wrap(f, std::forward<Args>(args)...); 
} 

Resvoid場合であっても、それが動作すること:

template <typename F> 
struct wrapper 
{}; 

template <typename Res, typename... Args> 
struct wrapper<Res(Args...)> 
{ 
    static Res wrap(Res (WINAPI *f)(Args...), Args&& args...) 
    { 
     Res r = f(std::forward<Args>(args)...); 
     // Blah blah 
     return r; 
    } 
}; 

template <typename... Args> 
struct wrapper<void(Args...)> 
{ 
    static void wrap(void (WINAPI *f)(Args...), Args&& args...) 
    { 
     f(std::forward<Args>(args)...); 
     // Blah blah 
    } 
}; 

さて、あなたはラッパーを書くことができます。 returnには、voidを返す関数でvoidを返す式が許可されています。

voidを返す関数の場合でも、Wrap(someFunc, 5, true)のように正しい型が導き出されます。

+0

おかげで、ありがとう。 <>内の型の必要性を排除しました(私は値下げして '#define vwrap wrap 'などと言っています)。それがうまくいくかどうかわかります。何らかの理由で 'std :: forward'と' Args && args ... 'はうまくいかないのですが、winapiはC言語になっています。 – chris

+0

心配しないで、フォワードは再び仕事をすることに決めました。私が家で使うコンパイラと学校でフラッシュドライブで使うコンパイラとの間に矛盾があるかもしれません。 – chris

+0

はい、これは完全に機能し、同じ構文を維持します。一つの変更は 'Args && args ...'を 'Args && ... args ...'に変更することでした。ありがとう。 – chris

1

(戻り値の型に明確にすることはできません)動作しませんでした、ボイドとして戻り値の型を専門とするが、空に特化した理由を私は理解しに答えるにコメントから推進し、追加のパラメータが動作するはずです追加して、何が起こったのか?明示的な型で呼び出す必要があるかもしれません。

+0

これは正常に動作したので、受け入れられた答えを変更することについてお詫びしますが、Alexandre C.の答えは、#defineステートメントやそのようなものを使用せずに以前と同じ*構文を使用しています。トレードオフはちょっとしたコードです。ラッパー全体と比較して何もありません。 – chris

+0

問題はありません、私は受け入れられた答えは、あなたが似たような状況にいる人に検索を見せたいと思うものでなければならないと思います。さらに、アレクサンドルの答えが自動的に推論するという事実は、バグプラスです。 upvoteを取得するだけでいいです。 – Tod

2

これを回避するために、私はTRetをvoidに置き換えたテンプレートのバージョンを作成しようとしました。残念なことに、これは実際にどちらを使用するかのあいまいさを引き起こします。

作業する必要があることvoidTRetより専門的なですが、ご指摘の通り、それはしませんので、私は、信じています。私は何かを逃しているかもしれませんが、どんな速度でも問題ではありません。TRet過負荷が選択されないようにすることができます。

template<typename TFun, typename... TArgs> 
auto Wrap(TFun api, TArgs&&... args) -> 
typename std::enable_if< 
    !std::is_void<typename std::result_of<TFun(TArgs...)>::type>::value, 
    typename std::result_of<TFun(TArgs...)>::type 
>::type 
{ 
    auto result = api(std::forward<TArgs&&>(args)...); 
    return result; 
} 

template<typename TFun, typename... TArgs> 
auto Wrap(TFun api, TArgs&&... args) -> 
typename std::enable_if< 
    std::is_void<typename std::result_of<TFun(TArgs...)>::type>::value, 
    typename std::result_of<TFun(TArgs...)>::type 
>::type 
{ 
    api(std::forward<TArgs&&>(args)...); 
} 

void WINAPI f1() 
{ 
} 

void WINAPI f2(double) 
{ 
} 

int WINAPI f3() 
{ 
    return 0; 
} 

int WINAPI f4(double) 
{ 
    return 0; 
} 

int main() { 
    Wrap(f1); 
    Wrap(f2, 0); 
    return Wrap(f3) * Wrap(f4, 0); 
} 

更新:パラメータタイプ引数の型からの変換を可能にするように調整されます。

+0

このコードは正常に動作します。しかし、私の他のコードと一致させようとすると、関数にいくつのパラメータもあるときにあいまいさがあることがわかりました。パラメーターがない場合、あいまいさは除去されます。 – chris

+0

@chrisああ、そうです、それは私にとっても失敗に終わります。ただし、使用しないでくださいオーバーロードを削除することで簡単に修正され、編集されます。 – hvd

+0

これはいくつかの面白いことです:私はいくつかの基本的な数学関数以外のテンプレートではあまり成し遂げていないので、新しいものに関するすべての構文では役に立たないです。しかし、これは私にとってもうまくいきます。そして今、私は誰の答えを使うのか分かりません。/ – chris