2016-10-17 4 views
0

std::thread에 대한 래퍼를 구현 중이며 스레드 실행이 끝난 후 임의의 반환 값을 검색 할 수 있습니다. C++11을 사용하는 동안, 나는, std::promise, std::packaged_task 및 많은 스레딩 기능을 사용할 수 없다는 것을 의미하는 완전 지원 원자 int를 지원하지 않는 이전 ARM 아키텍처를 사용하고 있습니다 (적어도 std::thread을 얻습니다.)). 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; 
} 

그리고 내 컴파일러 출력됩니다 : std::bind(*) to std::function<void(void)>에서 실패한 변환이있는 곳에

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) 

문제는, line 76에있을 것 같다 여기 소스입니다. 이 코드는 아직 개발 중이지만 앞으로이 문제를 해결해야합니다. 그래서 여기에 여러 개의 다른 게시물을 보았지만, 그들 모두는 문제없이 variadic 템플릿 매개 변수에 std :: bind를 사용할 수있는 것 같습니다.

솔루션 여기

내가 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에서 추론되지 않습니다.

gcc 4.8에서 다음과 같은 compiles successfully.2 :

C++ 11에 대해 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>'에서 재귀 적으로 계승되며 결국에는'seq <> '를 상속받은'GenSeq <1>'(또는 0)으로 끝납니다. 이 트리 구조가 필요한 이유는 무엇입니까? –

+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

, 바인드 그렇게를 rvalue로 바인드 함수를 rvalue 매개 변수로를 rvalue 매개 변수를 설정하지 않습니다, 그것은 반복적으로 호출 (특히 좌변 컨텍스트에서 호출 할 때!) 할 수 추정 매개 변수. 어떤 코드가 요구합니다. 그 람다는 완벽한 포워딩이 아닙니다!

당신은 또한 매달려있는 참조 hell을 초대 한 lambdas의 참조로 const&std::function을 참조하여 캡처 할 수 있습니다. 하지만 그것은 런타임 문제입니다. 일반적으로 람다 및 모든 복사본의 수명이 현재 범위에서 끝나지 않는 한 &을 캡처하지 않습니다. "확실한"문제가 아닐지라도 프로토 타이핑 중에는 절대하지 마십시오.

나는 std::applyindex_sequence s의 약한 버전을 기록한 다음, 호출 대상으로 압축하기 위해 apply를하고 tuple에 인수를 포장 고려할 것입니다. 그러나 이상적이라면 그건 편견입니다.

+0

조언을 주셔서 감사합니다. 나는이 아이디어를 따를 가능성이 높습니다. –

관련 문제