I want to move and call a boost::packaged_task inside a lambda.
However, I can't figure out an elegant solution.
e.g. This won't compile.
template<typename Func>
auto begin_invoke(Func&& func) -> boost::unique_future<decltype(func())> // noexcept
{
typedef boost::packaged_task<decltype(func())> task_type;
auto task = task_type(std::forward<Func>(func));
auto future = task.get_future();
execution_queue_.try_push([=]
{
try{task();}
catch(boost::task_already_started&){}
});
return std::move(future);
}
int _tmain(int argc, _TCHAR* argv[])
{
executor ex;
ex.begin_invoke([]{std::cout << "Hello world!";});
//error C3848: expression having type 'const boost::packaged_task<R>' would lose some const-volatile qualifiers in order to call 'void boost::packaged_task<R>::operator ()(void)'
// with
// [
// R=void
// ]
return 0;
}
My rather ugly solution:
struct task_adaptor_t
{
// copy-constructor acts as move constructor
task_adaptor_t(const task_adaptor_t& other) : task(std::move(other.task)){}
task_adaptor_t(task_type&& task) : task(std::move(task)){}
void operator()() const { task(); }
mutable task_type task;
} task_adaptor(std::move(task));
execution_queue_.try_push([=]
{
try{task_adaptor();}
catch(boost::task_already_started&){}
});
What is the "proper" way to move a packaged_task into a lambda which calls it?
With a proper implementation of std::bind (or something equivalent with respect to move-enabled types) you should be able to combine bind and a C++0x lambda like this:
task_type task (std::forward<Func>(func));
auto future = task.get_future();
execution_queue_.try_push(std::bind([](task_type const& task)
{
try{task();}
catch(boost::task_already_started&){}
},std::move(task)));
return future;
btw: You don't need a std::move around future because future is a local object. As such, it's already subject to potential copy elision and if the compiler is not able to do that elision it has to move construct the return value from 'future'. The explicit use of std::move in this case may actually inhibit a copy/move elision.
There's a similar question up that I posted about moving into lambdas. C++0x doesn't have any move capture syntax. The only solution I could come up with was some kind of proxy function object.
template<typename T, typename F> class move_capture_proxy {
T t;
F f;
public:
move_capture_proxy(T&& a, F&& b)
: t(std::move(a)), f(std::move(b)) {}
auto operator()() -> decltype(f(std::move(b)) {
return f(std::move(b));
}
};
template<typename T, typename F> move_capture_proxy<T, F> make_move_proxy(T&& t, F&& f) {
return move_capture_proxy<T, F>(std::move(t), std::move(f));
}
execution_queue.try_push(make_move_proxy(std::move(task), [](decltype(task)&& ref) {
auto task = std::move(ref);
// use task
});
Note that I haven't actually tried this code, and it would become a lot nicer with variadic templates, but MSVC10 doesn't have them so I don't really know about them.
Related
This question already has answers here:
How to create an std::function from a move-capturing lambda expression?
(3 answers)
Closed 2 years ago.
In C++14, a lambda expression can capture variables by moving from them using capture initializers. However, this makes the resulting closure object non-copyable. If I have an existing function that takes a std::function argument (that I cannot change), I cannot pass the closure object, because std::function's constructor requires the given functor to be CopyConstructible.
#include <iostream>
#include <memory>
void doit(std::function<void()> f) {
f();
}
int main()
{
std::unique_ptr<int> p(new int(5));
doit([p = std::move(p)] () { std::cout << *p << std::endl; });
}
This gives the following errors:
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:1911:10: error:
call to implicitly-deleted copy constructor of '<lambda at test.cpp:10:7>'
new _Functor(*__source._M_access<_Functor*>());
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:1946:8: note: in
instantiation of member function 'std::_Function_base::_Base_manager<<lambda at test.cpp:10:7>
>::_M_clone' requested here
_M_clone(__dest, __source, _Local_storage());
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:2457:33: note: in
instantiation of member function 'std::_Function_base::_Base_manager<<lambda at test.cpp:10:7>
>::_M_manager' requested here
_M_manager = &_My_handler::_M_manager;
^
test.cpp:10:7: note: in instantiation of function template specialization 'std::function<void
()>::function<<lambda at test.cpp:10:7>, void>' requested here
doit([p = std::move(p)] () { std::cout << *p << std::endl; });
^
test.cpp:10:8: note: copy constructor of '' is implicitly deleted because field '' has a deleted
copy constructor
doit([p = std::move(p)] () { std::cout << *p << std::endl; });
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/unique_ptr.h:273:7: note:
'unique_ptr' has been explicitly marked deleted here
unique_ptr(const unique_ptr&) = delete;
^
Is there a reasonable workaround?
Testing with Ubuntu clang version 3.5-1~exp1 (trunk)
There is this approach:
template< typename signature >
struct make_copyable_function_helper;
template< typename R, typename... Args >
struct make_copyable_function_helper<R(Args...)> {
template<typename input>
std::function<R(Args...)> operator()( input&& i ) const {
auto ptr = std::make_shared< typename std::decay<input>::type >( std::forward<input>(i) );
return [ptr]( Args... args )->R {
return (*ptr)(std::forward<Args>(args)...);
};
}
};
template< typename signature, typename input >
std::function<signature> make_copyable_function( input && i ) {
return make_copyable_function_helper<signature>()( std::forward<input>(i) );
}
where we make a shared pointer to our data, then make a copyable lambda that captures that shared pointer, then we wrap that copyable lambda into a std::function of the requested signature.
In your case above, you'd just:
doit( make_copyable_function<void()>( [p = std::move(p)] () { std::cout << *p << std::endl; } ) );
A slightly more advanced version defers the type erasure and adds a layer of perfect forwarding to reduce overhead:
template<typename input>
struct copyable_function {
typedef typename std::decay<input>::type stored_input;
template<typename... Args>
auto operator()( Args&&... args )->
decltype( std::declval<input&>()(std::forward<Args>(args)...) )
{
return (*ptr)(std::forward<Args>(args));
}
copyable_function( input&& i ):ptr( std::make_shared<stored_input>( std::forward<input>(i) ) ) {}
copyable_function( copyable_function const& ) = default;
private:
std::shared_ptr<stored_input> ptr;
};
template<typename input>
copyable_function<input> make_copyable_function( input&& i ) {
return {std::forward<input>(i)};
}
which does not require you to pass the signature in, and can be slightly more efficient in a few cases, but uses more obscure techniques.
In C++14 with this can be made even more brief:
template< class F >
auto make_copyable_function( F&& f ) {
using dF=std::decay_t<F>;
auto spf = std::make_shared<dF>( std::forward<F>(f) );
return [spf](auto&&... args)->decltype(auto) {
return (*spf)( decltype(args)(args)... );
};
}
doing away with the need for the helper type entirely.
If lifetime of the closure object isn't an issue, you could pass it in a reference wrapper:
int main()
{
std::unique_ptr<int> p(new int(5));
auto f = [p = std::move(p)]{
std::cout << *p << std::endl;
};
doit(std::cref(f));
}
This obviously doesn't apply to every scenario, but it's fine for your example program.
EDIT: Taking a glance at N3797 (C++14 working draft) § 20.9.11.2.1 [func.wrap.func.con] p7, the CopyConstructible requirement is still there. I wonder if there's a technical reason that can't be loosened to MoveConstructible, or if the committee just didn't get around to it?
EDIT: Answering my own question: std::function is CopyConstructible, so the wrapped functor needs to be CopyConstructible as well.
If you know you aren't actually going to copy your function object then you can just wrap it in a type that makes the compiler think it's copyable:
struct ThrowOnCopy {
ThrowOnCopy() = default;
ThrowOnCopy(const ThrowOnCopy&) { throw std::logic_error("Oops!"); }
ThrowOnCopy(ThrowOnCopy&&) = default;
ThrowOnCopy& operator=(ThrowOnCopy&&) = default;
};
template<typename T>
struct FakeCopyable : ThrowOnCopy
{
FakeCopyable(T&& t) : target(std::forward<T>(t)) { }
FakeCopyable(FakeCopyable&&) = default;
FakeCopyable(const FakeCopyable& other)
: ThrowOnCopy(other), // this will throw
target(std::move(const_cast<T&>(other.target))) // never reached
{ }
template<typename... Args>
auto operator()(Args&&... a)
{ return target(std::forward<Args>(a)...); }
T target;
};
template<typename T>
FakeCopyable<T>
fake_copyable(T&& t)
{ return { std::forward<T>(t) }; }
// ...
doit( fake_copyable([p = std::move(p)] () { std::cout << *p << std::endl; }) );
The function template fake_copyable creates a wrapper which is CopyConstructible according to the compiler (and <type_traits>) but cannot be copied at run-time.
If you store a FakeCopyable<X> in a std::function and then end up copying the std::function you will get a std::logic_error thrown, but if you only move the std::function everything will work OK.
The target(std::move(const_cast<T&>(other.target))) looks worrying, but that initializer will never run, because the base class initializer will throw first. So the worrying const_cast never really happens, it just keeps the compiler happy.
For a Packaged_Task implementation in C++11
i want to achieve what i've expressed in C++14 Code below. In other words i want to forward into a lambda expression.
template<class F>
Packaged_Task(F&& f) {
Promise<R> p;
_future = p.get_future();
auto f_holder = [f = std::forward<F>(f)]() mutable { return std::move(f); };
///...
I'm aware of workarounds for moving into a lambda (but unfortenately this workarounds need a default constructible Object, in my case the object is most often a lambda expression without default-constructor)
How about creating a wrapper struct which does a move during copy construction:(. (I know that's bad, makes me remember of auto_ptr)
template <typename F>
struct Wrapped {
using Ftype = typename std::remove_reference<F>::type;
Wrapped(Ftype&& f): f_(std::move(f)) {}
Wrapped(const Wrapped& o): f_(std::move(o.f_)) {}
mutable Ftype f_;
};
template<class F>
Packaged_Task(F&& f) {
Promise<R> p;
_future = p.get_future();
Wrapped<std::remove_reference<decltype(f)>::type> wrap(std::forward<F>(f));
auto f_holder = [wrap]() mutable { return std::move(wrap.f_); };
This is just a rough idea. Not compiled or tested.
NOTE: I have seen this technique before, do not remember whether it was on SO itself or on some blog.
First, let's boil down the question to its core: the function object is somewhat of a distraction. Essentially, you want to be able to create a lambda with a capture holding a move-only object. In C++11 that isn't directly supported which gave raise to the C++14 approach of allowing specification of how the capture is build.
For C++11 it is necessary to use a copy. Since the underlying type doesn't support copying, it become necessary to actually move the object instead of copying it. Doing so can be achieved by a suitable wrapper defining a copy constructor not really copying but rather moving. Here is an example showing that:
#include <utility>
struct foo {
foo(int) {}
foo(foo&&) = default;
foo(foo const&) = delete;
};
template <typename T>
class move_copy
{
T object;
public:
move_copy(T&& object): object(std::move(object)) {}
move_copy(move_copy& other): object(std::move(other.object)) {}
T extract() { return std::forward<T>(this->object); }
};
template <typename T>
void package(T&& object)
{
move_copy<T> mc(std::forward<T>(object));
foo g = [mc]() mutable { return mc.extract(); }();
}
int main()
{
foo f(0);
package(std::move(f));
}
The move_copy<T> wrapper actually just captures the argument the way it is passed: if an lvalue is being passed in, an lvalue is captured. To properly get hold of the contained object, the extract() member std::forward<T>()s the object: the function can be called only once safely as an object is potentially moved from there.
Breaking copy semantics by making it a move is a bad idea. If it was the only option, go for it, but it is not.
Instead, we can pass the moved value in as an argument to the lambda, and move it into the wrapping code.
curry_apply takes some value and a function object, and returns that function object with the value bound to the first argument.
template<class T, class F>
struct curry_apply_t {
T t;
F f;
template<class...Args>
auto operator()(Args&&...args)
-> typename std::result_of_t<F&(T&, Args...)>::type
{
return f(t, std::forward<Args>(args)...);
}
};
template<class T, class F>
curry_apply_t<typename std::decay<T>::type, typename std::decay<F>::type>
curry_apply( T&& t, F&& f ) {
return {std::forward<T>(t), std::forward<F>(f)};
}
Use:
template<class F>
Packaged_Task(F&& f) {
Promise<R> p;
_future = p.get_future();
auto f_holder = curry_apply(
std::move(_future),
[](Future<R>& f) mutable { return std::move(f); };
);
basically we store the moved-in data outside of the lambda in a manually written function object. We then pass it in as an lvalue argument at the front of the lambda's argument list.
Here is a more complex version of the same solution.
This question already has answers here:
How to create an std::function from a move-capturing lambda expression?
(3 answers)
Closed 2 years ago.
In C++14, a lambda expression can capture variables by moving from them using capture initializers. However, this makes the resulting closure object non-copyable. If I have an existing function that takes a std::function argument (that I cannot change), I cannot pass the closure object, because std::function's constructor requires the given functor to be CopyConstructible.
#include <iostream>
#include <memory>
void doit(std::function<void()> f) {
f();
}
int main()
{
std::unique_ptr<int> p(new int(5));
doit([p = std::move(p)] () { std::cout << *p << std::endl; });
}
This gives the following errors:
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:1911:10: error:
call to implicitly-deleted copy constructor of '<lambda at test.cpp:10:7>'
new _Functor(*__source._M_access<_Functor*>());
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:1946:8: note: in
instantiation of member function 'std::_Function_base::_Base_manager<<lambda at test.cpp:10:7>
>::_M_clone' requested here
_M_clone(__dest, __source, _Local_storage());
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:2457:33: note: in
instantiation of member function 'std::_Function_base::_Base_manager<<lambda at test.cpp:10:7>
>::_M_manager' requested here
_M_manager = &_My_handler::_M_manager;
^
test.cpp:10:7: note: in instantiation of function template specialization 'std::function<void
()>::function<<lambda at test.cpp:10:7>, void>' requested here
doit([p = std::move(p)] () { std::cout << *p << std::endl; });
^
test.cpp:10:8: note: copy constructor of '' is implicitly deleted because field '' has a deleted
copy constructor
doit([p = std::move(p)] () { std::cout << *p << std::endl; });
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/unique_ptr.h:273:7: note:
'unique_ptr' has been explicitly marked deleted here
unique_ptr(const unique_ptr&) = delete;
^
Is there a reasonable workaround?
Testing with Ubuntu clang version 3.5-1~exp1 (trunk)
There is this approach:
template< typename signature >
struct make_copyable_function_helper;
template< typename R, typename... Args >
struct make_copyable_function_helper<R(Args...)> {
template<typename input>
std::function<R(Args...)> operator()( input&& i ) const {
auto ptr = std::make_shared< typename std::decay<input>::type >( std::forward<input>(i) );
return [ptr]( Args... args )->R {
return (*ptr)(std::forward<Args>(args)...);
};
}
};
template< typename signature, typename input >
std::function<signature> make_copyable_function( input && i ) {
return make_copyable_function_helper<signature>()( std::forward<input>(i) );
}
where we make a shared pointer to our data, then make a copyable lambda that captures that shared pointer, then we wrap that copyable lambda into a std::function of the requested signature.
In your case above, you'd just:
doit( make_copyable_function<void()>( [p = std::move(p)] () { std::cout << *p << std::endl; } ) );
A slightly more advanced version defers the type erasure and adds a layer of perfect forwarding to reduce overhead:
template<typename input>
struct copyable_function {
typedef typename std::decay<input>::type stored_input;
template<typename... Args>
auto operator()( Args&&... args )->
decltype( std::declval<input&>()(std::forward<Args>(args)...) )
{
return (*ptr)(std::forward<Args>(args));
}
copyable_function( input&& i ):ptr( std::make_shared<stored_input>( std::forward<input>(i) ) ) {}
copyable_function( copyable_function const& ) = default;
private:
std::shared_ptr<stored_input> ptr;
};
template<typename input>
copyable_function<input> make_copyable_function( input&& i ) {
return {std::forward<input>(i)};
}
which does not require you to pass the signature in, and can be slightly more efficient in a few cases, but uses more obscure techniques.
In C++14 with this can be made even more brief:
template< class F >
auto make_copyable_function( F&& f ) {
using dF=std::decay_t<F>;
auto spf = std::make_shared<dF>( std::forward<F>(f) );
return [spf](auto&&... args)->decltype(auto) {
return (*spf)( decltype(args)(args)... );
};
}
doing away with the need for the helper type entirely.
If lifetime of the closure object isn't an issue, you could pass it in a reference wrapper:
int main()
{
std::unique_ptr<int> p(new int(5));
auto f = [p = std::move(p)]{
std::cout << *p << std::endl;
};
doit(std::cref(f));
}
This obviously doesn't apply to every scenario, but it's fine for your example program.
EDIT: Taking a glance at N3797 (C++14 working draft) § 20.9.11.2.1 [func.wrap.func.con] p7, the CopyConstructible requirement is still there. I wonder if there's a technical reason that can't be loosened to MoveConstructible, or if the committee just didn't get around to it?
EDIT: Answering my own question: std::function is CopyConstructible, so the wrapped functor needs to be CopyConstructible as well.
If you know you aren't actually going to copy your function object then you can just wrap it in a type that makes the compiler think it's copyable:
struct ThrowOnCopy {
ThrowOnCopy() = default;
ThrowOnCopy(const ThrowOnCopy&) { throw std::logic_error("Oops!"); }
ThrowOnCopy(ThrowOnCopy&&) = default;
ThrowOnCopy& operator=(ThrowOnCopy&&) = default;
};
template<typename T>
struct FakeCopyable : ThrowOnCopy
{
FakeCopyable(T&& t) : target(std::forward<T>(t)) { }
FakeCopyable(FakeCopyable&&) = default;
FakeCopyable(const FakeCopyable& other)
: ThrowOnCopy(other), // this will throw
target(std::move(const_cast<T&>(other.target))) // never reached
{ }
template<typename... Args>
auto operator()(Args&&... a)
{ return target(std::forward<Args>(a)...); }
T target;
};
template<typename T>
FakeCopyable<T>
fake_copyable(T&& t)
{ return { std::forward<T>(t) }; }
// ...
doit( fake_copyable([p = std::move(p)] () { std::cout << *p << std::endl; }) );
The function template fake_copyable creates a wrapper which is CopyConstructible according to the compiler (and <type_traits>) but cannot be copied at run-time.
If you store a FakeCopyable<X> in a std::function and then end up copying the std::function you will get a std::logic_error thrown, but if you only move the std::function everything will work OK.
The target(std::move(const_cast<T&>(other.target))) looks worrying, but that initializer will never run, because the base class initializer will throw first. So the worrying const_cast never really happens, it just keeps the compiler happy.
This question already has answers here:
How to create an std::function from a move-capturing lambda expression?
(3 answers)
Closed 2 years ago.
In C++14, a lambda expression can capture variables by moving from them using capture initializers. However, this makes the resulting closure object non-copyable. If I have an existing function that takes a std::function argument (that I cannot change), I cannot pass the closure object, because std::function's constructor requires the given functor to be CopyConstructible.
#include <iostream>
#include <memory>
void doit(std::function<void()> f) {
f();
}
int main()
{
std::unique_ptr<int> p(new int(5));
doit([p = std::move(p)] () { std::cout << *p << std::endl; });
}
This gives the following errors:
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:1911:10: error:
call to implicitly-deleted copy constructor of '<lambda at test.cpp:10:7>'
new _Functor(*__source._M_access<_Functor*>());
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:1946:8: note: in
instantiation of member function 'std::_Function_base::_Base_manager<<lambda at test.cpp:10:7>
>::_M_clone' requested here
_M_clone(__dest, __source, _Local_storage());
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:2457:33: note: in
instantiation of member function 'std::_Function_base::_Base_manager<<lambda at test.cpp:10:7>
>::_M_manager' requested here
_M_manager = &_My_handler::_M_manager;
^
test.cpp:10:7: note: in instantiation of function template specialization 'std::function<void
()>::function<<lambda at test.cpp:10:7>, void>' requested here
doit([p = std::move(p)] () { std::cout << *p << std::endl; });
^
test.cpp:10:8: note: copy constructor of '' is implicitly deleted because field '' has a deleted
copy constructor
doit([p = std::move(p)] () { std::cout << *p << std::endl; });
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/unique_ptr.h:273:7: note:
'unique_ptr' has been explicitly marked deleted here
unique_ptr(const unique_ptr&) = delete;
^
Is there a reasonable workaround?
Testing with Ubuntu clang version 3.5-1~exp1 (trunk)
There is this approach:
template< typename signature >
struct make_copyable_function_helper;
template< typename R, typename... Args >
struct make_copyable_function_helper<R(Args...)> {
template<typename input>
std::function<R(Args...)> operator()( input&& i ) const {
auto ptr = std::make_shared< typename std::decay<input>::type >( std::forward<input>(i) );
return [ptr]( Args... args )->R {
return (*ptr)(std::forward<Args>(args)...);
};
}
};
template< typename signature, typename input >
std::function<signature> make_copyable_function( input && i ) {
return make_copyable_function_helper<signature>()( std::forward<input>(i) );
}
where we make a shared pointer to our data, then make a copyable lambda that captures that shared pointer, then we wrap that copyable lambda into a std::function of the requested signature.
In your case above, you'd just:
doit( make_copyable_function<void()>( [p = std::move(p)] () { std::cout << *p << std::endl; } ) );
A slightly more advanced version defers the type erasure and adds a layer of perfect forwarding to reduce overhead:
template<typename input>
struct copyable_function {
typedef typename std::decay<input>::type stored_input;
template<typename... Args>
auto operator()( Args&&... args )->
decltype( std::declval<input&>()(std::forward<Args>(args)...) )
{
return (*ptr)(std::forward<Args>(args));
}
copyable_function( input&& i ):ptr( std::make_shared<stored_input>( std::forward<input>(i) ) ) {}
copyable_function( copyable_function const& ) = default;
private:
std::shared_ptr<stored_input> ptr;
};
template<typename input>
copyable_function<input> make_copyable_function( input&& i ) {
return {std::forward<input>(i)};
}
which does not require you to pass the signature in, and can be slightly more efficient in a few cases, but uses more obscure techniques.
In C++14 with this can be made even more brief:
template< class F >
auto make_copyable_function( F&& f ) {
using dF=std::decay_t<F>;
auto spf = std::make_shared<dF>( std::forward<F>(f) );
return [spf](auto&&... args)->decltype(auto) {
return (*spf)( decltype(args)(args)... );
};
}
doing away with the need for the helper type entirely.
If lifetime of the closure object isn't an issue, you could pass it in a reference wrapper:
int main()
{
std::unique_ptr<int> p(new int(5));
auto f = [p = std::move(p)]{
std::cout << *p << std::endl;
};
doit(std::cref(f));
}
This obviously doesn't apply to every scenario, but it's fine for your example program.
EDIT: Taking a glance at N3797 (C++14 working draft) § 20.9.11.2.1 [func.wrap.func.con] p7, the CopyConstructible requirement is still there. I wonder if there's a technical reason that can't be loosened to MoveConstructible, or if the committee just didn't get around to it?
EDIT: Answering my own question: std::function is CopyConstructible, so the wrapped functor needs to be CopyConstructible as well.
If you know you aren't actually going to copy your function object then you can just wrap it in a type that makes the compiler think it's copyable:
struct ThrowOnCopy {
ThrowOnCopy() = default;
ThrowOnCopy(const ThrowOnCopy&) { throw std::logic_error("Oops!"); }
ThrowOnCopy(ThrowOnCopy&&) = default;
ThrowOnCopy& operator=(ThrowOnCopy&&) = default;
};
template<typename T>
struct FakeCopyable : ThrowOnCopy
{
FakeCopyable(T&& t) : target(std::forward<T>(t)) { }
FakeCopyable(FakeCopyable&&) = default;
FakeCopyable(const FakeCopyable& other)
: ThrowOnCopy(other), // this will throw
target(std::move(const_cast<T&>(other.target))) // never reached
{ }
template<typename... Args>
auto operator()(Args&&... a)
{ return target(std::forward<Args>(a)...); }
T target;
};
template<typename T>
FakeCopyable<T>
fake_copyable(T&& t)
{ return { std::forward<T>(t) }; }
// ...
doit( fake_copyable([p = std::move(p)] () { std::cout << *p << std::endl; }) );
The function template fake_copyable creates a wrapper which is CopyConstructible according to the compiler (and <type_traits>) but cannot be copied at run-time.
If you store a FakeCopyable<X> in a std::function and then end up copying the std::function you will get a std::logic_error thrown, but if you only move the std::function everything will work OK.
The target(std::move(const_cast<T&>(other.target))) looks worrying, but that initializer will never run, because the base class initializer will throw first. So the worrying const_cast never really happens, it just keeps the compiler happy.
I'd like to create a function that takes a weak pointer and any kind of functor (lambda, std::function, whatever) and returns a new functor that only executes the original functor when the pointer was not removed in the meantime (so let's assume there is a WeakPointer type with such semantics). This should all work for any functor without having to specify explicitly the functor signature through template parameters or a cast.
EDIT:
Some commenters have pointed out that std::function - which I used in my approach - might not be needed at all and neither might the lambda (though in my original question I also forgot to mention that I need to capture the weak pointer parameter), so any alternative solution that solves the general problem is of course is also highly appreciated, maybe I didn't think enough outside the box and was to focused on using a lambda + std::function. In any case, here goes what I tried so far:
template<typename... ArgumentTypes>
inline std::function<void(ArgumentTypes...)> wrap(WeakPointer pWeakPointer, const std::function<void(ArgumentTypes...)>&& fun)
{
return [=] (ArgumentTypes... args)
{
if(pWeakPointer)
{
fun(args...);
}
};
}
This works well without having to explicitly specify the argument types if I pass an std::function, but fails if I pass a lambda expression. I guess this because the std::function constructor ambiguity as asked in this question. In any case, I tried the following helper to be able to capture any kind of function:
template<typename F, typename... ArgumentTypes>
inline function<void(ArgumentTypes...)> wrap(WeakPointer pWeakPointer, const F&& fun)
{
return wrap(pWeakPointer, std::function<void(ArgumentTypes...)>(fun));
}
This now works for lambdas that don't have parameters but fails for other ones, since it always instantiates ArgumentTypes... with an empty set.
I can think of two solution to the problem, but didn't manage to implement either of them:
Make sure that the correct std::function (or another Functor helper type) is created for a lambda, i.e. that a lambda with signature R(T1) results in a std::function(R(T1)) so that the ArgumentTypes... will be correctly deduced
Do not put the ArgumentTypes... as a template parameter instead have some other way (boost?) to get the argument pack from the lambda/functor, so I could do something like this:
-
template<typename F>
inline auto wrap(WeakPointer pWeakPointer, const F&& fun) -> std::function<void(arg_pack_from_functor(fun))>
{
return wrap(pWeakPointer, std::function<void(arg_pack_from_functor(fun))(fun));
}
You don't have to use a lambda.
#include <iostream>
#include <type_traits>
template <typename F>
struct Wrapper {
F f;
template <typename... T>
auto operator()(T&&... args) -> typename std::result_of<F(T...)>::type {
std::cout << "calling f with " << sizeof...(args) << " arguments.\n";
return f(std::forward<T>(args)...);
}
};
template <typename F>
Wrapper<F> wrap(F&& f) {
return {std::forward<F>(f)};
}
int main() {
auto f = wrap([](int x, int y) { return x + y; });
std::cout << f(2, 3) << std::endl;
return 0;
}
Assuming the weak pointer takes the place of the first argument, here's how I would do it with a generic lambda (with move captures) and if C++ would allow me to return such a lambda:
template<typename Functor, typename Arg, typename... Args>
auto wrap(Functor&& functor, Arg&& arg)
{
return [functor = std::forward<Functor>(functor)
, arg = std::forward<Arg>(arg)]<typename... Rest>(Rest&&... rest)
{
if(auto e = arg.lock()) {
return functor(*e, std::forward<Rest>(rest)...);
} else {
// Let's handwave this for the time being
}
};
}
It is possible to translate this hypothetical code into actual C++11 code if we manually 'unroll' the generic lambda into a polymorphic functor:
template<typename F, typename Pointer>
struct wrap_type {
F f;
Pointer pointer;
template<typename... Rest>
auto operator()(Rest&&... rest)
-> decltype( f(*pointer.lock(), std::forward<Rest>(rest)...) )
{
if(auto p = lock()) {
return f(*p, std::forward<Rest>(rest)...);
} else {
// Handle
}
}
};
template<typename F, typename Pointer>
wrap_type<typename std::decay<F>::type, typename std::decay<Pointer>::type>
wrap(F&& f, Pointer&& pointer)
{ return { std::forward<F>(f), std::forward<Pointer>(pointer) }; }
There are two straightforward options for handling the case where the pointer has expired: either propagate an exception, or return an out-of-band value. In the latter case the return type would become e.g. optional<decltype( f(*pointer.lock(), std::forward<Rest>(rest)...) )> and // Handle would become return {};.
Example code to see everything in action.
[ Exercise for the ambitious: improve the code so that it's possible to use auto g = wrap(f, w, 4); auto r = g();. Then, if it's not already the case, improve it further so that auto g = wrap(f, w1, 4, w5); is also possible and 'does the right thing'. ]