summaryrefslogtreecommitdiff
path: root/indra/llcommon/always_return.h
blob: b99eb49096e6704499251b5fbed3fbe2716e0db6 (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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/**
 * @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) */