#include <utility>
#include <iostream>
#include <memory>
template<class T>
struct moved_value{
moved_value(T* v):pval(v){};
T& get(){return *pval;}
const T& get()const{return *pval;}
private:
T* pval;
};
// Copied with removal of Parameter info from
// http://stackoverflow.com/questions/7943525/is-it-possible-to-figure-out-the-parameter-type-and-return-type-of-a-lambda
template <typename T>
struct function_traits
: public function_traits<decltype(&T::operator())>
{};
// For generic types, directly use the result of the signature of its 'operator()'
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const>
// we specialize for pointers to member function
{
typedef ReturnType result_type;
};
template<class T,class F>
struct move_lambda{
T val;
F f_;
typedef function_traits<F> traits;
typedef typename traits::result_type result_type;
move_lambda(T&& v, F f):val(std::move(v)),f_(f){};
move_lambda(move_lambda&& other):val(std::move(other.val)),f_(std::move(other.f_)){}
move_lambda& operator=(move_lambda&& other){
val = std::move(other.val);
f_ = std::move(other.f_);
}
template<class... Args>
auto operator()(Args&& ...args) -> result_type
{
moved_value<T> mv(&val);
return f_(mv,std::forward<Args>(args)...);
}
private:
move_lambda();
move_lambda(const move_lambda&);
move_lambda& operator=(const move_lambda&);
};
template<class T,class F>
move_lambda<T,F>create_move_lambda(T&& t, F f){
return move_lambda<T,F>(std::move(t),f);
}
// Unfortunately, std::function does not seem to support move-only callables
// Here is our quick movable replacement
template< class ReturnType, class... ParamTypes>
struct movable_function_base{
virtual ReturnType callFunc(ParamTypes&&... p) = 0;
};
template<class F, class ReturnType, class... ParamTypes>
struct movable_function_imp:public movable_function_base<ReturnType,ParamTypes...>{
F f_;
virtual ReturnType callFunc(ParamTypes&&... p){
return f_(std::forward<ParamTypes>(p)...);
}
explicit movable_function_imp(F&& f):f_(std::move(f)){};
private:
movable_function_imp();
movable_function_imp(const movable_function_imp&);
movable_function_imp& operator=(const movable_function_imp&);
};
template<class FuncType>
struct movable_function{};
template<class ReturnType, class... ParamTypes>
struct movable_function<ReturnType(ParamTypes...)>{
std::unique_ptr<movable_function_base<ReturnType,ParamTypes...>> ptr_;
template<class F>
explicit movable_function(F&& f):ptr_(new movable_function_imp<F,ReturnType,ParamTypes...>(std::move(f))){}
movable_function(movable_function&& other):ptr_(std::move(other.ptr_)){}
movable_function& operator=(movable_function&& other){
ptr_ = std::move(other.ptr_);
return *this;
}
template<class... Args>
auto operator()(Args&& ...args) -> ReturnType
{
return ptr_->callFunc(std::forward<Args>(args)...);
}
private:
movable_function();
movable_function(const movable_function&);
movable_function& operator=(const movable_function&);
};
struct TestMove{
TestMove():k(0){}
TestMove(TestMove&& other):k(other.k){
other.k = -1;
}
TestMove& operator=(TestMove&& other){
k = other.k;
other.k = -1;
return *this;
}
int k;
private:
TestMove(const TestMove&);
TestMove& operator=(const TestMove&);
};
movable_function<void()> CreateLambda()
{
typedef TestMove HugeObject;
HugeObject hugeObj;
hugeObj.k = 9;
// ...preparation of hugeObj...
auto f = create_move_lambda(std::move(hugeObj),[](moved_value<HugeObject> hugeObj){// manipulate huge object
std::cout << hugeObj.get().k << std::endl;
});
movable_function<void()> toReturn(std::move(f));
return toReturn;
}
int main(){
// A movable only type, not copyable
TestMove m;
m.k = 5;
// A movable only type, not copyable
TestMove m2;
m2.k = 6;
// Create a lambda that takes 2 parameters and returns int
auto lambda = create_move_lambda(std::move(m),[](moved_value<TestMove> m,int i,int)->int{std::cout << m.get().k << " " << i << std::endl;return 7;});
// Create a lambda that takes 0 parameters and returns void
auto lambda2 = create_move_lambda(std::move(m2),[](moved_value<TestMove> m){std::cout << m.get().k << std::endl;});
std::cout << lambda(1,2) << std::endl;
lambda2();
// Compiler error if you try to copy
//auto lambda4 = lambda;
// Able to move
auto lambda3 = std::move(lambda2);
lambda3();
auto lambda4 = CreateLambda();
lambda4();
}