2016-09-04 26 views
1

私はstd :: thread用のいくつかのクラスワーパを持っています。ここではコンストラクタは次のとおりです。std :: vector :: emplace_backの使用

template <typename Function, typename... Args> 
InterruptibleThread(Function&& fun, Args&&... args) 
{ 
    _thread = std::thread([](std::atomic_bool * f, Function&& function, Args&&... arguments) 
    { 
     _flag_ref = f; 
     (function)(std::forward<Args>(arguments)...); 
    }, 
     &_flag, 
     std::forward<Function>(fun) 
     , std::forward<Args>(args)... 
     ); 
} 

そして私は(例)ので、それを使用しています: InterruptibleThread(& SourceImageList :: StartFrameProcessingStatic、この、のstd :: refの(それ))

コンパイラのビルドこのコードは成功しています。しかし、今、私はそのようなオブジェクトのベクトルを作りたいと思います:

std::vector<InterruptibleThread> grp; 

私は私がやっているので、何をスタックに割り当てるしたいです:

grp.emplace_back(&SourceImageList::StartFrameProcessingStatic, this, std::ref(it)); 

そして、私はこれを取得していますエラー:ここ

C2064 term does not evaluate to a function taking 0 arguments 

は、コンパイラによって検証されているオプションは次のとおりです。

1) grp.push_back(new InterruptibleThread(&SourceImageList::StartFrameProcessingStatic, this, std::ref(it))); 
2) grp.push_back(InterruptibleThread(&SourceImageList::StartFrameProcessingStatic, this, std::ref(it))); 

しかし、最初のものはヒープ上にオブジェクトを割り当てているので、手動で解放する必要があり、2番目のものはオブジェクトのコピーを作成する必要があります。

ここでemplace_back(コンパイラはMSVC 2015アップデート3)を使用できますか?

更新

[OK]を、私は、回答に基づいて、いくつかの修正をしました。使用の

#pragma once 
#include <exception> 
#include <atomic> 
#include <thread> 
#include <future> 
#include <windows.h> 
// Synopsis 
class InterruptThreadException; 
class InterruptibleThread; 

// Interrupt exception 
class InterruptThreadException : public virtual std::exception { 
public: 
    virtual char const* what() const override { return "interrupt"; } 
}; // class InterruptThreadException 

    // Interruptible thread 
class InterruptibleThread { 
public: 
static void InterruptionPoint() noexcept(false) { 
    if (!InterruptibleThread::_flag_ref) { return; } 
    if (!InterruptibleThread::_flag_ref->load()) { return; } 

    throw InterruptThreadException(); 
} // check_for_interrupt 

template <typename Function> 
InterruptibleThread(Function&& fun) : 
    _thread([this, fun = std::move(std::forward<Function>(fun))] 
{ 
    _flag_ref = _flag.get(); 
    fun(); 
}) 
{} 

InterruptibleThread(InterruptibleThread&&) = default; 
InterruptibleThread(const InterruptibleThread&) = delete; 

bool Interrupting() const { return _flag->load(); } 

void Interrupt() { _flag->store(true); } 

void Join() 
{ 
    _thread.join(); 
} 

bool TimedJoin(int msec) 
{ 
    return (std::async([=]() {Join(); }).wait_for(std::chrono::milliseconds(msec)) != std::future_status::timeout); 
} 

bool Joinable() 
{ 
    return _thread.joinable(); 
} 

void Terminate() 
{ 
    TerminateThread(_thread.native_handle(), -1); 
} 

~InterruptibleThread() 
{ 
    if (_flag.get() != nullptr) 
    { 
     *_flag = false; 
     Interrupt(); 
    } 
    if (_thread.joinable()) 
     _thread.join() 
} 

private: 
    static thread_local std::atomic_bool* _flag_ref; 
    std::unique_ptr<std::atomic_bool> _flag = std::make_unique<std::atomic_bool>(); 
    std::thread _thread; 
}; 

そして例:

std::vector<InterruptibleThread> grp; 
for (auto it : _sourceImages) 
    grp.emplace_back([this, it] { 
    it->StartFrameProcessing(); 
    it->SetImageDelay(const_cast<EngineConfig*>(GetConfig())->ImageDelay); 
}); 
+2

