I created a threadpool which captures a function and arguments into tuples and then perfect forwards when the task is dequeued.
However I am unable to pass a vector of unique_ptr's to the thread by rvalue. A simplified project is below:
#include <future>
#include <memory>
#include <vector>
template <typename F, typename... Args>
typename std::result_of<F(Args...)>::type pushTask(F&& f, Args&&... args)
{
using result_type = typename std::result_of<F(Args...)>::type;
// create a functional object of the passed function with the signature std::function<result_type(void)> by creating a
// bound Functor lambda which will bind the arguments to the function call through perfect forwarding and lambda capture
auto boundFunctor = [func = std::move(std::forward<F>(f)),
argsTuple = std::move(std::make_tuple(std::forward<Args>(args)...))](void) mutable->result_type
{
// forward function and turn variadic arguments into a tuple
return result_type();
};
// create a packaged task of the function object
std::packaged_task<result_type(void)> taskFunctor{ std::move(boundFunctor) };
}
int main(int argc, char *argv [])
{
auto testvup = [](std::vector<std::unique_ptr<int>>&& vup)
{
};
std::vector<std::unique_ptr<int>> vup;
pushTask(testvup, std::move(vup));
}
I get the following compiler error with VS2015 rather I use the std::function or std::packaged_task
Severity Description Project File Line
Error error C2280: 'std::unique_ptr<int,std::default_delete<_Ty>>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)': attempting to reference a deleted function Stack xmemory0 659
passing other arguments by rvalue including std::vector works.
Has anyone else run across this or have suggestions.
C++ Standard section §20.9.11.2.1 [func.wrap.func]
template<class F> function(F f);
template <class F, class A> function(allocator_arg_t, const A& a, F
f);
Requires: F shall be CopyConstructible. f shall be Callable for
argument types ArgTypes and return type R. The copy constructor and
destructor of A shall not throw exceptions.
Your lambda function boundFunctor is a move only type (because it captures move only types, since std::unique_ptr cannot be copied)
Hence, boundFunctor is not copyable and not suitable as an argument to an std::function
Related
I have the following program, compile+run, no problem
#include <thread>
#include <future>
#include <iostream>
#include <algorithm>
void f(int* first,
int* last,
std::promise<int> accumulate_promise)
{
int sum = std::accumulate(first, last, 0);
accumulate_promise.set_value(sum); // Notify future
}
int main()
{
int numbers[] = { 1, 2, 3, 4, 5, 6 };
std::promise<int> accumulate_promise;
std::future<int> accumulate_future = accumulate_promise.get_future();
std::thread work_thread(f, begin(numbers), end(numbers),
std::move(accumulate_promise));
accumulate_future.wait(); // wait for result
std::cout << "result=" << accumulate_future.get() << '\n';
work_thread.join(); // wait for thread completion
}
But if I change "f" into a template:
template<typename Iterator>
void f(Iterator first,
Iterator last,
std::promise<int> accumulate_promise)
{
int sum = std::accumulate(first, last, 0);
accumulate_promise.set_value(sum); // Notify future
}
Then it fails compilation,gcc report that thread::thread() ctor cannot find proper overload:
error: no matching function for call to 'std::thread::thread(, int*, int*, std::remove_reference&>::type)'
What is the message indicating, anything wrong with my template?
How to fix it?
Thanks.
f is a template.
std::thread work_thread(f, begin(numbers), end(numbers),
std::move(accumulate_promise));
To put it in loose terms, std::thread's first parameter is either a function pointer or something that acts like a function pointer. It doesn't take a template as the first parameter.
A template becomes a class, or a function, when it is instantiated. The template gets instantiated when it gets used. So, given this template definition, and using it in a manner like this:
f(something.begin(), something.end(), some_kind_of_a_promise);
this instantiates a template, and uses it. To instantiate a template explicitly, without using it:
f<int *>
Now, you have an instantiated template here. The following works here:
std::thread work_thread(f<int *>, std::begin(numbers),
std::end(numbers),
std::move(accumulate_promise));
Tested with gcc 5.3.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());
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.
void f(vector<int>& v){
for(const auto& x:v) cout << x;
}
class F{
private:
vector<int> v;
public:
F(vector<int>& vc):v{vc}{}
void operator()(){
for(const auto& x:v) cout << x;
}
};
int main()
{
vector<int> some_vec{3,5,77,32,1};
vector<int> vec{66,8,90,45,777};
thread t1{f,some_vec};
thread t2{F(vec)};
t1.join();
t2.join();
cout << '\n';
}
An error "no type named 'type' in 'class std::result_of< void (*(std::vector))(std::vector&)>' occur
If the argument vector in f is declared as const, void f(const vector<int>& v), the error disappears.
On the other hand, the code with function object F works just fine.
Code from Bjarne Stroustrup -- the C++ programming language 5.3.2 Passing Arguments
std::thread stores copies of the arguments passed to its constructor, and then uses rvalues of those copies as the arguments for a handler. That is, function f cannot be called with an rvalue of std::vector, as it expects a non-const lvalue reference. Even if you change it to a const lvalue reference, then it's a copy of what is actually passed to the t1's constructor.
On the contrary, class F has an implicitly defined copy-constructor, and its function call operator expects no arguments, hence you get no errors. (And F itself is constructed before it's passed to a thread's constructor).
If you want function f to operate on the some_vec instance, you'd have to wrap it with a reference wrapper:
#include <functional>
std::thread t1{f, std::ref(some_vec)};
// ~~~~~~~^
According to this Q&A, std::future works if a function returns a value, but you can't pass references and get multiple values. So a function like this will give no results with std::future:
void doSomething(int &a, int &b) { a = 1; b = 2; }
My idea was to create a structure and have the function return the structure:
#include <iostream>
#include <future>
using namespace std;
struct myData
{
int a;
int b;
};
myData doSomething()
{
myData d;
d.a = 1;
d.b = 2;
return d;
}
int main()
{
future<myData> t1 = async(launch::deferred, doSomething);
printf("A=%d, B=%d\n", t1.get().a, t1.get().b);
return 0;
}
So, how can I get two or more values from a std::future? Is there a better method than this?
but you can't pass references and get multiple values.
Not true, as explained in the answers to the linked question, you can pass references, you just need to use std::ref to protect them from decaying. So to call void doSomething(int &a, int &b) you would use:
int a;
int b;
auto fut = std::async(std::launch::deferred, doSomething, std::ref(a), std::ref(b));
fut.get(); // wait for future to be ready
std::printf("A=%d, B=%d\n", a, b);
But that function doesn't return multiple values, it uses out parameters to set multiple variables. For a function to return multiple values you do need to return some composite type such as a struct, but that has nothing to do with std::future, that's how C++ works. Functions have a single return type.
Your solution returning a struct is the idiomatic way, although your code will fail at run-time because you use t1.get() twice, and you can only retrieve the result from a std::future once. To access the result twice either move the result into a new variable:
auto result = t1.get();
or convert the future to a std::shared_future which allows the result to be accessed multiple times:
auto t2 = t1.share();
But you don't need to use a custom structure to return multiple values, you can just use a pair or tuple:
#include <cstdio>
#include <future>
#include <tuple>
std::tuple<int, int> doSomething()
{
return std::make_tuple(1, 2);
}
int main()
{
auto fut = std::async(std::launch::deferred, doSomething);
auto result = fut.get();
std::printf("A=%d, B=%d\n", std::get<0>(result), std::get<1>(result));
}
The error you get have nothing to do with your implementation, it's that the linker doesn't link with the pthread library by default.
Add the flagg -pthread to the compiler and linker (if you're using GCC or Clang) and it should work.
Alternatively, add the pthread library as a linker library with the -l linker flag.
Using the insight of this question (and a few others) I have been able to write the following for interrogating normal lambda function type infromation (i.e. return type, argument count etc)
// helper classes ========================================
template <typename R, typename... A>
class lambda_traits_evaluation {
public:
typedef R r_type;
enum { n_args = sizeof...(A) };
// ...
};
template <typename R, typename... A>
class lambda_traits_helper
: public lambda_traits_evaluation<R,A...>{};
template <typename R, typename F, typename... A>
class lambda_traits_helper<R (F::*)(A...) const>
: public lambda_traits_evaluation<R,A...>{};
// use class ========================================
template <typename F>
class lambda_traits {
typedef typename lambda_traits_helper<decltype(&F::operator())> helper_impl;
// ...
}
I can then use this with lambda_traits<decltype(myLambda)> but that is where my smug coding ends because if my lambda is amp restricted for the gpu i.e.
auto myLambda = [](int) restrict(amp) -> void {};
as obviously the template specialisation is not picked up. However adding the new specialisation
template <typename R, typename F, typename... A>
class lambda_traits_helper<R (F::*)(A...) const restrict(amp)>
: public lambda_traits_evaluation<R,A...> {};
still does not solve the problem as I discover that the compiler barks
error C3939: 'abstract declarator' : pointer to member functions, function
pointers, references to functions with 'amp' restriction
specifier are not allowed
is there another way to interrogate the types in lambdas or else a way to strip the restrict off the lambda type?
The inability to form a pointer to an amp-restricted function, even in unevaluated context, is a bummer. There is however a workaround, which is viable as long as you can require the amp-restricted lambdas to be cpu,amp-restricted. In such case you can cast-away the amp-restriction, forming a pointer to the cpu-restricted member function -- which you can interrogate further.
See the following proof-of-concept:
#include <type_traits>
template <typename R, typename F, typename... A>
auto get_R(R (F::*)(A...) const) -> R
{}
template <typename L>
struct lambda_traits
{
using ret_type = decltype(get_R(&L::operator()));
};
int main()
{
auto lambda_1 = [](int) restrict(cpu,amp) -> void {};
auto lambda_2 = [](int) restrict(cpu,amp) -> int { return 0; };
// Test:
static_assert(std::is_same<lambda_traits<decltype(lambda_1)>::ret_type, void>::value, "Failed 1.");
static_assert(std::is_same<lambda_traits<decltype(lambda_2)>::ret_type, int>::value, "Failed 2.");
}
Hope that helps!