2016-10-17 7 views
0

std::threadのラッパーを実装しています。スレッドの実行が終了した後に任意の戻り値を取得できます。私はC++11を使用していますが、私は、std::promisestd::packaged_task、および多くのstlスレッド機能(私はstd::thread以上を取得できません)を使用することができないことを意味する、古いARMアーキテクチャを使用しています。 )。私はgcc 4.8.4でテストしています。variadicテンプレートパラメータでstd :: bindを使用できません

私の実装では、this bugが発生しました。これは、ラムダで可変のテンプレートパラメータを取得することができません。残念ながら、コンパイラを4.9にアップグレードすることはできません。

std::bindを使用して回避策を実装しようとしていますが、かなりの問題があります。私はこれらがコンパイラのバグか実装エラーであるかどうかは分かりません。ここではソースです:

#include <iostream> 
#include <memory> 
#include <thread> 

#include <unistd.h> 
#include <pthread.h> 

class ConcurrentTaskBase 
{ 
public: 
    ConcurrentTaskBase(int priority, const std::function<void()>& runTask) 
     : m_thread(), 
     m_active(true) 
    { 
     auto wrap = [this](int priority, const std::function<void()>& runTask) 
     { 
     //Unrelated pthread stuff that I commented out 
//   sched_param param{priority}; 
// 
//   int err = pthread_setschedparam(pthread_self(), SCHED_RR, &param); 
//   if (err) 
//   cout << "failed to set new priority: " << err << endl; 

     runTask(); 
     }; 
     m_thread = std::thread(wrap, priority, runTask); 
    } 

    virtual ~ConcurrentTaskBase(void) 
    { 
     waitForCompletion(); 
    } 

    void waitForCompletion(void) 
    { 
     if (m_active) 
     { 
     m_thread.join(); 
     m_active = false; 
     } 
    } 

private: 
    std::thread m_thread; 
    bool m_active; 
}; 

template<class R, class... ArgTypes> 
class ConcurrentTask; 

template<class R, class... ArgTypes> 
class ConcurrentTask<R(ArgTypes...)> : public ConcurrentTaskBase 
{ 
public: 
    ConcurrentTask(int priority, const std::function<R(ArgTypes...)>& task, ArgTypes&&... args) 
     : ConcurrentTaskBase(priority, bindTask(task, std::forward<ArgTypes>(args)...)) 
    {} 

    std::shared_ptr<R> getReturn(void) noexcept 
    { 
     waitForCompletion(); 
     return m_storage; 
    }; 

private: 
    static std::function<void(void)> bindTask(const std::function<R(ArgTypes...)>& task, ArgTypes&&... args) 
    { 
     auto action = [task](ArgTypes&&... args) -> void 
     { 
     //Eventually m_storage = std::make_shared<R>(task(std::forward<ArgTypes>(args)...)); after bugs are fixed 
     task(std::forward<ArgTypes>(args)...); 
     return; 
     }; 
     std::function<void(void)> bound = std::bind(action, std::forward<ArgTypes>(args)...); 
     return bound; 
    }; 

    std::shared_ptr<R> m_storage; 
}; 

int testFunction(int val) 
{ 
    std::cout << "Was given " << val << std::endl; 
    return val + 10; 
} 

int main() 
{ 
    ConcurrentTask<int(int)> task(20, testFunction, 5); 
// shared_ptr<int> received = task.getReturn(); 
// testFunction(*received); 
    return 0; 
} 

そしてここでは、私のコンパイラの出力です:

