2016-09-04 2 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::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))); 

Но первый из них выделяет объект в куче, поэтому мне нужно отпустить его вручную, а второй - копию объекта.

Могу ли я использовать emplace_back здесь (компилятор MSVC 2015 update 3)?

Update

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

Вторая версия 'push_back', которую вы предоставили, должна фактически« перемещать »« ПрерываниеТребра »при возврате назад к вектору, а не в создании копии - компилятор почти наверняка будет использовать эту перегрузку. – 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

Смежные вопросы