/** * @file always_return.h * @author Nat Goodspeed * @date 2023-01-20 * @brief Call specified callable with arbitrary arguments, but always return * specified type. * * $LicenseInfo:firstyear=2023&license=viewerlgpl$ * Copyright (c) 2023, Linden Research, Inc. * $/LicenseInfo$ */ #if ! defined(LL_ALWAYS_RETURN_H) #define LL_ALWAYS_RETURN_H #include <type_traits> // std::enable_if, std::is_convertible namespace LL { #if __cpp_lib_is_invocable >= 201703L // C++17 template <typename CALLABLE, typename... ARGS> using invoke_result = std::invoke_result<CALLABLE, ARGS...>; #else // C++14 template <typename CALLABLE, typename... ARGS> using invoke_result = std::result_of<CALLABLE(ARGS...)>; #endif // C++14 /** * AlwaysReturn<T>()(some_function, some_args...) calls * some_function(some_args...). It is guaranteed to return a value of type * T, regardless of the return type of some_function(). If some_function() * returns a type convertible to T, it will convert and return that value. * Otherwise (notably if some_function() is void), AlwaysReturn returns * T(). * * When some_function() returns a type not convertible to T, if * you want AlwaysReturn to return some T value other than * default-constructed T(), pass that value to AlwaysReturn's constructor. */ template <typename DESIRED> class AlwaysReturn { public: /// pass explicit default value if other than default-constructed type AlwaysReturn(const DESIRED& dft=DESIRED()): mDefault(dft) {} // callable returns a type not convertible to DESIRED, return default template <typename CALLABLE, typename... ARGS, typename std::enable_if< ! std::is_convertible< typename invoke_result<CALLABLE, ARGS...>::type, DESIRED >::value, bool >::type=true> DESIRED operator()(CALLABLE&& callable, ARGS&&... args) { // discard whatever callable(args) returns std::forward<CALLABLE>(callable)(std::forward<ARGS>(args)...); return mDefault; } // callable returns a type convertible to DESIRED template <typename CALLABLE, typename... ARGS, typename std::enable_if< std::is_convertible< typename invoke_result<CALLABLE, ARGS...>::type, DESIRED >::value, bool >::type=true> DESIRED operator()(CALLABLE&& callable, ARGS&&... args) { return { std::forward<CALLABLE>(callable)(std::forward<ARGS>(args)...) }; } private: DESIRED mDefault; }; // specialize for AlwaysReturn<void> template <> struct AlwaysReturn<void> { public: AlwaysReturn() {} // callable returns a type not convertible to DESIRED, return default template <typename CALLABLE, typename... ARGS> void operator()(CALLABLE&& callable, ARGS&&... args) { // discard whatever callable(args) returns std::forward<CALLABLE>(callable)(std::forward<ARGS>(args)...); } }; /** * always_return<T>(some_function, some_args...) calls * some_function(some_args...). It is guaranteed to return a value of type * T, regardless of the return type of some_function(). If some_function() * returns a type convertible to T, it will convert and return that value. * Otherwise (notably if some_function() is void), always_return() returns * T(). */ template <typename DESIRED, typename CALLABLE, typename... ARGS> DESIRED always_return(CALLABLE&& callable, ARGS&&... args) { return AlwaysReturn<DESIRED>()(std::forward<CALLABLE>(callable), std::forward<ARGS>(args)...); } /** * make_always_return<T>(some_function) returns a callable which, when * called with appropriate some_function() arguments, always returns a * value of type T, regardless of the return type of some_function(). If * some_function() returns a type convertible to T, the returned callable * will convert and return that value. Otherwise (notably if * some_function() is void), the returned callable returns T(). * * When some_function() returns a type not convertible to T, if * you want the returned callable to return some T value other than * default-constructed T(), pass that value to make_always_return() as its * optional second argument. */ template <typename DESIRED, typename CALLABLE> auto make_always_return(CALLABLE&& callable, const DESIRED& dft=DESIRED()) { return [dft, callable = std::forward<CALLABLE>(callable)] (auto&&... args) { return AlwaysReturn<DESIRED>(dft)(callable, std::forward<decltype(args)>(args)...); }; } } // namespace LL #endif /* ! defined(LL_ALWAYS_RETURN_H) */