2016-08-22 23 views
13

私はC++ 11の実験でこれを見つけました。私はそれが明白な解決策であることがわかりましたが、私は野生のそれの他の例を見つけることができなかったので、私は何かが欠けていることを心配しています。std :: functionの代わりに関数を引数として渡す(コールバックなど)

私は(「addAsync」機能で)を参照しています実際:

#include <thread> 
#include <future> 
#include <iostream> 
#include <chrono> 

int addTwoNumbers(int a, int b) { 
    std::cout << "Thread ID: " << std::this_thread::get_id() << std::endl; 

    return a + b; 
} 

void printNum(std::future<int> future) { 
    std::cout << future.get() << std::endl; 
} 

void addAsync(int a, int b, auto callback(std::future<int>) -> void) { //<- the notation in question 
    auto res = std::async(std::launch::async, addTwoNumbers, a, b); 

    if (callback) //super straightforward nullptr handling 
     return callback(std::move(res)); 
} 

int main(int argc, char** argv) { 
    addAsync(10, 10, [](std::future<int> number) { //lambda functions work great 
     addAsync(number.get(), 20, [](std::future<int> number) { 
      addAsync(893, 4387, printNum); //as do standard functions 
      addAsync(2342, 342, nullptr); //executes, sans callback 

      std::cout << number.get() << std::endl; 
     }); 
    }); 

    std::cout << "main thread: " << std::this_thread::get_id() << std::endl; 

    return 0; 
} 

は(私はMSVC++ 2015でそれをのみ試してみた)、それは悪い習慣を検討し、またはそれは非移植可能です?また、コンパイラはこれをどのように扱いますか? std :: functionへの変換によって?

"署名"に必要な引数の型と戻り値を明示し、オプションとしてnullptrを受け入れ、 "うまくいく"ように見えるので、私は自分のプロジェクトでこれを使い続けたいと思っていますこれらはC++の有名な最後の言葉です)。 stdする::機能」を

1つの選択肢は、(メンバ関数ポインタを含む)関数ポインタになります。しかし、std::functionはちょうどそんなによりよい(IMO)で、引数としての機能を通過させるための

+2

@Amadeus:多分、それは疑問符で終わる文章でしょうか。 SOには多くのil-formed質問があります。これは*一つではありません。 –

+3

このパラメータは、 'void(std :: future )'関数へのポインタに調整された関数型 'void(std :: future )'を持つと宣言されています。 –

答えて

11

ます機能するために生のポインタを使用している。

std::functionとは異なり、これはキャプチャラムダで動作する、またはstd::bindの結果ではないだろうか、operator()を実装する一般的なクラス型を持つ。

+0

ああ、私はそれが生のポインタに終わらないことを望んでいた。明らかにしていただきありがとうございます。制限事項は:キャプチャは多くの場合、ディール・ブレーカーであり、一貫性のために私はstd :: functionとstd :: bindに固執すると思います。私はこれを答えとしてマークします! –

1

オルタナティブ。

14

auto callback(std::future<int>) -> voidはと呼ばれるタイプvoid(std::future<int>)のエンティティの宣言です。引数としてリストされている場合、コンパイラはこれをポインタvoid(*)(std::future<int>)のポインタに調整します。

ラムダはステートレスなので、暗黙的に関数ポインタに変換できます。

あなたは非自明なキャプチャを追加すると、あなたのコードがコンパイルを停止します:

[argc](std::future<int> number) { 
    std::cout << argc << '\n'; 

を...今

、あなたの質問の内容を無視して、タイトルを見て...

std::functionは、ビュータイプではなく値タイプであるため、適度なコストがあります。値型として、実際にはその引数をコピーします。

あなたはstd::refに呼び出し元のオブジェクトをラップすることによってこの問題を回避することができますが、状態にしたい場合は、「私は周りに長いこの呼び出しよりも、この関数オブジェクトを保持していないだろう」、次のようにfunction_viewタイプを記述することができます。

template<class Sig> 
struct function_view; 

template<class R, class...Args> 
struct function_view<R(Args...)> { 
    void* ptr = nullptr; 
    R(*pf)(void*, Args...) = nullptr; 

    template<class F> 
    using pF = decltype(std::addressof(std::declval<F&>())); 

    template<class F> 
    void bind_to(F& f) { 
    ptr = (void*)std::addressof(f); 
    pf = [](void* ptr, Args... args)->R{ 
     return (*(pF<F>)ptr)(std::forward<Args>(args)...); 
    }; 
    } 
    // when binding to a function pointer 
    // even a not identical one, check for 
    // null. In addition, we can remove a 
    // layer of indirection and store the function 
    // pointer directly in the `void*`. 
    template<class R_in, class...Args_in> 
    void bind_to(R_in(*f)(Args_in...)) { 
    using F = decltype(f); 
    if (!f) return bind_to(nullptr); 
    ptr = (void*)f; 
    pf = [](void* ptr, Args... args)->R{ 
     return (F(ptr))(std::forward<Args>(args)...); 
    }; 
    } 
    // binding to nothing: 
    void bind_to(std::nullptr_t) { 
    ptr = nullptr; 
    pf = nullptr; 
    }  
    explicit operator bool()const{return pf;} 

    function_view()=default; 
    function_view(function_view const&)=default; 
    function_view& operator=(function_view const&)=default; 

    template<class F, 
    std::enable_if_t< !std::is_same<function_view, std::decay_t<F>>{}, int > =0, 
    std::enable_if_t< std::is_convertible< std::result_of_t< F&(Args...) >, R >{}, int> = 0 
    > 
    function_view(F&& f) { 
    bind_to(f); // not forward 
    } 

    function_view(std::nullptr_t) {} 

    R operator()(Args...args) const { 
     return pf(ptr, std::forward<Args>(args)...); 
    } 
}; 

live example

これは、より単純な種類の消去タイプであるstd::functionよりも有用であるため、それを上書きするには教育的かもしれません。

+0

ascheplerの答えを拡張していただきありがとうございます。コンパイラハンドリングの別の例 –

+1

s /変数/エンティティ/関数型の変数はありません。 –

関連する問題