简体   繁体   中英

Use of std::vector::emplace_back

I have some class-warper for std::thread. Here is the constructor:

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)...
        );
}

And then I'm using it so (example): InterruptibleThread(&SourceImageList::StartFrameProcessingStatic, this, std::ref(it))

The compiler builds this code successfuly. But now I would like to make a vector of such objects:

std::vector<InterruptibleThread> grp;

I would like to allocate it on the stack so what I'm doing is:

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

And I'm getting this error:

C2064   term does not evaluate to a function taking 0 arguments

Here are the options which are validated by the compiler:

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

But the first one is allocating an object on the heap so I need to release it manually, and the second one makes a copy of the object.

Can I use emplace_back here (compiler is MSVC 2015 update 3)?

Update

Ok, I made some fixes based on answers. Here is the final version of this class:

#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;
};

And example of use:

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

You could modernize your code and pass a lambda into InterruptibleThread instead of passing a function and it's arguments (ie bind-style).

#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();
}

My guess would be that the immediate problem is that the vector class is trying to instantiate the move constructor of your class, which happens to match the template signature but then fails to compile the body with Function = InterruptibleThread, Args = {} . You'll need to provide an override by explicitly defaulting the move constructor.

Aside from this, a couple other points:

  • As written, the constructor shouldn't work with pointer-to-member-functions and std::reference_wrapper arguments. (It didn't work under gcc, and I don't see how it could have worked in MSVC.)
  • It's not in the code sample, but it looks like _flag is a member variable of type atomic_bool . That would prevent a default move constructor from being able to be instantiated.

Here's a revised version which compiles for me under 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));
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM