#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();

}