16:31:00 **** Incremental Build of configuration Debug for project TestLinuxMint **** 
make all 
Building file: ../src/TestLinuxMint.cpp 
Invoking: GCC C++ Compiler 
g++ -std=c++0x -O0 -g3 -Wall -pthread -c -fmessage-length=0 -MMD -MP -MF"src/TestLinuxMint.d" -MT"src/TestLinuxMint.o" -o "src/TestLinuxMint.o" "../src/TestLinuxMint.cpp" 
../src/TestLinuxMint.cpp: In instantiation of ‘static std::function<void()> ConcurrentTask<R(ArgTypes ...)>::bindTask(const std::function<_Res(_ArgTypes ...)>&, ArgTypes&& ...) [with R = int; ArgTypes = {int}]’: 
../src/TestLinuxMint.cpp:58:84: required from ‘ConcurrentTask<R(ArgTypes ...)>::ConcurrentTask(int, const std::function<_Res(_ArgTypes ...)>&, ArgTypes&& ...) [with R = int; ArgTypes = {int}]’ 
../src/TestLinuxMint.cpp:91:53: required from here 
../src/TestLinuxMint.cpp:76:90: error: conversion from ‘std::_Bind_helper<false, ConcurrentTask<R(ArgTypes ...)>::bindTask(const std::function<_Res(_ArgTypes ...)>&, ArgTypes&& ...) [with R = int; ArgTypes = {int}]::__lambda1&, int>::type {aka std::_Bind<ConcurrentTask<R(ArgTypes ...)>::bindTask(const std::function<_Res(_ArgTypes ...)>&, ArgTypes&& ...) [with R = int; ArgTypes = {int}]::__lambda1(int)>}’ to non-scalar type ‘std::function<void()>’ requested 
     std::function<void(void)> bound = std::bind(action, std::forward<ArgTypes>(args)...); 
                         ^
make: *** [src/TestLinuxMint.o] Error 1 

16:31:01 Build Finished (took 319ms) 

問題がstd::bind(*) to std::function<void(void)>から失敗した変換があるline 76、上のようです。このコードはまだ開発中ですが、私はこの問題を乗り越えて前進する必要があります。私はここで複数の他の投稿を見てきましたが、それらのすべては問題なしにバリデーションテンプレートパラメータにstd :: bindを使用できるようです。

SOLUTIONここ

私はkzraqのおかげとthis post思い付いた(この質問に関連するような)最終的な解決策があります。

出典:推測として

#include <iostream> 
#include <memory> 
#include <utility> 
#include <vector> 
#include <thread> 
#include <type_traits> 
#include <typeinfo> 
#include <tuple> 
#include <memory> 

//------------------------------------------------------------------------------------------------------------ 
template <std::size_t... Ints> 
struct idx_sequence 
{ 
    using type = idx_sequence; 
    using value_type = std::size_t; 
    static constexpr std::size_t size() noexcept { return sizeof...(Ints); } 
}; 

//------------------------------------------------------------------------------------------------------------ 
template <class Sequence1, class Sequence2> 
struct _merge_and_renumber; 

template <std::size_t... I1, std::size_t... I2> 
struct _merge_and_renumber<idx_sequence<I1...>, idx_sequence<I2...> > 
    : idx_sequence<I1..., (sizeof...(I1)+I2)...> 
{ 
}; 

//------------------------------------------------------------------------------------------------------------ 
template <std::size_t N> 
struct make_idx_sequence : _merge_and_renumber<make_idx_sequence<N/2>, make_idx_sequence<N - N/2> > 
{ 
}; 
template<> struct make_idx_sequence<0> : idx_sequence<> { }; 
template<> struct make_idx_sequence<1> : idx_sequence<0> { }; 

//------------------------------------------------------------------------------------------------------------ 
template<typename Func, typename Tuple, std::size_t... Ints> 
auto applyImpl(Func&& f, Tuple&& params, idx_sequence<Ints...>) 
    -> decltype(f(std::get<Ints>(std::forward<Tuple>(params))...)) 
{ 
    return f(std::get<Ints>(std::forward<Tuple>(params))...); 
}; 

template<typename Func, typename Tuple> 
auto apply(Func&& f, Tuple&& params) 
    -> decltype(applyImpl(std::forward<Func>(f), 
       std::forward<Tuple>(params), 
       make_idx_sequence<std::tuple_size<typename std::decay<Tuple>::type>::value>{})) 
{ 
    return applyImpl(std::forward<Func>(f), 
        std::forward<Tuple>(params), 
        make_idx_sequence<std::tuple_size<typename std::decay<Tuple>::type>::value>{}); 
}; 

