/** * @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 // std::enable_if, std::is_convertible #include // std::forward namespace LL { #if __cpp_lib_is_invocable >= 201703L // C++17 template using invoke_result = std::invoke_result; #else // C++14 template using invoke_result = std::result_of; #endif // C++14 /** * AlwaysReturn()(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 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 ::type, DESIRED >::value, bool >::type=true> DESIRED operator()(CALLABLE&& callable, ARGS&&... args) { // discard whatever callable(args) returns std::forward(callable)(std::forward(args)...); return mDefault; } // callable returns a type convertible to DESIRED template ::type, DESIRED >::value, bool >::type=true> DESIRED operator()(CALLABLE&& callable, ARGS&&... args) { return { std::forward(callable)(std::forward(args)...) }; } private: DESIRED mDefault; }; // specialize for AlwaysReturn template <> struct AlwaysReturn { public: AlwaysReturn() {} // callable returns a type not convertible to DESIRED, return default template void operator()(CALLABLE&& callable, ARGS&&... args) { // discard whatever callable(args) returns std::forward(callable)(std::forward(args)...); } }; /** * always_return(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 DESIRED always_return(CALLABLE&& callable, ARGS&&... args) { return AlwaysReturn()(std::forward(callable), std::forward(args)...); } /** * make_always_return(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 auto make_always_return(CALLABLE&& callable, const DESIRED& dft=DESIRED()) { return [dft, callable = std::forward(callable)] (auto&&... args) { return AlwaysReturn(dft)(callable, std::forward(args)...); }; } } // namespace LL #endif /* ! defined(LL_ALWAYS_RETURN_H) */