Reputation: 1613
I'm sorry if I'm getting the whole concept wrong, but I'm trying to make a tuple the container of the actual objects, where only with its destruction those objects will go out of scope.
I currently have this:
class MiniThread {
public:
~MiniThread() {
if (m_thread) {
if (m_thread->joinable())
m_thread->join();
delete m_thread;
}
}
void join()
{
if (m_thread == nullptr)
return;
m_thread->join();
m_thread = nullptr;
}
template<typename F, typename... Args>
void run(F func, Args... args)
{
if (m_thread != nullptr)
join();
auto tuple = std::forward_as_tuple(args...);
m_thread = new std::thread([=]() {
__try
{
std::apply(func, tuple);
}
__except (CrashDump::GenerateDump(GetExceptionInformation()))
{
// TODO: log.
exit(1);
}
});
m_started = true;
}
bool started() const { return m_started; }
private:
std::thread *m_thread = nullptr;
bool m_started = false;
};
std::string getString()
{
return std::string("sono");
}
int main()
{
auto test = [&](std::string seila, const std::string& po, std::promise<int>* p)
{
std::cout << seila.c_str() << std::endl;
std::cout << po.c_str() << std::endl;
p->set_value(10);
};
std::promise<int> p;
std::future<int> f;
MiniThread thread;
std::string hello = "hello";
std::string seilapo = "seilapo";
f = p.get_future();
thread.run(test, getString(), "how are you", &p);
thread.join();
int ftest = f.get();
std::cout << ftest << std::endl;
}
By the time the thread is ran, the args
are no longer reliable. They have been destructed already. So I was wondering if is there a way to copy them in the call of the thread by value. I have made some attempts of moving the variadic arguments into tuples, but tuples are always rendered with rvalues
and fail all the same.
Upvotes: 1
Views: 78
Reputation: 303527
This:
auto tuple = std::forward_as_tuple(args...);
Creates a tuple of references into args...
That's what forward_as_tuple
's job is. You're then capturing that tuple of references by value:
m_thread = new std::thread([=]{ /* ... */ });
So once your arguments go out of scope, you're only holding onto references to them... and that'll dangle.
But you don't actually... need to have a tuple at all. Just copy the arguments themselves:
m_thread = std::thread([=]() {
func(args...); // func and args, no tuple here
});
Also don't write new thread
- thread
is already a handle type, just create one.
The above copies the arguments. If you want to move them, then in C++17, yes you'll need to have a tuple
and use std::apply
. But not forward_as_tuple
... just make_tuple
:
m_thread = std::thread([func, args=std::make_tuple(std::move(args)...)]() mutable {
std::apply(func, std::move(args));
});
In C++20, you won't need the tuple
again, and can write a pack-expansion:
m_thread = std::thread([func, ...args=std::move(args)]() mutable {
func(std::move(args)...);
});
Upvotes: 6