aboutsummaryrefslogtreecommitdiffstats
path: root/vespalib/src/vespa/vespalib/util/typify.h
blob: 3df59f488e511fc283d0542faef8b4e4ed299705 (plain) (blame)
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
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#pragma once

#include "traits.h"
#include <cstddef>
#include <utility>

namespace vespalib {

//-----------------------------------------------------------------------------

/**
 * Typification result for values resolving into actual types. Using
 * this exact template is not required, but the result type must have
 * the name 'type' for the auto-unwrapping performed by typify_invoke
 * to work.
 **/
template <typename T> struct TypifyResultType {
    using type = T;
};

/**
 * Typification result for values resolving into non-types. Using this
 * exact template is not required, but is supplied for
 * convenience. The resolved compile-time value should be called
 * 'value' for consistency across typifiers.
 **/
template <typename T, T VALUE> struct TypifyResultValue {
    static constexpr T value = VALUE;
};

/**
 * Typification result for values resolving into simple templates
 * (templated on one type). Using this exact template is not required,
 * but is supplied for convenience and as example. The resolved
 * template should be called 'templ' for consistency across typifiers.
 **/
template <template<typename> typename TT> struct TypifyResultSimpleTemplate {
    template <typename T> using templ = TT<T>;
};

/**
 * A Typifier is able to take a run-time value and resolve it into a
 * type, non-type constant value or a template. The resolve result is
 * passed to the specified function in the form of a thin result
 * wrapper.
 **/
struct TypifyBool {
    template <bool VALUE> using Result = TypifyResultValue<bool, VALUE>;
    template <typename F> static decltype(auto) resolve(bool value, F &&f) {
        if (value) {
            return f(Result<true>());
        } else {
            return f(Result<false>());
        }
    }
};

//-----------------------------------------------------------------------------

/**
 * Template used to combine individual typifiers into a typifier able
 * to resolve multiple types.
 **/
template <typename ...Ts> struct TypifyValue : Ts... { using Ts::resolve...; };

//-----------------------------------------------------------------------------

template <size_t N, typename Typifier, typename Target, typename ...Rs> struct TypifyInvokeImpl {
    static decltype(auto) select() {
        static_assert(sizeof...(Rs) == N);
        return Target::template invoke<Rs...>();
    }
    template <typename T, typename ...Args> static decltype(auto) select(T &&value, Args &&...args) {
        if constexpr (N == sizeof...(Rs)) {
            return Target::template invoke<Rs...>(std::forward<T>(value), std::forward<Args>(args)...);
        } else {
            return Typifier::resolve(value, [&](auto t)->decltype(auto)
                                     {
                                         using X = decltype(t);
                                         if constexpr (has_type_type<X>) {
                                             return TypifyInvokeImpl<N, Typifier, Target, Rs..., typename X::type>::select(std::forward<Args>(args)...);
                                         } else {
                                             return TypifyInvokeImpl<N, Typifier, Target, Rs..., X>::select(std::forward<Args>(args)...);
                                         }
                                     });
        }
    }
};

/**
 * Typify the N first parameters using 'Typifier' (typically an
 * instantiation of the TypifyValue template) and forward the
 * remaining parameters to the Target::invoke template function with
 * the typification results from the N first parameters as template
 * parameters. Note that typification results that are types are
 * unwrapped before being used as template parameters while
 * typification results that are non-types or templates are kept in
 * their wrappers when passed as template parameters. Please refer to
 * the unit test for examples.
 **/
template <size_t N, typename Typifier, typename Target, typename ...Args> decltype(auto) typify_invoke(Args && ...args) {
    static_assert(N > 0);
    return TypifyInvokeImpl<N,Typifier,Target>::select(std::forward<Args>(args)...);
}

//-----------------------------------------------------------------------------

}