Skip to content

Instantly share code, notes, and snippets.

@vmilea
Created June 19, 2013 16:38
Show Gist options
  • Save vmilea/5815777 to your computer and use it in GitHub Desktop.
Save vmilea/5815777 to your computer and use it in GitHub Desktop.
how to wrap noncopyable functors as std::function
#include "meta/FunctionTraits.h"
#include <utility>
#include <stdexcept>
#include <functional>
#include <string>
#include <cassert>
template <typename F>
class MoveOnCopyAdapter
{
public:
MoveOnCopyAdapter(F&& f)
: mF(std::move(f)) { }
MoveOnCopyAdapter(MoveOnCopyAdapter&& other)
: mF(std::move(other.mF)) { }
MoveOnCopyAdapter& operator=(MoveOnCopyAdapter&& other)
{
mF = std::move(other.mF);
return *this;
}
MoveOnCopyAdapter(const MoveOnCopyAdapter& other)
: mF(static_cast<F&&>(const_cast<F&>(other.mF)))
{
throw std::logic_error("not copyable");
}
MoveOnCopyAdapter& operator=(const MoveOnCopyAdapter& other)
{
throw std::logic_error("not copyable");
}
template <typename... Args>
auto operator()(Args&&... args)
-> typename std::result_of<F (Args...)>::type
{
using namespace std;
return mF(std::forward<Args>(args)...);
}
private:
F mF;
};
template <typename F>
auto asFunction(F&& f)
-> std::function<typename ut::FunctionTraits<F>::signature_type>
{
static_assert (std::is_rvalue_reference<F&&>::value, "needs rvalue");
return MoveOnCopyAdapter<F>(std::move(f));
}
struct Appender
{
std::string value;
Appender() { }
Appender(Appender&& other)
: value(std::move(other.value)) { }
const std::string& operator()(std::string s)
{
return value += s;
}
private:
Appender(const Appender& );
Appender& operator=(const Appender& );
};
int main()
{
Appender a;
std::function<const std::string& (std::string)> f;
f = asFunction(std::move(a));
f("123");
// auto g = f; // throws
auto g = std::move(f); // ok
assert (g("456") == "123456");
}
@frboyer
Copy link

frboyer commented Mar 17, 2017

The copy constructor has a problem. It will move other.mF before throwing the exception, and thus the original will be destroyed (i.e. in your example, if you catch the exception of auto g = f; and then call f, it will result in undefined behavior), not only the "failed copy". My solution will not move the original, and I think also looks cleaner:

MoveOnCopyAdapter(const MoveOnCopyAdapter& other) : mF(throwNotCopyable()) { }

[[noreturn]] F throwNotCopyable() {
	throw std::logic_error("not copyable");
}

As the function does not return, it does not have to hack some value having the correct type, but it still can have that return type.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment