#pragma once #include #include #include #include "openvic-simulation/types/AnyRef.hpp" namespace OpenVic { // Based on https://github.com/think-cell/think-cell-library/blob/b9c84dd7fc926fad80829ed49705fa51afe36e87/tc/base/ref.h // Boost Software License - Version 1.0 - August 17th, 2003 // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. template struct FunctionRefBase { template using type_erased_function_ptr = Ret2 (*)(AnyRef, Args2...) noexcept(bNoExcept2); template struct make_type_erased_function_ptr final { type_erased_function_ptr operator()() const& noexcept { return [](AnyRef anyref, Args2... args) noexcept(bNoExcept2) -> Ret2 { // implicit cast of stateless lambda to function pointer return std::invoke( anyref.template get_ref(), std::forward(args)... ); // MAYTHROW unless bNoExcept }; } }; template struct make_type_erased_function_ptr final { type_erased_function_ptr operator()() const& noexcept { return [](AnyRef anyref, Args2... args) noexcept(bNoExcept2) { // implicit cast of stateless lambda to function pointer std::invoke(anyref.template get_ref(), std::forward(args)...); // MAYTHROW unless bNoExcept }; } }; template [[nodiscard]] static constexpr T& as_lvalue(T&& t) noexcept { return t; } FunctionRefBase(FunctionRefBase const&) noexcept = default; Ret operator()(Args... args) const& noexcept(bNoExcept) { return m_pfuncTypeErased(m_anyref, std::forward(args)...); // MAYTHROW unless bNoExcept } template struct decayed_derived_from : std::bool_constant::type, Base>> { static_assert(std::same_as, Base>); }; template requires(!decayed_derived_from::value) && std::invocable&, Args...> && (std::convertible_to< decltype(std::invoke(std::declval&>(), std::declval()...)), Ret> || std::is_void::value) FunctionRefBase(Func&& func) noexcept : m_pfuncTypeErased(make_type_erased_function_ptr, Ret, Args...> {}()), m_anyref(as_lvalue(func)) { static_assert( !std::is_member_function_pointer::value, "Raw member functions are not supported (how would you call them?). Pass in a lambda or use " "std::mem_fn instead." ); static_assert(!std::is_pointer::value, "Pass in functions rather than function pointers."); // Checking the noexcept value of the function call is commented out because MAYTHROW is widely used in generic code // such as for_each, range_adaptor... static_assert(!bNoExcept || // noexcept(std::declval&>()(std::declval()...))); } private: type_erased_function_ptr m_pfuncTypeErased; AnyRef m_anyref; }; template struct FunctionRef; template struct FunctionRef : FunctionRefBase { using base_ = typename FunctionRef::FunctionRefBase; using base_::base_; }; template struct FunctionRef : FunctionRefBase { using base_ = typename FunctionRef::FunctionRefBase; using base_::base_; }; }