class ConcurrentTaskBase 
{ 
public: 
    ConcurrentTaskBase(int priority, const std::function<void()>& task) 
     : m_thread(), 
      m_active(true) 
    { 
     auto wrap = [this](int priority, const std::function<void()>& task) 
     { 
      //Unrelated pthread stuff that I commented out 
      sched_param param{priority}; 

      int err = pthread_setschedparam(pthread_self(), SCHED_RR, &param); 
      if (err) 
       std::cout << "failed to set new priority: " << err << std::endl; 

      task(); 
     }; 
     m_thread = std::thread(wrap, priority, task); 
    } 

    virtual ~ConcurrentTaskBase(void) 
    { 
     waitForCompletion(); 
    } 

    void waitForCompletion(void) 
    { 
     if (m_active) 
     { 
      m_thread.join(); 
      m_active = false; 
     } 
    } 

private: 
    std::thread m_thread; 
    bool m_active; 
}; 

template<class R, class... ArgTypes> 
class ConcurrentTask; 

template<class R, class... ArgTypes> 
class ConcurrentTask<R(ArgTypes...)> : public ConcurrentTaskBase 
{ 
public: 
    ConcurrentTask(int priority, const std::function<R(ArgTypes...)>& task, ArgTypes&&... args) 
    : ConcurrentTaskBase(priority, bindTask(task, std::forward<ArgTypes>(args)...)) 
    {} 

    std::shared_ptr<R> getReturn(void) noexcept 
    { 
     waitForCompletion(); 
     return m_storage; 
    } 

private: 
    std::function<void(void)> bindTask(const std::function<R(ArgTypes...)>& task, ArgTypes&&... args) 
    { 
     auto params = std::make_tuple(args...); 
     return [this, task, params](){m_storage = std::make_shared<R>(apply(task, params));}; 
    }; 
    std::shared_ptr<R> m_storage; 
}; 

template<class... ArgTypes> 
class ConcurrentTask<void(ArgTypes...)> : public ConcurrentTaskBase 
{ 
public: 
    ConcurrentTask(int priority, const std::function<void(ArgTypes...)>& task, ArgTypes&&... args) 
     : ConcurrentTaskBase(priority, bindTask(task, std::forward<ArgTypes>(args)...)) 
    {} 

private: 
    std::function<void(void)> bindTask(const std::function<void(ArgTypes...)>& task, ArgTypes&&... args) 
    { 
     auto params = std::make_tuple(args...); 
     return [this, task, params](){apply(task, params);}; 
    }; 
}; 

// Example stuff 
struct MyStruct 
{ 
    int x; 
    int y; 
}; 
int testFunction(MyStruct val) 
{ 
    std::cout << "X is " << val.x << " Y is " << val.y << std::endl; 
    return val.x + 10; 
} 

void printMe(int x) 
{ 
    std::cout << "Printing " << x << std::endl; 
} 
int main() 
{ 
    ConcurrentTask<int(MyStruct)> task(20, testFunction, {5, -21}); 
    std::shared_ptr<int> received = task.getReturn(); 
    std::cout << "Return value is " << *received << std::endl; 

    ConcurrentTask<void(int)> voidTask(25, printMe, -123); 
    return 0; 
} 
+0

CPUアーキテクチャは 'std :: future'とcoの存在に影響してはなりません。ハードウェアのサポートがない場合、標準ライブラリはロックなどを使用してそれをエミュレートするはずです....... – Vality

+0

正しいですが、ロックを使用できます。しかし、私が使用するC++ライブラリでは、 '__GCC_ATOMIC_INT_LOCK_FREE'が1より大きい必要があります(すべてのintがロックフリーであることが保証されています)。 'echo |コマンドを実行する。 arm-linux-g ++ -dM -E - | grep -i atomic'は1の値を返します。これは私のアーキテクチャでは、すべてのint型が完全に原子ではないことを示しています。私の選択肢は、ライブラリの実装を変更することです(ライブラリのサイズが大きくならないようにする必要があります)、プロセッサを変更したり(オプションではありません)、問題を解決するための小さなラッパーを作成します。 –

