/// // function_ref - A low-overhead non-owning function // Written in 2017 by Simon Brand (@TartanLlama) // // To the extent possible under law, the author(s) have dedicated all // copyright and related and neighboring rights to this software to the // public domain worldwide. This software is distributed without any warranty. // // You should have received a copy of the CC0 Public Domain Dedication // along with this software. If not, see // . /// #ifndef TL_FUNCTION_REF_HPP #define TL_FUNCTION_REF_HPP #define TL_FUNCTION_REF_VERSION_MAJOR 1 #define TL_FUNCTION_REF_VERSION_MINOR 0 #define TL_FUNCTION_REF_VERSION_PATCH 0 #if (defined(_MSC_VER) && _MSC_VER == 1900) /// \exclude #define TL_FUNCTION_REF_MSVC2015 #endif #if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ !defined(__clang__)) /// \exclude #define TL_FUNCTION_REF_GCC49 #endif #if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \ !defined(__clang__)) /// \exclude #define TL_FUNCTION_REF_GCC54 #endif #if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ !defined(__clang__)) // GCC < 5 doesn't support overloading on const&& for member functions /// \exclude #define TL_FUNCTION_REF_NO_CONSTRR #endif #if __cplusplus > 201103L /// \exclude #define TL_FUNCTION_REF_CXX14 #endif // constexpr implies const in C++11, not C++14 #if (__cplusplus == 201103L || defined(TL_FUNCTION_REF_MSVC2015) || \ defined(TL_FUNCTION_REF_GCC49)) && \ !defined(TL_FUNCTION_REF_GCC54) /// \exclude #define TL_FUNCTION_REF_11_CONSTEXPR #else /// \exclude #define TL_FUNCTION_REF_11_CONSTEXPR constexpr #endif #include #include namespace tl { namespace detail { namespace fnref { // C++14-style aliases for brevity template using remove_const_t = typename std::remove_const::type; template using remove_reference_t = typename std::remove_reference::type; template using decay_t = typename std::decay::type; template using enable_if_t = typename std::enable_if::type; template using conditional_t = typename std::conditional::type; // std::invoke from C++17 // https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround template >::value>, int = 0> constexpr auto invoke(Fn &&f, Args &&... args) noexcept( noexcept(std::mem_fn(f)(std::forward(args)...))) -> decltype(std::mem_fn(f)(std::forward(args)...)) { return std::mem_fn(f)(std::forward(args)...); } template >{}>> constexpr auto invoke(Fn &&f, Args &&... args) noexcept( noexcept(std::forward(f)(std::forward(args)...))) -> decltype(std::forward(f)(std::forward(args)...)) { return std::forward(f)(std::forward(args)...); } // std::invoke_result from C++17 template struct invoke_result_impl; template struct invoke_result_impl< F, decltype(tl::detail::fnref::invoke(std::declval(), std::declval()...), void()), Us...> { using type = decltype(tl::detail::fnref::invoke(std::declval(), std::declval()...)); }; template using invoke_result = invoke_result_impl; template using invoke_result_t = typename invoke_result::type; template struct is_invocable_r_impl : std::false_type {}; template struct is_invocable_r_impl< typename std::is_convertible, R>::type, R, F, Args...> : std::true_type {}; template using is_invocable_r = is_invocable_r_impl; } // namespace detail } // namespace fnref /// A lightweight non-owning reference to a callable. /// /// Example usage: /// /// ```cpp /// void foo (function_ref func) { /// std::cout << "Result is " << func(21); //42 /// } /// /// foo([](int i) { return i*2; }); template class function_ref; /// Specialization for function types. template class function_ref { public: constexpr function_ref() noexcept = delete; /// Creates a `function_ref` which refers to the same callable as `rhs`. constexpr function_ref(const function_ref &rhs) noexcept = default; /// Constructs a `function_ref` referring to `f`. /// /// \synopsis template constexpr function_ref(F &&f) noexcept template , function_ref>::value && detail::fnref::is_invocable_r::value> * = nullptr> TL_FUNCTION_REF_11_CONSTEXPR function_ref(F &&f) noexcept : obj_(const_cast(reinterpret_cast(std::addressof(f)))) { callback_ = [](void *obj, Args... args) -> R { return detail::fnref::invoke( *reinterpret_cast::type>(obj), std::forward(args)...); }; } /// Makes `*this` refer to the same callable as `rhs`. TL_FUNCTION_REF_11_CONSTEXPR function_ref & operator=(const function_ref &rhs) noexcept = default; /// Makes `*this` refer to `f`. /// /// \synopsis template constexpr function_ref &operator=(F &&f) noexcept; template ::value> * = nullptr> TL_FUNCTION_REF_11_CONSTEXPR function_ref &operator=(F &&f) noexcept { obj_ = reinterpret_cast(std::addressof(f)); callback_ = [](void *obj, Args... args) { return detail::fnref::invoke( *reinterpret_cast::type>(obj), std::forward(args)...); }; return *this; } /// Swaps the referred callables of `*this` and `rhs`. constexpr void swap(function_ref &rhs) noexcept { std::swap(obj_, rhs.obj_); std::swap(callback_, rhs.callback_); } /// Call the stored callable with the given arguments. R operator()(Args... args) const { return callback_(obj_, std::forward(args)...); } private: void *obj_ = nullptr; R (*callback_)(void *, Args...) = nullptr; }; /// Swaps the referred callables of `lhs` and `rhs`. template constexpr void swap(function_ref &lhs, function_ref &rhs) noexcept { lhs.swap(rhs); } #if __cplusplus >= 201703L template function_ref(R (*)(Args...))->function_ref; // TODO, will require some kind of callable traits // template // function_ref(F) -> function_ref; #endif } // namespace tl #endif