1

I'd like to have a wrapper thread function, i.e. a function executed by a thread which does some extra stuff, and then calls the user function.

template<class F, class... Args> void wrapper(F&& user_function, Args&&... args) { // do some extra stuff user_function(args); // maybe I need to forward args // do some extra stuff } 

Ok, this could be a nice wrapper, so I need a manager that uses this wrapper function and allows the user to spawn his own threads:

class ThreadManager { public: template<class F, class... Args> std::thread newThread(F&& f, Args&&... args) { return std::thread(thread_wrapper<F,Args...>, std::forward<F>(f), std::forward<Args>(args)...); } }; 

this way the thread manager SHOULD spawn a thread that uses the wrapper function which, in turn, does its extra work and calls the user function.

But the compiler now says: Attempt to use a deleted function.

The error is in the thread header:

template <class _Fp, class ..._Args, size_t ..._Indices> inline _LIBCPP_INLINE_VISIBILITY void __thread_execute(tuple<_Fp, _Args...>& __t, __tuple_indices<_Indices...>) { __invoke(_VSTD::move(_VSTD::get<0>(__t)), _VSTD::move(_VSTD::get<_Indices>(__t))...); } 

What am I missing/doing wrong?

[edit]

Using test:

void foo(int i) { std::cout << "foo: " << i << std::endl; } int main(int argc, const char *argv[]) { ThreadManager mgr; auto t = mgr.newThread(foo, 10); t.detach(); std::this_thread::sleep_for(std::chrono::milliseconds(1000)); return 0; } 

I'm using Xcode 7.1 with LLVM compiler, but fails on FreeBSD clang 3.3 too.

The Xcode error is:

/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/thread:337:5: error: attempt to use a deleted function __invoke(_VSTD::move(_VSTD::get<0>(__t)), _VSTD::move(_VSTD::get<_Indices>(__t))...); ^ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/thread:347:5: note: in instantiation of function template specialization 'std::__1::__thread_execute' requested here __thread_execute(*__p, _Index()); ^ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/thread:359:42: note: in instantiation of function template specialization 'std::__1::__thread_proxy >' requested here int __ec = pthread_create(&__t_, 0, &__thread_proxy<_Gp>, __p.get());

4
  • 1
    post the exact error. What compiler and version do you use? Commented Oct 25, 2015 at 8:35
  • please post a minimal reproducible example, I can't reproduce here: melpon.org/wandbox/permlink/V2DCrfEgtvpjwWX7 Commented Oct 25, 2015 at 8:37
  • your example on melpon.org fails if you use a normal function instead of a functor... Commented Oct 25, 2015 at 9:48
  • Note that explicitly using thread_wrapper<F, Args...> will turn your perfect forwarding F&& and Args&& parameters into actual rvalue reference parameters. Commented Oct 25, 2015 at 14:44

1 Answer 1

1

I'm not sure what is causing the "Attempt to use a deleted function" in your example, I get other errors related to std::thread's bind mechanism.

It appears the way you are spelling out the template arguments for thread_wrapper is not playing nice with std::thread's constructor - in particular when it uses a simplified std::bind internally. The mix of perfectly forwarded function types and std::decayed function pointers seems to upset std::result_of.

we can make it work by applying some std::decay in newThread ourselves:

return std::thread( thread_wrapper<typename std::decay<F>::type, typename std::decay<Args>::type...>, std::forward<F>(f), std::forward<Args>(args)... ); 

...but to be honest I'm not entirely sure why that works.

Alternatively, with some indirection and more forwarding, we can avoid having to spell out the template arguments. We just need a functor that forwards to thread_wrapper (or a polymorphic lambda in C++14):

struct wrapper_helper { template<class F, class... Args> void operator()(F&& f, Args&&... args) const { thread_wrapper(std::forward<F>(f), std::forward<Args>(args)...); } }; 

And use it in newThread:

return std::thread(wrapper_helper{}, std::forward<F>(f), std::forward<Args>(args)...); 

Here's the full example showing arguments passed by value, reference and rvalue reference working as intended: http://coliru.stacked-crooked.com/a/b75d5a264f583237

Note: For move-only types like std::unique_ptr, you will definitely want to forward args... in thread_wrapper.

Sign up to request clarification or add additional context in comments.

5 Comments

Nice, seems working fine, but fails if trying to pass a unique_ptr std::unique_ptr<int> p{new int(10)}; mgr.newThread(my_function, std::move(p)); // fails mgr.newThread(my_function, std::unique_ptr<int>{new int(10)}); // fails
@AntonelloSeprano I would expect this if you didn't change thread_ wrapper to also forward the arguments - in which case it would attempt to copy the unique_ptr argument, which it cannot. Works on my compiler if I do forward
Yes, I didn't forward args in thread_wrapper, but now (with forwarding) stops working with normal vars: coliru.stacked-crooked.com/a/1a8c80b265c915ff
@AntonelloSeprano I was able to dig myself out of it by wrapping wrapper in a generic forwarding lambda before passing it to std::thread's constructor: coliru.stacked-crooked.com/a/fae52fcdaa522b8e
works like a charm, but it is not C++11. Not a problem, of course :) Can I add another +1? :D I set your answed as 'valid', please edit your answer adding your last suggest :)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.