+0

@zeus_masta_funk別の基本クラスが必要ですか? – krzaq

答えて

2

これは多かれ少なかれヤックが書いたことです。たぶん私はあなたの考えをよく理解していないかもしれませんが、私にはあなたはそれを強調しており、あなたは早すぎるstd::functionを使っているようです。また、ArgTypes&&は、bindTaskで推測されないため、転送/ユニバーサル参照のリストにはなりません。

gcc4.8の次のcompiles successfully。2:

C++用に独自のinteger_sequenceを取得してください。礼儀Xeo

書き込みapplyが(多分これが改善される可能性)機能にタプルパラメータを適用するには:簡素化

template<typename Func, typename Tuple, unsigned int... is> 
auto apply_impl(Func&& f, Tuple&& params, seq<is...>) 
    // -> decltype(f(std::get<is>(std::forward<Tuple>(params))...)) // C++11 only 
{ 
    using std::get; // enable ADL-lookup for get in C++14 
    return f(get<is>(std::forward<Tuple>(params))...); 
} 

template<typename Func, typename Tuple> 
auto apply(Func&& f, Tuple&& params) 
    // -> decltype(apply_impl(std::forward<Func>(f), std::forward<Tuple>(params), 
    // GenSeq<std::tuple_size<typename std::decay<Tuple>::type>::value>{})) 
    // C++11 only 
{ 
    return apply_impl(std::forward<Func>(f), std::forward<Tuple>(params), 
     GenSeq<std::tuple_size<typename std::decay<Tuple>::type>::value>{}); 
} 

あなた(この時点で私はテンプレートとしてそれを維持するだろうが)bindTask

auto params = make_tuple(args...); 
std::function<void(void)> bound = [task,params]{ apply(task, params); }; 
return bound; 

C++ 14 do [task=std::move(task),params=std::move(params)]で、不要なコピーを避けることができます。

+0

助けてくれてありがとう!私はいくつか質問があります。私は 'gen_seq'構造を理解していません。私は 'Concat <*>'から継承し、テストを適用しました。 'gen_seq <4>'を使って、 'Concat 、GenSeq <2>>'を継承しています。それらのそれぞれは 'Concat 、GenSeq <1> 'から再帰的に' GenSeq <1> '(または0)まで継承し、結局' seq <> 'を継承します。なぜこの木構造が必要ですか? –

+0

次に、 'struct seq'は何を成し遂げますか?' type'メンバを自分自身に設定するだけですか?一見してもそれほど有用ではないようです。再度、助けをありがとう。 –

+0

これはC++ 11の「index_sequence」(http://en.cppreference.com/w/cpp/utility/integer_sequence)です。これは 'GenSeq 'のバリデーションテンプレートインスタンス化 'seq <0,1,2 .... n-1>'を生成します。これにより 'get '関数テンプレートでタプルを解凍し、 'apply'が関数にパラメータを渡せるようにします。 – krzaq

2

、バインドはとても右辺値としてその結合機能に右辺値パラメータに右辺値パラメータをオンにしない、(!左辺値コンテキストで呼び出さESPとき)を繰り返し呼び出すことができ前提パラメーター。あなたのコードが要求するもの。そのラムダは完璧なフォワーディングではありません!

あなたはまた、ぶら下がり参照地獄を招待するlambdasの参照によってconst&std::functionを参照して取り込みます。しかし、それは実行時の問題です。一般的な規則として、&は、ラムダとすべてのコピーの有効期間が現在のスコープで終了しない限りキャプチャしません。プロトタイプ作成中に間違いなく「特定の」問題が発生しなくてもそれをしないでください。

私はstd::applyindex_sequence秒の弱いバージョンを書いた後、呼び出し可能なターゲットに展開するために、あなたのapplyをやっtupleに引数を梱包検討します。しかしそれは理想的ならば偏見です。

+0

アドバイスをいただき、ありがとうございます。 –

関連する問題