I'm trying to subclass std::thread such that a member function of the subclass is executed on the new thread before the caller's passed-in function. Something like the following invalid code:
#include <thread>
#include <utility>
class MyThread : public std::thread {
template<class Func, class... Args>
void start(Func&& func, Args&&... args) {
... // Useful, thread-specific action
func(args...);
}
public:
template<class Func, class... Args>
MyThread(Func&& func, Args&&... args)
: std::thread{[=]{start(std::forward<Func>(func),
std::forward<Args>(args)...);}} {
}
};
g++ -std=c++11 has the following issue with the above code:
MyThread.h: In lambda function:
MyThread.h:ii:jj: error: parameter packs not expanded with '...':
std::forward<Args>(args)...);}}
^
I've tried a dozen different variations in the initializer-list to no avail.
How can I do what I want?
The biggest difficulty I had when I did this before was getting all the behavior of std::thread. It's constructor can take not only a pointer to a free function, but also a class method pointer and then a object of the class as the first argument. There are a number of variations on that: class methods, class function object data members, an object of the class type vs a pointer to an object, etc.
This is for C++14:
class MyThread : public std::thread {
void prolog() const { std::cout << "prolog\n"; }
public:
template <typename... ArgTypes>
MyThread(ArgTypes&&... args) :
std::thread(
[this, bfunc = std::bind(std::forward<ArgTypes>(args)...)]
() mutable {
prolog();
bfunc();
})
{ }
};
If you put the prolog code inside the lambda and it doesn't call class methods, then the capture of this is not needed.
For C++11, a small change is needed because of the lack of capture initializers, so the bind must be passed as an argument to std::thread:
std::thread(
[this]
(decltype(std::bind(std::forward<ArgTypes>(args)...))&& bfunc) mutable {
prolog();
bfunc();
}, std::bind(std::forward<ArgTypes>(args)...))
Here's a test program that also exercises the class member form of std::thread:
int main()
{
auto x = MyThread([](){ std::cout << "lambda\n"; });
x.join();
struct mystruct {
void func() { std::cout << "mystruct::func\n"; }
} obj;
auto y = MyThread(&mystruct::func, &obj);
y.join();
return 0;
}
I haven't checked, but I'm a bit worried that the capture of this, also seen in other solutions, is not safe in some cases. Consider when the object is an rvalue that is moved, as in std::thread t = MyThread(args). I think the MyThread object will go away before the thread it has created is necessarily finished using it. The "thread" will be moved into a new object and still be running, but the captured this pointer will point to a now stale object.
I think you need to insure your constructor does not return until your new thread is finished using all references or pointers to the class or class members. Capture by value, when possible, would help. Or perhaps prolog() could be a static class method.
This should do it (c++11 and c++14 solutions provided):
C++14
#include <thread>
#include <utility>
#include <tuple>
class MyThread : public std::thread {
template<class Func, class ArgTuple, std::size_t...Is>
void start(Func&& func, ArgTuple&& args, std::index_sequence<Is...>) {
// Useful, thread-specific action
func(std::get<Is>(std::forward<ArgTuple>(args))...);
}
public:
template<class Func, class... Args>
MyThread(Func&& func, Args&&... args)
: std::thread
{
[this,
func = std::forward<Func>(func),
args = std::make_tuple(std::forward<Args>(args)...)] () mutable
{
using tuple_type = std::decay_t<decltype(args)>;
constexpr auto size = std::tuple_size<tuple_type>::value;
this->start(func, std::move(args), std::make_index_sequence<size>());
}
}
{
}
};
int main()
{
auto x = MyThread([]{});
}
In C++17 it's trivial:
#include <thread>
#include <utility>
#include <tuple>
#include <iostream>
class MyThread : public std::thread {
public:
template<class Func, class... Args>
MyThread(Func&& func, Args&&... args)
: std::thread
{
[this,
func = std::forward<Func>(func),
args = std::make_tuple(std::forward<Args>(args)...)] () mutable
{
std::cout << "execute prolog here" << std::endl;
std::apply(func, std::move(args));
std::cout << "execute epilogue here" << std::endl;
}
}
{
}
};
int main()
{
auto x = MyThread([](int i){
std::cout << i << std::endl;
}, 6);
x.join();
}
C++11 (we have to facilitate moving objects into the mutable lambda, and provide the missing std::index_sequence):
#include <thread>
#include <utility>
#include <tuple>
namespace notstd
{
using namespace std;
template<class T, T... Ints> struct integer_sequence
{};
template<class S> struct next_integer_sequence;
template<class T, T... Ints> struct next_integer_sequence<integer_sequence<T, Ints...>>
{
using type = integer_sequence<T, Ints..., sizeof...(Ints)>;
};
template<class T, T I, T N> struct make_int_seq_impl;
template<class T, T N>
using make_integer_sequence = typename make_int_seq_impl<T, 0, N>::type;
template<class T, T I, T N> struct make_int_seq_impl
{
using type = typename next_integer_sequence<
typename make_int_seq_impl<T, I+1, N>::type>::type;
};
template<class T, T N> struct make_int_seq_impl<T, N, N>
{
using type = integer_sequence<T>;
};
template<std::size_t... Ints>
using index_sequence = integer_sequence<std::size_t, Ints...>;
template<std::size_t N>
using make_index_sequence = make_integer_sequence<std::size_t, N>;
}
template<class T>
struct mover
{
mover(T const& value) : value_(value) {}
mover(T&& value) : value_(std::move(value)) {}
mover(const mover& other) : value_(std::move(other.value_)) {}
T& get () & { return value_; }
T&& get () && { return std::move(value_); }
mutable T value_;
};
class MyThread : public std::thread {
template<class Func, class ArgTuple, std::size_t...Is>
void start(Func&& func, ArgTuple&& args, notstd::index_sequence<Is...>) {
// Useful, thread-specific action
func(std::get<Is>(std::forward<ArgTuple>(args))...);
}
public:
template<class Func, class... Args>
MyThread(Func&& func, Args&&... args)
: std::thread()
{
using func_type = typename std::decay<decltype(func)>::type;
auto mfunc = mover<func_type>(std::forward<Func>(func));
using arg_type = decltype(std::make_tuple(std::forward<Args>(args)...));
auto margs = mover<arg_type>(std::make_tuple(std::forward<Args>(args)...));
static_cast<std::thread&>(*this) = std::thread([this, mfunc, margs]() mutable
{
using tuple_type = typename std::remove_reference<decltype(margs.get())>::type;
constexpr auto size = std::tuple_size<tuple_type>::value;
this->start(mfunc.get(), std::move(margs).get(), notstd::make_index_sequence<size>());
});
}
};
int main()
{
auto x = MyThread([](int i){}, 6);
x.join();
}
Related
Here some code with holes:
template<typename... Args>
class A
{
typedef function_type = void(*)(Args...);
public:
void set_args(Args&& ... args)
{
// something magic manages to encapsulate
// args in instance of A
}
void apply_args(function_type function)
{
// something magic manages to "retrieve"
// the encapsulated args
function(std::forward<Args>(args)...);
}
};
Would that be somehow possible ?
You can store your template arguments in class data member of std::tuple type and the use std::apply in order to apply stored arguments to provided function.
So, let's say you have an Action class like this:
template <typename... Args>
class Action {
std::tuple<Args...> args_;
public:
Action() = default;
Action(Args&&... args)
: args_(std::forward<Args>(args)...)
{}
void args(Args&&... args) {
args_ = std::make_tuple<Args...>(std::forward<Args>(args)...);
}
template <typename F>
void apply(F&& fun) {
std::apply(std::forward<F&&>(fun), args_);
}
};
where you set arguments through constructor Action action(1, 2, 3); or through separate function action.set(3, 2, 1);.
Then your main function can look like this:
int main() {
Action action(1, 2);
action.apply([](int a, int b) {
std::cout << "a + b = " << (a + b) << std::endl;
});
return 0;
}
Check live example
You can make use of std::tuple and std::apply
#include <iostream>
#include <tuple>
#include <functional>
#include <string>
template <typename... Ts>
class A
{
private:
std::function<void (Ts...)> f;
std::tuple<Ts...> args;
public:
template <typename F>
A(F&& func, Ts&&... args)
: f(std::forward<F>(func)),
args(std::make_tuple(std::forward<Ts>(args)...))
{}
void Invoke()
{
std::apply(f, args);
}
};
template <typename F, typename... Args>
A<Args...> Create(F&& f, Args&&... args)
{
return A<Args...>(std::forward<F>(f), std::forward<Args>(args)...);
}
int main()
{
auto helloWorld = Create([] (std::string a, std::string b) { std::cout << a << ", " << b; }, std::string("Hello"), std::string("World!"));
helloWorld.Invoke();
}
I'm trying to store a member function of an unknown class with unkown arguments to be called later.
I found this code snippet to get the lambda:
template <auto Fn, typename T, typename R = void, typename... Args>
auto get_cb_inner(T* obj, R (T::*)(Args...) const) {
return [obj](Args... args) -> R {
return (obj->*Fn)(std::forward<Args>(args)...);
};
}
template <auto Fn, typename T>
auto get_cb(T* obj) {
return get_cb_inner<Fn, T>(obj, Fn);
}
But I can't figure out how I would store it and then be able to call it during runtime with the correct parameters.
I have a struct like this:
struct Job {
void execute(Data& data, const OtherData& otherData) const {
// do job
}
};
auto exe_fn = get_cb<&Job::execute>(new Job());
What I want to do is to store this "execute" function in a lambda and then store it in a vector-like container (with other functions that may have different arguments) that can be iterated and called on.
EDIT:
Using #KamilCuk code I created this wrapper-struct with no mem leaks/seg faults.
template <typename... Args>
using exec_fn = std::function<void(Args...)>;
template <typename Job>
using job_ptr = std::unique_ptr<Job>;
template <typename J, typename R = void, typename... Args>
struct JobExecuteCaller {
exec_fn<Args...> exec_fn;
job_ptr<J> job_ptr;
JobExecuteCaller (J* job, R (S::*f)(Args...) const)
: job_ptr{job_ptr<J>(job)} {
exec_fn = [this, f](Args... args) -> R {
(job_ptr.get()->*f)(std::forward<Args>(args)...);
};
}
void operator()(Args... args) { exec_fn(args...); }
};
auto process = JobExecuteCaller(new Job(), &Job::execute);
JobExecuteCaller(/*args ... */)
Now I just have to figure out a way to store different kinds of JobExecuteCallers.
You mean you want std::bind?
#include <utility>
#include <new>
#include <iostream>
#include <functional>
struct Job {
void execute(int a, int b) {
std::cout << a << " " << b << std::endl;
}
};
int main() {
auto exe_fn = std::bind(&Job::execute, new Job(),
std::placeholders::_1, std::placeholders::_2);
exe_fn(1, 2);
}
I have fixed your code. You need to pass not only the type of the function member pointer, but also the address of the function member pointer to the function. That way you can call it.
#include <utility>
#include <new>
#include <iostream>
#include <functional>
template <typename T, typename R = void, typename... Args>
auto get_cb(T* obj, R (T::*f)(Args...)) {
return [obj, f](Args... args) -> R {
return (obj->*f)(std::forward<Args>(args)...);
};
}
struct Job {
void execute(int a, int b) {
std::cout << a << " " << b << std::endl;
}
};
int main() {
auto exe_fn = get_cb(new Job(), &Job::execute);
exe_fn(1, 2);
}
Note that both examples leak memory from new Job().
I am trying to create a wrapper that calls std::terminate() when it catch an exception.
I would like this wrapper to take the same arguments as std::async() (it could be a call to a function as well as a call to a method).
Someone know how to make this code to compile ?
Thank you
http://ideone.com/tL7mTv
#include <iostream>
#include <functional>
#include <future>
template<class Fn, class... Args>
inline auto runTerminateOnException(Fn&& fn, Args&&... args) {
try {
return std::bind(std::forward<Fn>(fn), std::forward<Args>(args)...)();
} catch (...) {
std::terminate();
}
}
template<class Fn, class... Args>
inline auto runAsyncTerminateOnException(Fn&& fn, Args&&... args) {
return std::async(std::launch::async, runTerminateOnException<Fn, Args&&...>, std::forward<Fn>(fn), std::forward<Args>(args)...);
}
struct Foo {
void print() {
printf("Foo::print()\n");
}
};
int main() {
Foo foo;
std::future<void> future = runAsyncTerminateOnException(&Foo::print, &foo);
// your code goes here
return 0;
}
I personally think you over complicated it a bit. You can just bind the call and use a simple lambda to do the wrapping.
#include <iostream>
#include <future>
#include <functional>
#include <type_traits>
template<class Fn, class... Args>
inline auto runAsyncTerminateOnException(Fn&& fn, Args&&... args) {
auto make_call = std::bind(std::forward<Fn>(fn), std::forward<Args>(args)...);
return std::async(std::launch::async, [=]() -> decltype(make_call()) {
try {
return make_call();
} catch (...) {
std::cout << "Terminate Called!" << std::endl;
std::terminate();
}
});
}
struct Foo {
void print() {
printf("Foo::print()\n");
}
void print2() {
printf("Foo::print2()\n");
throw 1;
}
};
int main() {
Foo foo;
std::future<void> future = runAsyncTerminateOnException(&Foo::print, &foo);
std::future<void> future2 = runAsyncTerminateOnException(&Foo::print2, &foo);
return 0;
}
See it live, with possible output.
I obviously copied the first closure instead of preforming the required step to move it into the second closure (as one would do in c++11). You can of course move it with a specific move capture in c++14.
In c++17, the clean way to do this will be to use std::invoke.
I have hacked it in here to demonstrate.
#include <iostream>
#include <future>
#include <functional>
#include <type_traits>
namespace std
{
template<class T>
static constexpr bool is_member_pointer_v = std::is_member_pointer<T>::value;
template<class T>
static constexpr bool is_function_v = std::is_function<T>::value;
template<class B, class T>
static constexpr bool is_base_of_v = std::is_base_of<B, T>::value;
namespace detail {
template <class T>
struct is_reference_wrapper : std::false_type {};
template <class U>
struct is_reference_wrapper<std::reference_wrapper<U>> : std::true_type {};
template <class T>
constexpr bool is_reference_wrapper_v = is_reference_wrapper<T>::value;
template <class Base, class T, class Derived, class... Args>
auto INVOKE(T Base::*pmf, Derived&& ref, Args&&... args)
noexcept(noexcept((std::forward<Derived>(ref).*pmf)(std::forward<Args>(args)...)))
-> std::enable_if_t<std::is_function_v<T> &&
std::is_base_of_v<Base, std::decay_t<Derived>>,
decltype((std::forward<Derived>(ref).*pmf)(std::forward<Args>(args)...))>
{
return (std::forward<Derived>(ref).*pmf)(std::forward<Args>(args)...);
}
template <class Base, class T, class RefWrap, class... Args>
auto INVOKE(T Base::*pmf, RefWrap&& ref, Args&&... args)
noexcept(noexcept((ref.get().*pmf)(std::forward<Args>(args)...)))
-> std::enable_if_t<std::is_function_v<T> &&
is_reference_wrapper_v<std::decay_t<RefWrap>>,
decltype((ref.get().*pmf)(std::forward<Args>(args)...))>
{
return (ref.get().*pmf)(std::forward<Args>(args)...);
}
template <class Base, class T, class Pointer, class... Args>
auto INVOKE(T Base::*pmf, Pointer&& ptr, Args&&... args)
noexcept(noexcept(((*std::forward<Pointer>(ptr)).*pmf)(std::forward<Args>(args)...)))
-> std::enable_if_t<std::is_function_v<T> &&
!is_reference_wrapper_v<std::decay_t<Pointer>> &&
!std::is_base_of_v<Base, std::decay_t<Pointer>>,
decltype(((*std::forward<Pointer>(ptr)).*pmf)(std::forward<Args>(args)...))>
{
return ((*std::forward<Pointer>(ptr)).*pmf)(std::forward<Args>(args)...);
}
template <class Base, class T, class Derived>
auto INVOKE(T Base::*pmd, Derived&& ref)
noexcept(noexcept(std::forward<Derived>(ref).*pmd))
-> std::enable_if_t<!std::is_function_v<T> &&
std::is_base_of_v<Base, std::decay_t<Derived>>,
decltype(std::forward<Derived>(ref).*pmd)>
{
return std::forward<Derived>(ref).*pmd;
}
template <class Base, class T, class RefWrap>
auto INVOKE(T Base::*pmd, RefWrap&& ref)
noexcept(noexcept(ref.get().*pmd))
-> std::enable_if_t<!std::is_function_v<T> &&
is_reference_wrapper_v<std::decay_t<RefWrap>>,
decltype(ref.get().*pmd)>
{
return ref.get().*pmd;
}
template <class Base, class T, class Pointer>
auto INVOKE(T Base::*pmd, Pointer&& ptr)
noexcept(noexcept((*std::forward<Pointer>(ptr)).*pmd))
-> std::enable_if_t<!std::is_function_v<T> &&
!is_reference_wrapper_v<std::decay_t<Pointer>> &&
!std::is_base_of_v<Base, std::decay_t<Pointer>>,
decltype((*std::forward<Pointer>(ptr)).*pmd)>
{
return (*std::forward<Pointer>(ptr)).*pmd;
}
template <class F, class... Args>
auto INVOKE(F&& f, Args&&... args)
noexcept(noexcept(std::forward<F>(f)(std::forward<Args>(args)...)))
-> std::enable_if_t<!std::is_member_pointer_v<std::decay_t<F>>,
decltype(std::forward<F>(f)(std::forward<Args>(args)...))>
{
return std::forward<F>(f)(std::forward<Args>(args)...);
}
} // namespace detail
template< class F, class... ArgTypes >
auto invoke(F&& f, ArgTypes&&... args)
// exception specification for QoI
noexcept(noexcept(detail::INVOKE(std::forward<F>(f), std::forward<ArgTypes>(args)...)))
-> decltype(detail::INVOKE(std::forward<F>(f), std::forward<ArgTypes>(args)...))
{
return detail::INVOKE(std::forward<F>(f), std::forward<ArgTypes>(args)...);
}
}
template<class Fn, class... Args>
inline auto runAsyncTerminateOnException(Fn&& fn, Args&&... args) {
return std::async(std::launch::async, [=]() -> decltype(auto) {
try {
return std::invoke(fn, args...);
} catch (...) {
std::cout << "Terminate Called!" << std::endl;
std::terminate();
}
});
}
struct Foo {
void print() {
printf("Foo::print()\n");
}
void print2() {
printf("Foo::print2()\n");
throw 1;
}
};
int main() {
Foo foo;
std::future<void> future = runAsyncTerminateOnException(&Foo::print, &foo);
std::future<void> future2 = runAsyncTerminateOnException(&Foo::print2, &foo);
return 0;
}
error when invoking templated member function:
The error is this <source>: In instantiation of 'runAsyncTerminateOnException(Fn&&, Args&& ...)::<lambda()> [with Fn = void (Foo::*)(int&&); Args = {Foo*, int}]':
implying that Foo::print is demanding an int&& of course it is. That's what you wrote:
void print(Args&&... args)
It's not reasonable for a print function to demand ownership of objects. Declare it as it should be:
struct Foo {
template<class... Args>
void print(const Args&... args) {
printf("Foo::print(%d)\n", args...);
}
};
I found the solution for c++17.
It works only if we do not use auto for the return type of runTerminateOnException().
template<class Fn, class... Args>
inline std::result_of_t<Fn&&(Args&&...)> runTerminateOnException(Fn&& fn, Args&&... args) {
try {
return std::invoke(std::forward<Fn>(fn), std::forward<Args>(args)...);
} catch (...) {
std::terminate();
}
}
template<class Fn, class... Args>
inline auto runAsyncTerminateOnException(Fn&& fn, Args&&... args) {
return std::async(std::launch::async, runTerminateOnException<Fn, Args&&...>, std::forward<Fn>(fn), std::forward<Args>(args)...);
}
I'm working on implementing a wrapper for std::thread that will allow me retrieve arbitrary return values after the thread is finished executing. While I am using C++11, I am using an older ARM architecture that does not support fully support atomic int's, which means that I can't use std::future, std::promise, std::packaged_task, and much of the stl threading functionality (I do get std::thread at least). I am testing with gcc 4.8.4.
While working on my implementation, I ran into this bug, which makes it impossible for me capture variadic template parameters with a lambda. Unfortunately, I can not upgrade my compiler to 4.9 at the moment.
I'm attempting to implement a workaround using std::bind, but am running into quite a few issues. I'm unsure if these are compiler bugs or implementation errors on my part. Here is the source:
#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, ¶m);
// 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;
}
And here is my compiler output:
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)
The issue seems to be on line 76, where there is a failed conversion from std::bind(*) to std::function<void(void)>. This code is definitely still under development, but I need to get past this issue to move forward. I've looked at multiple other posts here on SO, but all of them seem to be able to use std::bind on variadic template parameters without issue.
SOLUTION
Here is the final solution (as pertaining to this question) that I came up with thanks to kzraq and this post.
Source:
#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, ¶m);
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;
}
This is more or less what Yakk wrote about. Maybe I do not understand your idea well enough, but to me it seems that you've overengineered it and you're using std::function too early. Also, ArgTypes&& won't be a list of forwarding/universal references, since they're not deduced in bindTask.
The following compiles successfully on gcc 4.8.2:
Get your own integer_sequence for C++11. Courtesy of Xeo.
Write apply to apply tuple parameters to a function (maybe this could be improved):
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>{});
}
Simplify your bindTask (though at this point I'd keep it as a template):
auto params = make_tuple(args...);
std::function<void(void)> bound = [task,params]{ apply(task, params); };
return bound;
In C++14 do [task=std::move(task),params=std::move(params)] to avoid needless copies.
As a guess, bind presumes it can be called repeatedly (esp when called in an lvalue context!), so does not turn rvalue parameters into rvalue parameters to its bound function as rvalue parameters. Which your code demands. That lambda is not perfect forwarding!
You are also capturing const& std::functions by reference in lambdas, which just invites dangling reference hell. But that is a runtime problem. As a general rule never & capture unless lifetime of lambda and all copies ends in the current scope; definitely don't do it during prototyping even if "certain" it won't be a problem.
I would consider writing a weak version of std::apply and index_sequences and packing the arguments into a tuple then doing your apply to unpack into the target callable. But that is a bias, dunno if ideal.
I want to name a thread, but unfortunately the pthread_setname_np() on Mac works only inside current thread.
Then I do the wrapper around std::thread with a following constructor:
template <class F, class ... Args>
Thread::Thread(const char* name, F&& f, Args&&... args) {
thread_ = std::thread([name, f, args...]() {
pthread_setname_np(name);
f(args...);
});
}
But it doesn't work with class methods:
error: called object type '<complex type>' is not a function or function pointer
f(args...);
^
In the code like this:
threads_.emplace_back("Name", &Aggregator<T>::DoPop, this, some_arg);
What is a proper way to wrap the std::thread and set the thread name, preserving the whole interface excepting the name argument in the constructor?
You can use std::mem_fn to call a member function. The first argument in args has to be the pointer to the member object.
Example:
#include <thread>
#include <functional>
template <class F, class ... Args>
std::thread thread_factory(const char* name, F&& f, Args&&... args) {
return std::thread([=]{
pthread_setname_np(name);
auto fun = std::mem_fn(f);
fun(args...);
});
}
struct test {
int t(int val) {
return val;
}
};
int main() {
test t;
auto b = thread_factory("name", &test::t, &t, 5);
b.join();
}
you have to bind your member function to a class instance. here's your function presented slightly differently with a (working) test:
#include <iostream>
#include <thread>
template <class F, class ... Args>
std::thread launch_named_thread(const char* name, F&& f, Args&&... args) {
return std::thread([name, f, args...]() {
pthread_setname_np(name);
f(args...);
});
}
struct myclass
{
void thread_loop(int i)
{
std::cout << i << std::endl;
}
};
auto main() -> int
{
myclass x;
auto t = launch_named_thread("hello", std::bind(&myclass::thread_loop, &x, 6));
// this could be:
// auto t = launch_named_thread("hello", std::bind(&myclass::thread_loop, &x, std::placeholders::_1), 6);
// the difference is subtle. i'll leave it to you to work out why
t.join();
return 0;
}