2017-11-17 8 views
6

スレッドへの転送参照を渡すとの最初の具体的な機能を見てみましょう:ラッパークラスに

void boo1() { std::cout << "boo1\n"; } 
void boo2 (std::string) { std::cout << "boo2\n"; } 

struct X { 
    void boo3() { std::cout << "boo3\n"; } 
    void boo4 (std::string) { std::cout << "boo4\n"; } 
}; 

私はそれらの関数が例外保護(以下の説明を)行い、いくつかの「ガード」機能、を実行することにしたいです。

template <typename C, typename... Args> 
typename std::enable_if<!std::is_member_function_pointer<C>::value, void>::type 
foo (C&& c, Args&&... args) { 
    std::cout << "not memfun\n"; 
    c (std::forward<Args> (args)...); 
} 

template <typename C, typename T, typename... Args> 
typename std::enable_if<std::is_member_function_pointer<C>::value, void>::type 
foo (C&& c, T* o, Args&&... args) { 
    std::cout << "memfun\n"; 
    (o->*c) (std::forward<Args> (args)...); 
} 

そして今、私は新しいスレッドでboo1()boo2()X::boo3()X::boo4()を実行したいが、foo()関数で使用される「保護」と:だから、私は2つの機能を無料機能用と他のメンバ関数のために書かれています。明示的std::join()またはstd::detach()の使用を停止し、さらに一歩、私が書かれているスレッドのラッパークラスを行く、だから、

X x; 
std::thread th1 {&foo<void (*)()>, &boo1}; 
std::thread th2 {&foo<void (*) (std::string), std::string>, &boo2, "Hello"}; 
std::thread th3 {&foo<void (X::*)(), X>, &X::boo3, &x}; 
std::thread th4 {&foo<void (X::*) (std::string), X, std::string>, &X::boo4, &x, "Hello"}; 

:だから、何の問題もなく私はこれを行うことができます。 デストラクタが動作しているときにInstedが呼び出されます。この例の簡潔さのために、私はそれらのうちの1つだけを使用しました。 ので、コードは次のようになります。

class Th { 
public: 
    template <typename C, 
       typename = typename std::enable_if<!std::is_member_function_pointer<C>::value, void>::type, 
       typename... Args> 
    Th (C&& c, Args... args) // (X) 
    : t {foo<C, Args...>, std::forward<C> (c), std::forward<Args> (args)...} {} 

    template <typename C, 
       typename = typename std::enable_if<std::is_member_function_pointer<C>::value, void>::type, 
       typename T, 
       typename... Args> 
    Th (C&& c, T* o, Args... args) // (Y) 
    : t { foo<C, T, Args...>, std::forward<C> (c), o, std::forward<Args> (args)...} {} 


    ~Th() { t.join(); } 

private: 
    std::thread t; 
}; 

そして、この実装では問題、以下の使用例はありません。excepctedとして

X x; 
Th h1 {&boo1}; 
Th h2 {&boo2, "Hello"}; 
Th h3 {&X::boo3, &x}; 
Th h4 {&X::boo4, &x, "Hello"}; 

は仕事が。しかし、ここには一つの欠陥があります - 完全な転送はありません。私が望まれるArgs&&...(X)(Y)Args...に変更した場合は、 コードはいくつかのエラーでコンパイルできない、そのうちの一つは、次のとおりです。std::threadコンストラクタで行うことができない、私はそれを正しく理解すれば

error: no type named ‘type’ in ‘class std::result_of<void (*(void (*)(std::basic_string<char>), const char*))(void (*&&)(std::basic_string<char>), const char (&)[6])>’ 

std::bind()それは仕事です、または私が間違っている場合は私を修正します。

私の質問はこの場合、完璧な転送を利用するにはどうすればよいですか?

これをコンパイルするために使用されたコンパイラはGCC 4.8.1でした。

約束の説明:私はtrycatchc()への呼び出しを囲むfoo()関数は例外がロギングメカニズムのいくつかの種類では、プロセスの情報をcatchedされていることを確認し、透明性を必要に応じて例外を再スローしたいですThクラスのユーザーに送信します。

答えて

4

左辺参照をstd::threadに渡す場合は、std::reference_wrapperにラップする必要があります。

namespace detail 
{ 

template<typename T> 
std::reference_wrapper<T> forward_to_thread(T& t, std::true_type) { 
    return std::ref(t); 
} 

template<typename T> 
T&& forward_to_thread(T&& t, std::false_type) { 
    return std::move(t); 
} 

} 

template<typename T> 
auto forward_to_thread(T&& t) 
    -> decltype(detail::forward_to_thread(std::forward<T>(t), std::is_lvalue_reference<T>{})) 
{ 
    return detail::forward_to_thread(std::forward<T>(t), std::is_lvalue_reference<T>{}); 
} 

は次に、あなただけのthread初期設定でforward_to_threadからstd::forwardの呼び出しに置き換えます。たとえば、次の方法でそれを行うことができます

class Th { 
public: 
    template <typename C, 
       typename = typename std::enable_if<!std::is_member_function_pointer<C>::value, void>::type, 
       typename... Args> 
    Th (C&& c, Args&&... args) // (X) 
    : t {foo<C, Args...>, std::forward<C> (c), forward_to_thread<Args> (std::forward<Args>(args))...} {} 

    template <typename C, 
       typename = typename std::enable_if<std::is_member_function_pointer<C>::value, void>::type, 
       typename T, 
       typename... Args> 
    Th (C&& c, T* o, Args&&... args) // (Y) 
    : t { foo<C, T, Args...>, std::forward<C> (c), o, forward_to_thread<Args> (std::forward<Args>(args))...} {} 


    ~Th() { t.join(); } 

private: 
    std::thread t; 
}; 

をこれは、GCC 4.8で動作しません。2 [link]

さらに新しいコンパイラを使用できる場合は、より多くのコプロセッサと読み取り可能なコード(this)にはstd::invoke(C++ 17)を使用することを検討してください。

+0

興味深い。これは、 'h2'と' h4'の '' Hello "'ではなく 'std :: string(" Hello ")'で実行するとコンパイルに失敗します。このエラーは、第1引数auto forward_to_thread(T && t) '(' clang 5.0')に対して 'std :: __ cxx11 :: basic_string 'から 'std :: __ cxx11 :: basic_string &&'への 'no known conversion from' – cantordust

+1

'forward_to_thread'の引数も転送され、固定されるべきです。ありがとう。 – krzaq

+0

これはクールです、ありがとう –

関連する問題