#ifndef FUNCTION_HPP_INCLUDED #define FUNCTION_HPP_INCLUDED /* function is a buffed duplicate of std::function, but can have arbitrary amounts of signatures function f; // wow! Differences: Doesn't have "target access" whatever that means, mainly because I didn't find a use for that my entire life using std::function. Doesn't support allocator stuff, they're deprecated in C++17 anyways. Note: When the function is invoked, overload resolution is performed function f = [](auto x){cout << sizeof(x) << endl;}; f('c'); // prints 1 f(4.2); // prints sizeof(double), likely 8 It is thus valid to construct a function with an object that doesn't have the exact signature as specified as long as there is an implicit conversion from the specified signature to the object's operator() function f = [](int i){cout << i << endl;}; f(42.5); // prints 42, due to double to int conversion f(420); // prints 420 Remember the return types can vary function (int), double (char, char)> f = something; int* p = f(); auto [x, y] = f(4200); double d = f('4', '2'); */ #include #include #include #include //#include namespace detail { struct dummy {}; template struct erasure_base { void operator()(dummy) {}; }; template struct erasure_base : erasure_base { virtual Ret operator()(Args&&...) = 0; using erasure_base::operator(); }; template struct erasure; // Base is the common erasure_base, only the base case of erasure contains the callable object template struct erasure : Base { template erasure(C&& callable) : callable{std::forward(callable)} {} Ret operator()(Args&&... args) override { return Ret(callable(std::forward(args)...)); } std::decay_t callable; }; // Next is the erasure type that contains the next signature template struct erasure : erasure { using Next = erasure; template erasure(C&& callable) : Next{std::forward(callable)} {} Ret operator()(Args&&... args) override { return Ret(Next::callable(std::forward(args)...)); } }; } template class function { using erasure_base = detail::erasure_base; template using erasure = detail::erasure; using nullptr_t = decltype(nullptr); public: function() = default; function(nullptr_t) {} function(const function&) = delete; function(function&&) = default; template function(Callable&& callable) : ptr{new erasure(std::forward(callable))} {} function& operator=(const function&) = delete; function& operator=(function&&) = default; ~function() = default; template decltype(auto) operator()(Args&&... args) { if(!ptr) throw std::bad_function_call{}; return ptr->operator()(std::forward(args)...); } void swap(function& other) {swap(ptr, other.ptr);} friend void swap(function& x, function& y) noexcept {x.swap(y);} operator bool() const noexcept {return ptr;} friend bool operator==(nullptr_t, const function& f) noexcept {return !f;} friend bool operator==(const function& f, nullptr_t) noexcept {return !f;} friend bool operator!=(nullptr_t, const function& f) noexcept {return f;} friend bool operator!=(const function& f, nullptr_t) noexcept {return f;} private: std::unique_ptr ptr; }; // deduction guide, not core //template //function(Callable&&) -> function::signature>; #endif // FUNCTION_HPP_INCLUDED