"私はここにemplace_back使用することはできますが、" - あなたは* *てみたのですか?実際にはあなたの質問に答えるだけです。 –

+1

2番目の 'push_back'バージョンは、コピーを作成するのではなくベクトルにプッシュバックするときに、実際に' InterruptibleThread'を '移動 'する必要があります。コンパイラはほぼ確実にこのオーバーロードを使います。 – ArchbishopOfBanterbury

+1

@ArchbishopOfBanterbury C++ 11では、 'push_back'にはrvalueのオーバーロードがあるので、コンパイラはそれを確実に移動します(つまり、コンパイラは追加の最適化を行いません)http://en.cppreference.com/w/cpp/container/ vector/push_back – vsoftco

答えて

4

あなたはあなたのコードを近代化し、代わりに機能を渡すInterruptibleThreadにラムダを渡すことができ、それの引数(つまりバインドここでは、最終的なこのクラスのバージョンがあります-スタイル)。

#include <atomic> 
#include <iostream> 
#include <thread> 
#include <vector> 

struct InterruptibleThread 
{ 
    std::thread _thread; 

    template <typename Function> 
    InterruptibleThread(Function&& fun) 
     : _thread(std::forward<Function>(fun)) 
    { 
    } 
}; 

struct Test 
{ 
    std::vector<InterruptibleThread> grp; 
    void test(int x) { 
     grp.emplace_back([this, x]{ t1(x); }); // <==== HERE 
    } 
    void t1(int x) { 
     std::cout << x << "\n"; 
    } 
}; 

int main() 
{ 
    Test t; 
    t.test(5); 
    t.grp[0]._thread.join(); 
} 
+0

ニース。天才は簡単です。ありがとう! – snk

2

私の推測では、当面の問題は、ベクトルクラスはテンプレートの署名を一致させるために起こるが、その後Function = InterruptibleThread, Args = {}で体をコンパイルに失敗し、あなたのクラスの移動のコンストラクタをインスタンス化しようとしていることであるということでしょう。移動コンストラクタを明示的にデフォルトにすることによって、オーバーライドを提供する必要があります。これとは別に

、カップル他の点:

  • 書き込まれるように、コンストラクタは、ポインタ・ツー・メンバ関数とstd::reference_wrapper引数で動作してはなりません。 (gccの下では動作しませんでしたが、MSVCでどのように動作するのかわかりません)
  • コードサンプルにはありませんが、_flagはタイプatomic_boolのメンバ変数です。これは、デフォルトの移動コンストラクターをインスタンス化できないようにします。

ここではgccの下で私のためにコンパイル改訂版です:

#include <thread> 
#include <mutex> 
#include <atomic> 
#include <vector> 
#include <utility> 

thread_local std::atomic_bool* _flag_ref; 

class InterruptibleThread { 
private: 
    std::thread _thread; 

    // Need unique_ptr instead of a directly contained atomic_bool 
    // to make the object MoveConstructible. 
    std::unique_ptr<std::atomic_bool> _flag; 

public: 
    template <typename Function, typename... Args> 
    explicit InterruptibleThread(Function&& fun, Args&&... args) 
    { 
    _flag = std::make_unique<std::atomic_bool>(); 

    // Use std::bind to take care of all the details of 
    // calling pointer-to-member-function or pointer-to-member-variable, 
    // unwrapping std::reference_wrapper arguments, etc. 
    auto bound_fun = std::bind(std::forward<Function>(fun), std::forward<Args>(args)...); 

    _thread = std::thread([this, bound_fun = std::move(bound_fun)] 
    { 
     _flag_ref = _flag.get(); 
     bound_fun(); 
    } 
     ); 
    } 

    InterruptibleThread(InterruptibleThread&&) = default; 
    InterruptibleThread(const InterruptibleThread&) = delete; 
}; 

class Foo { 
public: 
    void func(int& n); 
}; 

void test_func() { 
    std::vector<InterruptibleThread> v; 
    Foo f; 
    int n = 5; 
    v.emplace_back(&Foo::func, &f, std::ref(n)); 
} 
+0

これはコンパイル可能なように見えます。私は@RustyXのソリューションも好きです。コードをもっとシンプルにすることができます。ありがとう! – snk

関連する問題