1 /*
2  *  Copyright (c) 2016, Facebook, Inc.
3  *  All rights reserved.
4  *
5  *  This source code is licensed under the BSD-style license found in the
6  *  LICENSE file in the root directory of this source tree. An additional grant
7  *  of patent rights can be found in the PATENTS file in the same directory.
8  */
9 
10 #ifndef FATAL_INCLUDE_fatal_type_registry_h
11 #define FATAL_INCLUDE_fatal_type_registry_h
12 
13 #include <type_traits>
14 
15 namespace fatal {
16 namespace detail {
17 namespace registry_impl {
18 template <typename, typename> class lookup;
19 struct abort_on_not_found;
20 } // namespace registry_impl {
21 } // namespace detail {
22 
23 /**
24  * Associates a type pair `Tag`/`Key` with a type, so thay it can be looked up
25  * from any scope.
26  *
27  * NOTE: this macro must be called from the same namespace of either the `Tag`
28  * or `Key`, or some other namespace that respects the C++ rules for Argument
29  * Dependent Lookup (http://en.cppreference.com/w/cpp/language/adl).
30  *
31  * See `registry_lookup` and `try_registry_lookup` below for more information
32  * on how to query the registry.
33  *
34  * Example:
35  *
36  *  struct algo {
37  *    FATAL_STR(quick_sort, "quicksort");
38  *    FATAL_STR(merge_sort, "merge sort");
39  *    FATAL_STR(insertion_sort, "insertion sort");
40  *  };
41  *
42  *  struct prop {
43  *    struct sorting {};
44  *    struct adaptive {};
45  *    struct stable {};
46  *    struct in_place {};
47  *  };
48  *
49  *  FATAL_REGISTER_TYPE(algo::quick_sort, prop::adaptive, std::false_type);
50  *  FATAL_REGISTER_TYPE(algo::quick_sort, prop::stable, std::false_type);
51  *  FATAL_REGISTER_TYPE(algo::quick_sort, prop::in_place, std::true_type);
52  *
53  *  FATAL_REGISTER_TYPE(algo::merge_sort, prop::adaptive, std::false_type);
54  *  FATAL_REGISTER_TYPE(algo::merge_sort, prop::stable, std::true_type);
55  *  FATAL_REGISTER_TYPE(algo::merge_sort, prop::in_place, std::false_type);
56  *
57  *  FATAL_REGISTER_TYPE(algo::insertion_sort, prop::adaptive, std::true_type);
58  *  FATAL_REGISTER_TYPE(algo::insertion_sort, prop::stable, std::true_type);
59  *  FATAL_REGISTER_TYPE(algo::insertion_sort, prop::in_place, std::true_type);
60  *
61  *  FATAL_REGISTER_TYPE(
62  *    algo,
63  *    prop::sorting,
64  *    type_list<algo::quick_sort, algo::merge_sort, algo::insertion_sort>
65  *  );
66  *
67  *  struct print_info_visitor {
68  *    template <typename Algorithm, std::size_t Index>
69  *    void operator ()(indexed_type_tag<Algorithm, Index>) const {
70  *      std::cout << Algorithm::z_data() << '{'
71  *        << " adaptive:" << registry_lookup<Algorithm, prop::adaptive>::value
72  *        << " stable:" << registry_lookup<Algorithm, prop::stable>::value
73  *        << " in-place:" << registry_lookup<Algorithm, prop::in_place>::value
74  *        << " }" << std::endl;
75  *    }
76  *  };
77  *
78  *  // prints `
79  *  //   quicksort { adaptive:0 stable:0 in-place:1 }
80  *  //   merge sort { adaptive:0 stable:1 in-place:0 }
81  *  //   insertion sort { adaptive:1 stable:1 in-place:1 }
82  *  // `
83  *  registry_lookup<algo, prop::sorting>::foreach(print_info_visitor());
84  *
85  * @author: Marcelo Juchem <marcelo@fb.com>
86  */
87 #define FATAL_REGISTER_TYPE(Tag, Key, ...) \
88   __VA_ARGS__ operator <<(Tag, Key *)
89 
90 /**
91  * Looks up a type previously associated by `FATAL_REGISTER_TYPE` with the type
92  * pair `Tag`/`Key`.
93  *
94  * If the type was not previosly associated then `Default` will be returned.
95  *
96  * Example:
97  *
98  *  struct my_tag {};
99  *  struct my_key_1 {};
100  *  struct my_key_2 {};
101  *  struct my_metadata_1 {};
102  *
103  *  FATAL_REGISTER_TYPE(my_tag, my_key_1, my_metadata_1);
104  *
105  *  // yields `my_metadata_1`
106  *  using result1 = try_registry_lookup<my_tag, my_key_1, void>;
107  *
108  *  // yields `void`
109  *  using result2 = try_registry_lookup<my_tag, my_key_2, void>;
110  *
111  * @author: Marcelo Juchem <marcelo@fb.com>
112  */
113 template <typename Tag, typename Key, typename Default>
114 using try_registry_lookup = typename detail::registry_impl::lookup<Tag, Default>
115   ::template key<Key>;
116 
117 /**
118  * Looks up a type previously associated by `FATAL_REGISTER_TYPE` with the type
119  * pair `Tag`/`Key`.
120  *
121  * If the type was not previosly associated then the compilation will fail.
122  *
123  * Example:
124  *
125  *  struct my_tag {};
126  *  struct my_key_1 {};
127  *  struct my_key_2 {};
128  *  struct my_metadata_1 {};
129  *
130  *  FATAL_REGISTER_TYPE(my_tag, my_key_1, my_metadata_1);
131  *
132  *  // yields `my_metadata_1`
133  *  using result1 = registry_lookup<my_tag, my_key_1>;
134  *
135  *  // fails compilation
136  *  using result2 = registry_lookup<my_tag, my_key_2>;
137  *
138  * @author: Marcelo Juchem <marcelo@fb.com>
139  */
140 template <typename Tag, typename Key>
141 using registry_lookup = try_registry_lookup<
142   Tag, Key, detail::registry_impl::abort_on_not_found
143 >;
144 
145 ////////////////////////////
146 // IMPLEMENTATION DETAILS //
147 ////////////////////////////
148 
149 namespace detail {
150 namespace registry_impl {
151 
152 template <typename Key>
153 struct registry_key {
154   using key = Key;
155 };
156 
157 template <typename Tag, typename Default>
158 class lookup {
159   struct impl {
160     template <
161       typename Key,
162       typename Metadata = decltype(
163         std::declval<Tag>() << static_cast<Key *>(nullptr)
164       )
165     >
166     static Metadata sfinae(registry_key<Key> *);
167 
168     template <typename... Args>
169     static Default sfinae(...);
170   };
171 
172 public:
173   template <typename Key>
174   using key = decltype(
175     impl::sfinae(static_cast<registry_key<Key> *>(nullptr))
176   );
177 };
178 
179 template <typename Tag>
180 class lookup<Tag, abort_on_not_found> {
181 public:
182   template <typename Key>
183   using key = decltype(std::declval<Tag>() << static_cast<Key *>(nullptr));
184 };
185 
186 } // namespace registry_impl {
187 } // namespace detail {
188 } // namespace fatal {
189 
190 #endif // FATAL_INCLUDE_fatal_type_registry_h
191