summaryrefslogtreecommitdiff
path: root/indra/llcommon/always_return.h
blob: a206471da501fed41eda6b34590150c5258556bb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/**
 * @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;
    };

    /**
     * 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) */