├── .gitignore ├── LICENSE_1_0.txt ├── README.md ├── include ├── hop.hpp └── hop_utils.hpp ├── pictures ├── Luna_Meersau.png ├── Luna_Pacino.jpg ├── hop_experts_clipped.jpg └── luna.png └── test ├── CMakeLists.txt ├── hop_test.cpp ├── hop_test.sln ├── hop_test.vcxproj ├── hop_test.vcxproj.filters └── hop_test_2.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .vs/* 3 | /hop/.vs/hop/v16 4 | /hop/Debug 5 | /hop/x64/Debug 6 | /out/build 7 | -------------------------------------------------------------------------------- /LICENSE_1_0.txt: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hop 2 | homogeneous variadic function parameters 3 | 4 | Copyright Tobias Loew 2019. 5 | 6 | Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | 8 | 9 | # come and hop with me! 10 | ![luna-bunny-wants-to-hop](/pictures/Luna_Meersau.png) 11 | 12 | 13 | # what is hop 14 | hop is a small library that allows to create *proper* homogeneous variadic function parameters 15 | 16 | # what does *proper* mean 17 | *proper* means, that the functions you equip with hop's homogeneous variadic parameters are subject to C++ overload resolution. 18 | Let me show you an example: 19 | 20 | Suppose you want to have a function `foo` that accepts an arbitrary non-zero number of `int` arguments. 21 | 22 | The traditional solution from the pre C++11 age was to create overloads for `foo` up to a required/reasonable number of arguments 23 | ``` 24 | void foo(int n1); 25 | void foo(int n1, int n2); 26 | void foo(int n1, int n2, int n3); 27 | void foo(int n1, int n2, int n3, int n4); 28 | ``` 29 | 30 | Now, with C++11 and variadic Templates, we can write the whole overload-set as a single function 31 | ``` 32 | template 33 | void foo(Args&&... args); 34 | ``` 35 | but wait, we haven't said anything about `int` - specified as above, `foo` can be called with *any* list of parameters. So, how can we constrain `foo` to only accept argument-list containing one or more `int` arguments ? Of course, we use SFINAE 36 | ``` 37 | template 38 | using AllInts = typename std::conjunction...>::type; 39 | 40 | template::value, void>> 41 | void foo(Ts&& ... ts) {} 42 | ``` 43 | in the same way we can do this for `double` 44 | ``` 45 | template 46 | using AllDoubles = typename std::conjunction...>::type; 47 | 48 | template::value, void>> 49 | void foo(Ts&& ... ts) {} 50 | ``` 51 | But, when we use both overload set together, we get an error that `foo` is defined twice. ((C++17; §17.1.16) A template-parameter shall not be given default arguments by two different declarations in the same scope.) cf. https://www.fluentcpp.com/2018/05/15/make-sfinae-pretty-1-what-value-sfinae-brings-to-code/ 52 | 53 | One possible solution is 54 | ``` 55 | template 56 | using AllInts = typename std::conjunction...>::type; 57 | 58 | template::value, int> = 0> 59 | void foo(Ts&& ... ts) {} 60 | 61 | 62 | template 63 | using AllDoubles = typename std::conjunction...>::type; 64 | 65 | template::value, int> = 0> 66 | void foo(Ts&& ... ts) {} 67 | ``` 68 | But when we now call `foo(42)` or `foo(0.5, -1.3)` we always get ambigous call errors - and that's absolutely correct: both `foo` templates accept the argument-lists (`int` is convertible to `double` and vice-versa) and both take their arguments as *forwarding-references* so they're both equivalent perfect matches - bang! 69 | 70 | And here we are at the core of the problem: when we have multiple functions defined as above C++'s overload resolution won't step in to select the best match - they're all best matches (as long as we only consider only template functions). And here __hop__ can help... 71 | 72 | # creating overload-sets with __hop__ 73 | With __hop__ we define only a single overload of `foo` but with a quite sophisticated SFINAE condition: 74 | ``` 75 | using overloads = hop::ol_list < 76 | hop::ol>, 77 | hop::ol> 78 | >; 79 | 80 | 81 | template = 0 > 82 | void foo(Ts&& ... ts) { 83 | using OL = hop::enable_t; 84 | } 85 | ``` 86 | Now, we can call `foo` the same way we did for the traditional (bounded) overload-sets: 87 | ``` 88 | foo(42, 17); 89 | foo(1.5, -0.4, 12.0); 90 | foo(42, 0.5); // error: ambigous 91 | ``` 92 | Let's take a look a the types that are involved: 93 | - `hop::enable_t` is defined as `decltype(hop::enable())` and holds the information about the selected overload 94 | 95 | while the almost identical 96 | - `hop::enable_test` is defined as `decltype((hop::enable()), 0)` and can be used as SFINAE condition (as non-type template parameter of type `int`, which usually has the default value `0`). 97 | 98 | ``` 99 | using overloads = hop::ol_list < 100 | hop::ol>, 101 | hop::ol> 102 | >; 103 | 104 | template 105 | void output_as(T&& t) { 106 | std::cout << (Out)t << std::endl; 107 | } 108 | 109 | template = 0 > 110 | void foo(Ts&& ... ts) { 111 | using OL = hop::enable_t; 112 | 113 | if constexpr (hop::index
    ::value == 0) { 114 | std::cout << "got a bunch of ints\n"; 115 | (output_as(ts),...); 116 | std::cout << std::endl; 117 | } 118 | else 119 | if constexpr (hop::index
      ::value == 1) { 120 | std::cout << "got a bunch of doubles\n"; 121 | (output_as(ts), ...); 122 | std::cout << std::endl; 123 | } 124 | } 125 | ``` 126 | output 127 | ``` 128 | got a bunch of ints 129 | 42 130 | 17 131 | 132 | got a bunch of doubles 133 | 1.5 134 | -0.4 135 | 12 136 | ``` 137 | Alternatively, we can *tag* an overload, and test for it: 138 | ``` 139 | struct tag_ints {}; 140 | struct tag_doubles {}; 141 | 142 | using overloads = hop::ol_list < 143 | hop::tagged_ol>, 144 | hop::tagged_ol> 145 | >; 146 | 147 | template = 0 > 148 | void foo(Ts&& ... ts) { 149 | using OL = hop::enable_t; 150 | 151 | if constexpr (hop::has_tag::value) { 152 | // ... 153 | } 154 | else 155 | if constexpr (hop::has_tag::value) { 156 | // ... 157 | } 158 | } 159 | ``` 160 | Instead of using just a single entry-point for the overload-set (or as Quuxplusone called it: "one entry point to rule them all") you can use `hop::match_tag_t` to select only allow oveloads with the given tag. In the above case, we would have: 161 | ``` 162 | struct tag_ints {}; 163 | struct tag_doubles {}; 164 | 165 | using overloads = hop::ol_list < 166 | hop::tagged_ol>, 167 | hop::tagged_ol> 168 | >; 169 | 170 | template = 0 172 | > 173 | void foo(Ts&& ... ts) { 174 | // ... 175 | } 176 | 177 | template = 0 179 | > 180 | void foo(Ts&& ... ts) { 181 | // ... 182 | } 183 | ``` 184 | 185 | We can also tag types of an overload. This is useful, when we want to access the argument(s) belonging to a certain type of the overload: 186 | ``` 187 | struct tag_ints {}; 188 | struct tag_double {}; 189 | struct tag_numeric {}; 190 | 191 | using overloads = hop::ol_list < 192 | hop::tagged_ol>>, 193 | hop::tagged_ol>> 194 | >; 195 | 196 | template = 0 > 197 | void foo(Ts&& ... ts) { 198 | using OL = hop::enable_t; 199 | 200 | if constexpr (hop::has_tag::value) { 201 | auto&& numeric_args = hop::get_tagged_args(std::forward(ts)...); 202 | // numeric_args is a std::tuple containing all the int args 203 | // ... 204 | } 205 | else 206 | if constexpr (hop::has_tag::value) { 207 | auto&& numeric_args = hop::get_tagged_args(std::forward(ts)...); 208 | // numeric_args is a std::tuple containing all the double args 209 | // ... 210 | } 211 | } 212 | ``` 213 | 214 | Up to now, we can create non-empty homogeneous overloads for specific types. Let's see what else we can do with __hop__. 215 | A single overload `hop::ol<...>` consists of a list of types that are: 216 | - normal C++ types, like `int`, `vector`, user-defined types, which may be qualified. Those types are matched as if they were types of function arguments. 217 | - `hop::repeat`, `hop::repeat` at least `min` (and up to `max`) times the argument-list generated by `T`. If `max` is not specified, then `repeat` is unbounded. Multiple `repeat`s (even unbounded) in a single overload are possible! Also all other types/type-constructs after `repeat` are possible. 218 | - `hop::pack` or `hop::non_empty_pack` are aliases for `hop::repeat` resp. `hop::repeat`. 219 | - `hop::optional` is an alias for `hop::repeat` 220 | - `hop::eps` is a typedef for `hop::repeat` (it consumes no argument) 221 | - `hop::seq` appends the argument-lists generated by `T1`, ... , `TN` 222 | - `hop::alt` generates the argument-lists for `T1`, ... , `TN` and handles each as a separate case 223 | - `hop::cpp_defaulted_param>` creates an argument of type `T` or nothing. `hop::cpp_defaulted_param` creates a C++-style defult-param: types following a `hop::cpp_defaulted_param` must also be a `hop::cpp_defaulted_param` 224 | - `hop::general_defaulted_param>` creates an argument of type `T` or nothing. `hop::general_defaulted_param` can appear in any position of the type-list 225 | - `hop::fwd` is a place holder for a *forwarding-reference* and accepts any type 226 | - `hop::fwd_if class _If>` is a *forwarding-reference* with SFINAE condition applied to the actual parameter type 227 | - `hop::adapt` adapts an existing function as an overload: `hop::adapt` 228 | - `hop::adapted` can be used to adapt existing overload-sets or templates: 229 | ``` 230 | 231 | void bar(int n, std::string s) { 232 | ... 233 | } 234 | 235 | template 236 | auto qux(T&& t, double d, std::string const& s) { 237 | ... 238 | } 239 | 240 | struct adapt_qux { 241 | template 242 | static decltype(qux(std::declval()...)) forward(Ts&&... ts) { 243 | return qux(std::forward(ts)...); 244 | } 245 | }; 246 | 247 | using overloads_t = hop::ol_list < 248 | hop::adapt, 249 | hop::adapted 250 | >; 251 | 252 | template = 0 > 253 | decltype(auto) foo(Ts&& ... ts) { 254 | using OL = hop::enable_t; 255 | if constexpr (hop::is_adapted_v
        ) { 256 | return hop::forward_adapted
          (std::forward(ts)...); 257 | } 258 | } 259 | 260 | ``` 261 | - for template type deduction there is a *global* and a *local* version: 262 | - the *global* version corresponds to the usual template type deducing. Let's look a an example: 263 | ``` 264 | template 265 | using map_alias = std::mapconst&; 266 | 267 | template // !!! class T1 is required 268 | using set_alias = std::setconst&; 269 | 270 | ... 271 | 272 | hop::ol, hop::deduce> 273 | 274 | ... 275 | std::map my_map; 276 | std::set my_set; 277 | foo(my_map, my_set); 278 | 279 | std::set another_set; 280 | foo(my_map, another_set); // error 281 | ``` 282 | All arguments specified with `hop::deduce` take part in the global type-deduction, thus `foo` can only be called with a map and a set, where the set-type is the same as the mapped-to-type. 283 | Please note, that in the definition of the template-alias for `set_alias` the unused template type `class T1` is required, since `T1` and `T2` are deduced by matching `map_alias` and `set_alias` *simultaneously* 284 | and for all templates in the same order. Template non-type parameters are currently not supported. 285 | 286 | - in the *local* version the types are deduced independently for each argument, for example 287 | ``` 288 | template 289 | using map_vector = std::vectorconst&; 290 | 291 | ... 292 | 293 | hop::ol>> 294 | 295 | ... 296 | std::vector v1; 297 | std::vector v2; 298 | std::vector v3; 299 | foo(v1, v2, v3); 300 | ``` 301 | `foo` matches any list of `std::vector`s. Note, that this cannot be achived with global-deduction as the number of deduced-types is variable. 302 | - types can be tagged with `hop::tagged_ty` for accessing the arguments of an overload 303 | 308 | - finally, the following variations of `hop::ol<...>`: 309 | ``` 310 | template class _If, class... _Ty> 311 | using ol_if; 312 | ``` 313 | and 314 | ``` 315 | template class _If, class... _Ty> 316 | using tagged_ol_if; 317 | ``` 318 | allow to specify an additional SFINAE-condition which is applied to the actual parameter type pack. There is also version `tagged_ol_if_q` with expects a quoted meta-function as SFINAE-condition. 319 | 320 | All overloads for a single function are gathered in a `hop::ol_list<...>` 321 | 322 | The following grammar describes how to build argument-lists for overload-sets: 323 |
          
          324 | CppType =  
          325 |     any (possibly cv-qualified) C++ type
          326 | 
          327 | Type =  
          328 |     CppType
          329 |     | tagged_ty<tag, Type> 
          330 | 
          331 | Argument =
          332 |     Type 
          333 |     | repeat<Argument, min, max> 
          334 |     | seq<ArgumentList> 
          335 |     | alt<ArgumentList> 
          336 |     | cpp_defaulted_param<Type, init>
          337 |     | general_defaulted_param<Type, init> 
          338 |     | fwd
          339 |     | fwd_if<condition>
          340 |     
          341 | ArgumentList =
          342 |     Argument 
          343 |     | ArgumentList, Argument
          344 |     
          345 | 
          346 | 
          347 | 348 | Inside a function `hop` provides several templates and functions for inspecting the current overload and accessing function arguments: 349 | - `get_count...` returns the number of arguments (having a certain tag or satisfying a certain condition) 350 | ``` 351 | template 352 | constexpr size_t get_count(); 353 | 354 | template 355 | constexpr size_t get_tagged_count(); 356 | 357 | template 358 | constexpr size_t get_count_if_q(); 359 | 360 | template class _If> 361 | constexpr size_t get_count_if(); 362 | ``` 363 | 364 | - `get_args...(std::forward(ts))...)` returns the arguments (having a certain tag or satisfying a certain condition) as a tuple of references 365 | ``` 366 | template 367 | constexpr decltype(auto) get_args(Ts &&... ts); 368 | 369 | template 370 | constexpr decltype(auto) get_tagged_args(Ts &&... ts); 371 | 372 | template 373 | constexpr decltype(auto) get_args_if_q(Ts &&... ts); 374 | 375 | template class _If, class... Ts> 376 | constexpr decltype(auto) get_args_if(Ts &&... ts); 377 | ``` 378 | 379 | 380 | - 381 | ``` 382 | template 383 | constexpr decltype(auto) get_arg(Ts &&... ts); 384 | ``` 385 | returns 386 | template 387 | constexpr decltype(auto) get_tagged_args(Ts &&... ts); 388 | 389 | template 390 | constexpr decltype(auto) get_args_if_q(Ts &&... ts); 391 | 392 | template class _If, class... Ts> 393 | constexpr decltype(auto) get_args_if(Ts &&... ts); 394 | ``` 395 | 396 | 397 | // get_arg_or_call will always go for the first type with a matching tag 398 | template 399 | constexpr decltype(auto) get_arg_or_call(_FnOr&& _fnor, Ts&&... ts) { 400 | return impl::get_arg_or<_Overload, _Tag, tag_index, impl::or_behaviour::is_a_callable>(std::forward<_FnOr>(_fnor), std::forward(ts)...); 401 | } 402 | 403 | // get_arg_or will always go for the first type with a matching tag 404 | template 405 | constexpr decltype(auto) get_arg_or(_Or && _or, Ts &&... ts) { 406 | return impl::get_arg_or<_Overload, _Tag, tag_index, impl::or_behaviour::is_a_value>(std::forward<_Or>(_or), std::forward(ts)...); 407 | } 408 | 409 | 410 | // get_arg will always go for the first type with a matching tag 411 | template 412 | constexpr decltype(auto) get_arg(Ts &&... ts) { 413 | return impl::get_arg_or<_Overload, _Tag, tag_index, impl::or_behaviour::result_in_compilation_error>(0, std::forward(ts)...); 414 | } 415 | 416 | Examples can be found in test\hop_test.cpp. 417 | 418 | # hop on FluentC++ 419 | hop was the subject of a guest-post at FluentC++ "How to Define A Variadic Number of Arguments of the Same Type – Part 4", released at 07/01/20 (https://www.fluentcpp.com/2020/01/07/how-to-define-a-variadic-number-of-arguments-of-the-same-type-part-4/) 420 | 421 | 422 | # this library is presented to you by the hop-experts
          Luna & Rolf 423 | ![hop-experts](/pictures/hop_experts_clipped.jpg) 424 | 425 | # that's one small step for man, a lot of hops for a bunny! 426 | ![luna-bunny](/pictures/luna.png) 427 | `bunny(hop, hop, hop, ...);` 428 | -------------------------------------------------------------------------------- /include/hop.hpp: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////// 2 | // 3 | // hop library 4 | // 5 | // Copyright Tobias Loew 2019. Use, modification and 6 | // distribution is subject to the Boost Software License, Version 7 | // 1.0. (See accompanying file LICENSE_1_0.txt or copy at 8 | // http://www.boost.org/LICENSE_1_0.txt) 9 | // 10 | // For more information, see https://github.com/tobias-loew/hop 11 | // 12 | 13 | #ifndef HOP_HOP_HPP_INCLUDED 14 | #define HOP_HOP_HPP_INCLUDED 15 | 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #define HOP_ENABLE_REPEAT_OPTIMIZATION 25 | 26 | #ifndef HOP_MAX_DEDUCIBLE_TYPES 27 | #define HOP_MAX_DEDUCIBLE_TYPES 10 28 | #endif 29 | 30 | #define HOP_MAX_DEDUCIBLE_TYPES_END BOOST_PP_INC(HOP_MAX_DEDUCIBLE_TYPES) 31 | 32 | // homogeneous variadic overload sets 33 | namespace hop { 34 | using namespace boost::mp11; 35 | 36 | #if BOOST_VERSION < 107300 37 | // definition of mp_flatten from https://github.com/boostorg/mp11/blob/master/include/boost/mp11/algorithm.hpp (master, b24d228) 38 | // it is part of Boost.MP11 starting version 1.73 39 | // mp_flatten> 40 | namespace detail { 41 | 42 | template struct mp_flatten_impl { 43 | template using fn = T; 44 | // template using fn = mp_if, T, mp_list>; 45 | }; 46 | 47 | } // namespace detail 48 | 49 | template> using mp_flatten = mp_apply, L>, mp_clear>>; 50 | #endif 51 | 52 | 53 | #ifdef _DEBUG 54 | template 55 | struct debug_impl; 56 | template 57 | using debug = typename debug_impl::type; 58 | #endif 59 | 60 | // meta and fused from http://attugit.github.io/2015/02/Accessing-nth-element-of-parameter-pack/ (http://coliru.stacked-crooked.com/a/ab5f39be452d9448) 61 | namespace meta { 62 | struct ignore final { 63 | template 64 | constexpr ignore(Ts&& ...) noexcept {} 65 | }; 66 | 67 | template 68 | using eat = ignore; 69 | 70 | template 71 | using eat_n = ignore; 72 | } 73 | 74 | namespace fused { 75 | template 76 | constexpr decltype(auto) front(Tp&& t, Us&& ...) noexcept { 77 | return std::forward(t); 78 | } 79 | 80 | template 81 | constexpr decltype(auto) back(Tp&& t, Us&& ... us) noexcept { 82 | return [](meta::eat..., auto&& x) -> decltype(x) { 83 | return std::forward(x); 84 | }(std::forward(t), std::forward(us)...); 85 | } 86 | 87 | namespace detail { 88 | template > 89 | struct at; 90 | 91 | template 92 | struct at> { 93 | template 94 | constexpr decltype(auto) operator()(meta::eat_n..., Tp&& x, 95 | Us&& ...) const noexcept { 96 | return std::forward(x); 97 | } 98 | }; 99 | } 100 | 101 | template 102 | constexpr decltype(auto) nth(Ts&& ... args) { 103 | return detail::at{}(std::forward(args)...); 104 | } 105 | } 106 | 107 | 108 | template struct dependent_false : mp_false {}; 109 | 110 | 111 | /////////////////////////////////////////////////////////////////////////////////// 112 | // 113 | // parameter-list building blocks - the grammar 114 | // 115 | 116 | // template to create a repeated [min, .. , max] parameter 117 | static constexpr size_t infinite = std::numeric_limits::max(); 118 | 119 | // repetition 120 | template 121 | struct repeat; 122 | 123 | 124 | // grouping a type-sequence 125 | template 126 | struct seq; 127 | 128 | // alternative types 129 | template 130 | struct alt; 131 | 132 | 133 | 134 | // template to tag a type 135 | template 136 | struct tagged_ty; 137 | 138 | 139 | // template to gather a parameter-list 140 | // gather is not yet implemented !!! 141 | template 142 | struct gather; 143 | 144 | 145 | // syntactic sugar 146 | 147 | // parameter pack 148 | template 149 | using pack = repeat<_Ty, 0, infinite>; 150 | 151 | // non-empty parameter pack 152 | template 153 | using non_empty_pack = repeat<_Ty, 1, infinite>; 154 | 155 | // optional parameter (w/o default value) 156 | template 157 | using optional = repeat<_Ty, 0, 1>; 158 | 159 | // exactly n-times 160 | template 161 | using n_times = repeat<_Ty, _times, _times>; 162 | 163 | // no parameter (epsilon) 164 | using eps = repeat; 165 | 166 | 167 | // template access parameter-type in tagged_ty 168 | namespace impl { 169 | template 170 | struct remove_tag { 171 | using type = _Ty; 172 | }; 173 | 174 | template 175 | struct remove_tag> { 176 | using type = typename remove_tag<_Ty>::type; 177 | }; 178 | } 179 | 180 | // template to provide a default-created value 181 | namespace impl { 182 | template 183 | struct default_create { 184 | constexpr decltype(auto) operator()() const { return typename remove_tag<_Ty>::type{}; } 185 | }; 186 | } 187 | 188 | // template to create C++-style defaulted parameter 189 | // that can appear only at end of the parameter list 190 | template> 191 | struct cpp_defaulted_param; 192 | 193 | // template to create a parameter with a default value 194 | // that can appear at any position of the parameter list 195 | template> 196 | struct general_defaulted_param; 197 | 198 | // template to create a forwarded parameter: _Ty has to be a quoted meta-function 199 | template 200 | struct tmpl_q; 201 | 202 | // template to create a forwarded parameter: 203 | // F has to be a meta-function in the sense of Boost.MP11 204 | template class F> 205 | using tmpl = tmpl_q>; 206 | 207 | // struct to create a forward-reference 208 | namespace impl { 209 | template 210 | using fwd_helper_t = _Ty&&; 211 | } 212 | using fwd = tmpl; 213 | 214 | namespace impl { 215 | template 216 | using true_t = mp_true; 217 | 218 | struct none_tag {}; 219 | 220 | 221 | template 222 | struct if_test { 223 | private: 224 | struct invalid_type_tag; 225 | 226 | public: 227 | using _if = _If; 228 | 229 | template 230 | using fn = mp_if< 231 | typename _If::template fn, 232 | T&&, 233 | invalid_type_tag 234 | >; 235 | 236 | }; 237 | 238 | template 239 | struct if_not_test { 240 | private: 241 | struct invalid_type_tag; 242 | 243 | public: 244 | template 245 | using fn = mp_if_c< 246 | !_If::template fn::value, 247 | T&&, 248 | invalid_type_tag 249 | >; 250 | 251 | }; 252 | 253 | 254 | template class Pattern_> 255 | struct deducer_local { 256 | 257 | // here we really would like to write the following test function 258 | // but since Pattern_ may be an alias template instantiating it with a pack is not allowed 259 | // template class Pattern, class... T> 260 | // static mp_list> test(Pattern); 261 | 262 | #define HOP_MACRO_LOCAL_DEDUCER_TEST(z, n, data) \ 263 | template class Pattern BOOST_PP_ENUM_TRAILING_PARAMS(n, class T)> \ 264 | static mp_list> test(Pattern); 265 | 266 | // overloads to deduce 1 - 10 template types 267 | BOOST_PP_REPEAT_FROM_TO(1, HOP_MAX_DEDUCIBLE_TYPES_END, HOP_MACRO_LOCAL_DEDUCER_TEST, _) 268 | 269 | #undef HOP_MACRO_LOCAL_DEDUCER_TEST 270 | 271 | template class Pattern> 272 | static mp_list> test(...); 273 | 274 | template 275 | using fn = mp_first(std::declval()))>; 276 | 277 | template 278 | using deduced = mp_second(std::declval()))>; 279 | }; 280 | 281 | 282 | template class Pattern_> 283 | struct deducer_mixed { 284 | 285 | // here we really would like to write the following test function 286 | // but since Pattern_ may be an alias template instantiating it with a pack is not allowed 287 | // template class Pattern, class... T> 288 | // static mp_list> test(Pattern); 289 | 290 | #define HOP_MACRO_LOCAL_DEDUCER_TEST(z, n, data) \ 291 | template class Pattern BOOST_PP_ENUM_TRAILING_PARAMS(n, class T)> \ 292 | static mp_list> test(Pattern); 293 | 294 | // overloads to deduce 1 - 10 template types 295 | BOOST_PP_REPEAT_FROM_TO(1, HOP_MAX_DEDUCIBLE_TYPES_END, HOP_MACRO_LOCAL_DEDUCER_TEST, _) 296 | 297 | #undef HOP_MACRO_LOCAL_DEDUCER_TEST 298 | 299 | template class Pattern> 300 | static mp_list> test(...); 301 | 302 | template 303 | using fn = mp_first(std::declval()))>; 304 | 305 | template 306 | using deduced = mp_second(std::declval()))>; 307 | 308 | using global_deduction_bindings_type = GlobalDeductionBindings; 309 | }; 310 | 311 | 312 | 313 | // global deduction 314 | template class Pattern> 315 | struct pattern_helper; 316 | 317 | #define HOP_MACRO_PATTERN_HELPER(z, n, data) \ 318 | template class Pattern> \ 319 | struct pattern_helper { \ 320 | template \ 321 | using fn = Pattern; \ 322 | }; 323 | 324 | BOOST_PP_REPEAT_FROM_TO(1, HOP_MAX_DEDUCIBLE_TYPES_END, HOP_MACRO_PATTERN_HELPER, _) 325 | 326 | #undef HOP_MACRO_PATTERN_HELPER 327 | 328 | 329 | template class Pattern> 330 | struct deduction_pattern { 331 | // this is for perfect forwarding of a single argument; deduction is done in deducer_t 332 | template 333 | using fn = T&&; 334 | 335 | 336 | 337 | #define HOP_MACRO_ARITY_TEST(z, n, data) \ 338 | template class Pattern_ BOOST_PP_ENUM_TRAILING_BINARY_PARAMS(n, class T, = int BOOST_PP_INTERCEPT)> \ 339 | static std::integral_constant arity_test(Pattern_); 340 | 341 | 342 | BOOST_PP_REPEAT_FROM_TO(1, HOP_MAX_DEDUCIBLE_TYPES_END, HOP_MACRO_ARITY_TEST, _) 343 | 344 | #undef HOP_MACRO_ARITY_TEST 345 | 346 | template class Pattern_> 347 | static std::integral_constant arity_test(...); //fault 348 | 349 | 350 | 351 | template 352 | using arity = decltype(arity_test(std::declval<_Ty>())); 353 | 354 | template 355 | using pattern = pattern_helper::value, Pattern>; 356 | }; 357 | 358 | 359 | template 360 | struct lazy_expand { 361 | using type = T; 362 | }; 363 | 364 | 365 | 366 | template 367 | struct deducer_t { 368 | 369 | // here we really would like to write the following test function 370 | /* 371 | 372 | template class... Pattern, class... T> 373 | static mp_list> test(Pattern...); 374 | 375 | */ 376 | // but since Pattern may be an alias template instantiating it with a pack is not allowed 377 | 378 | // overloads to deduce 0 - 10 template types 379 | //template class... Pattern, class... _Tys> 380 | //static mp_list, mp_list<>> test(Pattern<>..., _Tys&&...); 381 | 382 | //template class lazy_expander, class... _Tys> 383 | //static mp_list> test(typename lazy_expander::type::template fn<_Tys...>...); 384 | 385 | #define HOP_MACRO_DEDUCER_T_TEST(z, n, data) \ 386 | template class lazy_expander BOOST_PP_ENUM_TRAILING_PARAMS(n, class T)> \ 387 | static mp_list> test(typename lazy_expander::type::template fn...); 388 | 389 | // overloads to deduce 1 - 10 template types 390 | BOOST_PP_REPEAT_FROM_TO(1, HOP_MAX_DEDUCIBLE_TYPES_END, HOP_MACRO_DEDUCER_T_TEST, _) 391 | 392 | #undef HOP_MACRO_DEDUCER_T_TEST 393 | 394 | 395 | template class lazy_expander> 396 | static mp_list> test(...); 397 | 398 | template 399 | using fn = mp_first(std::declval()...))>; 400 | 401 | template 402 | using deduced = mp_second(std::declval()...))>; 403 | }; 404 | } 405 | 406 | 407 | 408 | // forward-reference guarded by quoted meta-function 409 | template 410 | using fwd_if_q = tmpl_q>; 411 | 412 | // forward-reference guarded by meta-function (Boost.MP11-style) 413 | template class _If> 414 | using fwd_if = fwd_if_q>; 415 | 416 | 417 | template 418 | using fwd_if_not_q = tmpl_q>; 419 | 420 | template class _If> 421 | using fwd_if_not = fwd_if_not_q>; 422 | 423 | 424 | 425 | template class Pattern> 426 | using deduce_local = fwd_if_q>; 427 | 428 | template class Pattern> 429 | using deduce = tmpl_q< impl::deduction_pattern>; 430 | 431 | 432 | template 433 | struct global_deduction_binding { 434 | static constexpr size_t type_index{ type_index_ }; 435 | using tag_type = Tag; 436 | }; 437 | 438 | template class Pattern> 439 | using deduce_mixed = fwd_if_q>; 440 | 441 | 442 | namespace impl { 443 | template 444 | struct adapter { 445 | template 446 | static decltype(f(std::declval()...)) forward(Ts&&... ts) { 447 | return f(std::forward(ts)...); 448 | } 449 | 450 | }; 451 | 452 | template 453 | struct adapter_test { 454 | /* 455 | * // adapter_test::fn is only called inside enable_if-condition of _single_overload_helper, 456 | * // so fallback option is not needed because of SFINAE 457 | * // furthermore, test(...) gives wrong results for adapted functions 458 | * // with all parameters defaulted and called with empty argument-list 459 | * // the reason is because calling functions 460 | * // test(...); 461 | * // test(int i = 0); 462 | * // with zero arguments is AMBIGUOUS !!! 463 | * 464 | * template 465 | * static true_t()...))> test(int, Ts&&... ts); 466 | * 467 | * 468 | * static std::false_type test(...); 469 | * 470 | * template 471 | * using fn = decltype(test(0, std::declval()...)); 472 | */ 473 | 474 | // adding leading int dummy-parameter to prevent the test(int i = 0), test(...) ambiguity 475 | 476 | template 477 | static true_t()...))> test(int, Ts&&... ts); 478 | // ^ dummy-parameter 479 | 480 | 481 | static std::false_type test(...); 482 | 483 | template 484 | using fn = decltype(test(0, std::declval()...)); 485 | // ^ int dummy-argument 486 | }; 487 | } 488 | 489 | 490 | 491 | namespace impl { 492 | 493 | template 494 | struct is_repeat : mp_false {}; 495 | 496 | template 497 | struct is_repeat> : mp_true {}; 498 | 499 | template 500 | struct remove_repeat { 501 | using type = T; 502 | }; 503 | 504 | template 505 | struct remove_repeat> { 506 | using type = T; 507 | }; 508 | 509 | template 510 | using remove_repeat_t = typename remove_repeat::type; 511 | 512 | 513 | 514 | template 515 | struct is_cpp_defaulted_param : mp_false {}; 516 | 517 | template 518 | struct is_cpp_defaulted_param> : mp_true {}; 519 | 520 | template 521 | struct remove_cpp_defaulted_param { 522 | using type = T; 523 | }; 524 | 525 | template 526 | struct remove_cpp_defaulted_param> { 527 | using type = T; 528 | }; 529 | template 530 | using remove_cpp_defaulted_param_t = typename remove_cpp_defaulted_param::type; 531 | 532 | 533 | template 534 | struct is_general_defaulted_param : mp_false {}; 535 | 536 | template 537 | struct is_general_defaulted_param> : mp_true {}; 538 | 539 | template 540 | struct remove_general_defaulted_param { 541 | using type = T; 542 | }; 543 | 544 | template 545 | struct remove_general_defaulted_param> { 546 | using type = T; 547 | }; 548 | template 549 | using remove_general_defaulted_param_t = typename remove_general_defaulted_param::type; 550 | 551 | 552 | template 553 | struct is_defaulted_param : mp_or< is_cpp_defaulted_param, is_general_defaulted_param> {}; 554 | 555 | template 556 | struct is_not_defaulted_param : mp_not> {}; 557 | 558 | template 559 | struct remove_defaulted_param { 560 | using type = T; 561 | }; 562 | template 563 | struct remove_defaulted_param> { 564 | using type = T; 565 | }; 566 | template 567 | struct remove_defaulted_param> { 568 | using type = T; 569 | }; 570 | template 571 | using remove_defaulted_param_t = typename remove_defaulted_param::type; 572 | 573 | 574 | 575 | 576 | 577 | template 578 | struct get_init_type { 579 | using type = T; 580 | }; 581 | 582 | template 583 | struct get_init_type> { 584 | using type = Init; 585 | }; 586 | template 587 | struct get_init_type> { 588 | using type = Init; 589 | }; 590 | template 591 | using get_init_type_t = typename get_init_type::type; 592 | 593 | 594 | 595 | template 596 | struct is_tmpl : mp_false {}; 597 | 598 | template 599 | struct is_tmpl> : mp_true {}; 600 | 601 | 602 | 603 | template 604 | struct get_tmpl { 605 | using type = T; 606 | }; 607 | 608 | template 609 | struct get_tmpl> { 610 | using type = T; 611 | }; 612 | 613 | 614 | 615 | 616 | 617 | template 618 | struct unpack_replace_tmpl { 619 | using type = T; 620 | }; 621 | 622 | template 623 | struct unpack_replace_tmpl, Arg> { 624 | using type = mp_invoke_q; 625 | }; 626 | 627 | 628 | template 629 | struct unpack_replace_tmpl, Arg> { 630 | using type = typename unpack_replace_tmpl<_Ty, Arg>::type; 631 | }; 632 | 633 | 634 | 635 | // helper types for default-param 636 | 637 | template 638 | struct defaulted_type_t { 639 | using type = _Ty; 640 | static constexpr bool specified = _specified; 641 | static constexpr bool general = _general; 642 | }; 643 | 644 | 645 | 646 | template 647 | struct is_default_specified : mp_false {}; 648 | 649 | template 650 | struct is_default_specified> : 651 | mp_bool<_specified && !_general> {}; 652 | 653 | template 654 | using is_default_specified_t = typename is_default_specified<_Ty>::type; 655 | 656 | 657 | 658 | template 659 | struct is_defaulted_type : mp_false {}; 660 | 661 | template 662 | struct is_defaulted_type> : mp_true {}; 663 | 664 | template 665 | using is_defaulted_type_t = typename is_defaulted_type<_Ty>::type; 666 | 667 | 668 | template 669 | struct is_seq : mp_false {}; 670 | 671 | template 672 | struct is_seq> : mp_true {}; 673 | 674 | 675 | template 676 | struct is_alt : mp_false {}; 677 | 678 | template 679 | struct is_alt> : mp_true {}; 680 | 681 | 682 | template 683 | struct is_gather : mp_false {}; 684 | 685 | template 686 | struct is_gather> : mp_true {}; 687 | 688 | 689 | template 690 | struct is_hop_type_builder : mp_or< 691 | is_repeat<_Ty>, 692 | is_defaulted_param<_Ty>, 693 | is_general_defaulted_param<_Ty>, 694 | is_seq<_Ty>, 695 | is_alt<_Ty>, 696 | is_gather<_Ty> 697 | > {}; 698 | 699 | template 700 | struct is_hop_type_builder> : is_hop_type_builder<_Ty> {}; 701 | 702 | 703 | 704 | 705 | template 706 | struct any_deduction_pattern { 707 | template 708 | using fn = Arg&&; 709 | }; 710 | 711 | 712 | template 713 | struct make_deduction_pattern { 714 | using type = any_deduction_pattern; 715 | }; 716 | 717 | template class Pattern, class Arg> 718 | struct make_deduction_pattern>, Arg> { 719 | using type = typename deduction_pattern:: template pattern; 720 | }; 721 | 722 | template 723 | struct make_deduction_pattern, Arg> : make_deduction_pattern<_Ty, Arg> { 724 | using type = typename make_deduction_pattern<_Ty, Arg>::type; 725 | }; 726 | 727 | 728 | 729 | template 730 | struct deduction_helper; 731 | 732 | template 733 | struct deduction_helper, mp_list<_FormalTys...>> { 734 | 735 | static constexpr bool has_no_deduction_pattern = (std::is_same_v::type, any_deduction_pattern<_ActualTys>> && ...); 736 | 737 | static constexpr bool value = has_no_deduction_pattern || 738 | deducer_t::type...>::template fn<_ActualTys...>::value; 739 | 740 | using deduced_t = 741 | mp_eval_if_c< has_no_deduction_pattern, 742 | mp_list<>, 743 | deducer_t::type...>::template deduced, 744 | _ActualTys... 745 | >; 746 | }; 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | template 755 | struct mixed_deduction_get_global_deduced_types { 756 | // empty by default, i.e. no further constraints 757 | using type = mp_list<>; 758 | }; 759 | 760 | 761 | template class Pattern> 762 | struct mixed_deduction_get_global_deduced_types<_ActualTy, tmpl_q>>> { 763 | // create list of 2-element-lists 764 | 765 | 766 | using deduced_type_list = typename impl::deducer_mixed::template deduced<_ActualTy>; 767 | 768 | struct get_deduced_types { 769 | template 770 | using fn = mp_list< 771 | typename T::tag_type, // tag 772 | mp_at_c // get deduced type by index 773 | >; 774 | }; 775 | 776 | 777 | using type = 778 | mp_transform_q< 779 | get_deduced_types, 780 | GlobalDeductionBindings 781 | >; 782 | 783 | }; 784 | 785 | 786 | template 787 | struct mixed_deduction_helper; 788 | 789 | template 790 | struct mixed_deduction_helper, mp_list<_FormalTys...>> { 791 | 792 | 793 | using global_deduced_type_lists = mp_list::type...>; 794 | using global_deduced_types = mp_flatten; 795 | 796 | // turn global_deduced_types into map (tag -> {deduced-type}), finally check that all {deduced-type} are the same ! 797 | 798 | struct tag_inconsistent; 799 | 800 | template 801 | struct consistency_check { 802 | template 803 | using fn = mp_if_c< 804 | (!std::is_same_v<_T1, _T2>) || std::is_same_v<_T1, tag_inconsistent> || std::is_same_v<_T2, tag_inconsistent>, 805 | tag_inconsistent, 806 | _T1 807 | >; 808 | }; 809 | 810 | 811 | template 812 | using add_to_map = mp_map_update_q<_Map, _Global_Deduced_Type, consistency_check>>; 813 | 814 | 815 | // fold list to map and check if types with same tag are the same 816 | using map_type = mp_fold, add_to_map>; 817 | 818 | 819 | template 820 | struct contains_tag_inconsistent : mp_same< mp_second<_MapElement>, tag_inconsistent> {}; 821 | 822 | // finally check if there is any 'tag_inconsistent' in the map 823 | static constexpr bool value = 824 | mp_none_of< 825 | map_type, 826 | contains_tag_inconsistent 827 | >::value; 828 | }; 829 | 830 | 831 | 832 | ////////////////////////////////////////////////////////////////////////////////////////////////// 833 | // core template that generates the call-operator to test 834 | template 835 | struct _single_overload_helper; 836 | 837 | template 838 | struct _single_overload_helper<_Idx, mp_list<_ExpandedTys...>, mp_list<_ExpectedTys...>, mp_list<_DefaultedTys...>, _Info, _If, _Expansion> { 839 | 840 | using expanded_types = mp_list<_ExpandedTys...>; 841 | using expected_types = mp_list<_ExpectedTys...>; 842 | using defaulted_types = mp_list<_DefaultedTys...>; 843 | //using h1 = debug<_If>; 844 | //using h = debug::value>>; 845 | 846 | template< 847 | class... T, 848 | std::enable_if_t< 849 | mp_invoke_q<_If, T...>::value 850 | && 851 | deduction_helper, expanded_types>::value 852 | && 853 | mixed_deduction_helper, expanded_types>::value 854 | , int* > = nullptr 855 | > 856 | constexpr mp_list< 857 | _Info, //static constexpr size_t information_index = 0; 858 | std::integral_constant, //static constexpr size_t overload_index = 1; 859 | expected_types, //static constexpr size_t expected_parameter_overload_type_index = 2; // the type-list of the selected overload WITH default-params 860 | defaulted_types, //static constexpr size_t default_info_type_index = 3; // the original secification of the selected overload with default-info 861 | expanded_types, //static constexpr size_t formal_parameter_overload_type_index = 4; // the type-list of the selected overload 862 | mp_list, //static constexpr size_t deduced_local_parameter_overload_type_index = 5; // the local deduced types 863 | typename deduction_helper, expanded_types>::deduced_t,//static constexpr size_t deduced_parameter_overload_type_index = 6; // the (global) deduced types 864 | _Expansion //static constexpr size_t expansion_type_index = 7; // type holding the inductive expansion of the types 865 | > test(typename unpack_replace_tmpl<_ExpandedTys, T>::type...) const; 866 | }; 867 | 868 | 869 | template 870 | struct _single_overload; 871 | 872 | template 873 | struct _single_overload<_Idx, mp_list, _Info, _If>> 874 | :_single_overload_helper< 875 | _Idx, 876 | mp_flatten>, 877 | mp_flatten>, 878 | mp_flatten>, 879 | _Info, 880 | _If, 881 | mp_list<_ArgTys...> 882 | > { 883 | using _single_overload_helper< 884 | _Idx, 885 | mp_flatten>, 886 | mp_flatten>, 887 | mp_flatten>, 888 | _Info, 889 | _If, 890 | mp_list<_ArgTys...> 891 | >::test; 892 | }; 893 | 894 | 895 | 896 | //////////////////////////////////////////////////////////////////////////////////////////////////// 897 | // 898 | // calculation of the valid number-of-arguments 899 | // 900 | 901 | struct tag_args_min; 902 | struct tag_args_max; 903 | 904 | template 905 | struct _arg_count { 906 | static constexpr size_t value = 1; 907 | }; 908 | 909 | 910 | template 911 | struct _arg_count> { 912 | static constexpr size_t value = _min * _arg_count::value; 913 | }; 914 | template 915 | struct _arg_count> { 916 | static constexpr size_t value = _max * _arg_count::value; 917 | }; 918 | 919 | 920 | template 921 | struct _arg_count> { 922 | static constexpr size_t value = 0; 923 | }; 924 | 925 | template 926 | struct _arg_count> { 927 | static constexpr size_t value = 0; 928 | }; 929 | 930 | 931 | template 932 | struct _arg_count<_Tag, seq<_Tys...>> { 933 | static constexpr size_t value = (_arg_count<_Tag, _Tys>::value + ... + 0); 934 | }; 935 | 936 | template 937 | struct _arg_count> { 938 | static constexpr size_t value = std::min({ _arg_count::value... }); 939 | }; 940 | 941 | template 942 | struct _arg_count> { 943 | static constexpr size_t value = std::max({ _arg_count::value... }); 944 | }; 945 | 946 | template 947 | struct _arg_count<_Tag, gather<_TyTag, _Ty>> { 948 | static constexpr size_t value = _arg_count<_Tag, _Ty>::value; 949 | }; 950 | 951 | 952 | constexpr size_t add_inifinite_aware(size_t lhs, size_t rhs) { 953 | return lhs == infinite || rhs == infinite 954 | ? infinite 955 | : (lhs + rhs < lhs) // overflow ? 956 | ? infinite 957 | : lhs + rhs 958 | ; 959 | } 960 | 961 | constexpr size_t minus_non_negative(size_t lhs, size_t rhs) { 962 | return rhs >= lhs 963 | ? 0 964 | : lhs == infinite 965 | ? infinite 966 | : lhs - rhs 967 | ; 968 | } 969 | 970 | 971 | struct _add_args { 972 | template 973 | static constexpr size_t arg_min = _arg_count::value; 974 | template 975 | static constexpr size_t arg_max = _arg_count::value; 976 | 977 | template 978 | using add_min_max = mp_list_c::value, 980 | mp_at_c<_Front, 5>::value, 981 | arg_min<_Ty>, 982 | arg_max<_Ty>, 983 | add_inifinite_aware(mp_at_c<_Front, 4>::value, arg_min<_Ty>), 984 | add_inifinite_aware(mp_at_c<_Front, 5>::value, arg_max<_Ty>) 985 | >; 986 | 987 | 988 | template 989 | using fn = mp_push_front<_Value, add_min_max, _Ty>>; 990 | }; 991 | 992 | 993 | template 994 | using _minmax_args_count_list = mp_reverse_fold_q<_Ty, mp_list>, _add_args>; 995 | 996 | 997 | // forward declared 998 | template 999 | struct _expand_overload_set_helper; 1000 | template 1001 | struct _expand_overload_set_impl; 1002 | 1003 | // helper-template to store argument-type info 1004 | template 1005 | struct argument_t { 1006 | using expanded_type_list = _Expanded; // type validated against actual paramters 1007 | using expected_type_list = _Expected; // type expected inside the function 1008 | using defaulted_type_list = _Defaulted; // kind of defaulted type 1009 | }; 1010 | 1011 | 1012 | template 1013 | struct _expand_current { 1014 | static_assert(_arg_count == 1); 1015 | 1016 | using argument_type = argument_t, mp_list<_Ty>, mp_list<_Ty>>; 1017 | using single_type = mp_list; // generated argument-list 1018 | using type = mp_list; // generated argument-lists 1019 | }; 1020 | 1021 | #if defined(HOP_ENABLE_REPEAT_OPTIMIZATION) 1022 | // speed-up the simple cases 1023 | template 1024 | struct _expand_current<_arg_count, repeat<_Ty, _min, _max>, typename std::enable_if::value>::type> { 1025 | static_assert(_arg_count >= _min && _arg_count <= _max); 1026 | 1027 | using argument_type = argument_t< mp_repeat_c, _arg_count>, mp_repeat_c, _arg_count>, mp_repeat_c, _arg_count>>; 1028 | using single_type = mp_list; // generated argument-list 1029 | using type = mp_list; // generated argument-lists 1030 | }; 1031 | #endif 1032 | 1033 | 1034 | // general case for 0 1035 | template 1036 | struct _expand_current<0, repeat<_Ty, _min, _max> 1037 | #if defined(HOP_ENABLE_REPEAT_OPTIMIZATION) 1038 | , typename std::enable_if::value>::type 1039 | #endif 1040 | > { 1041 | static constexpr size_t _arg_count = 0; 1042 | 1043 | static_assert(_arg_count >= _min && _arg_count <= _max); 1044 | 1045 | using type = mp_list>; // generated argument-lists 1046 | }; 1047 | 1048 | 1049 | 1050 | // induction case for i > 0 1051 | template 1052 | struct _expand_current<_arg_count, repeat<_Ty, _min, _max> 1053 | #if defined(HOP_ENABLE_REPEAT_OPTIMIZATION) 1054 | , typename std::enable_if::value>::type 1055 | #endif 1056 | > { 1057 | static_assert(_arg_count >= _min && _arg_count <= _max); 1058 | 1059 | // multiple first expansion(s) with the expansion(s) of the rest 1060 | using minmax_args_count_list = _minmax_args_count_list>; 1061 | using _minmax = mp_front< minmax_args_count_list>; 1062 | static constexpr size_t _min_rest = mp_front<_minmax>::value; 1063 | static constexpr size_t _max_rest = mp_second<_minmax>::value; 1064 | static constexpr size_t _min_ = mp_at_c<_minmax, 2>::value; 1065 | static constexpr size_t _max_ = /*seen_defaulted*/ false 1066 | ? 0 // no further args after a daulfted 1067 | : mp_at_c<_minmax, 3>::value; 1068 | 1069 | static_assert(_min_rest == 0, "_Ty is not is single entity"); 1070 | static_assert(_max_rest == 0, "_Ty is not is single entity"); 1071 | static_assert(_min_ > 0, "at least one argument has to be consumed by _Ty, otherwise it's ambigous"); 1072 | 1073 | // static constexpr size_t _lower_bound = std::max(_min_, minus_non_negative(_arg_count, _max_rest)); 1074 | static constexpr size_t _lower_bound = _min_; // at least one argument has to be consumed 1075 | static constexpr size_t _upper_bound = std::min(_max_, minus_non_negative(_arg_count, _min_rest)); 1076 | 1077 | // drop funktioniert nicht, wenn mehr als der Inhalt gedroppt wird 1078 | using valid_arg_counts = mp_drop_c< 1079 | mp_iota_c<_upper_bound + 1>, 1080 | std::min(_lower_bound, _upper_bound + 1) 1081 | >; 1082 | 1083 | template 1084 | using first_type = typename _expand_overload_set_impl< args_used, mp_list<_Ty>>::type; 1085 | 1086 | 1087 | template 1088 | using rest_type = typename _expand_overload_set_impl< 1089 | _arg_count - args_used, mp_list> 1090 | >::type; 1091 | 1092 | // for each args_used build product of first_type x rest_type 1093 | // append all of them 1094 | 1095 | struct build_product { 1096 | 1097 | template 1098 | using fn = 1099 | mp_product, rest_type>; 1100 | }; 1101 | 1102 | 1103 | // the result 1104 | using type = 1105 | mp_flatten < 1106 | mp_transform_q< 1107 | build_product, 1108 | valid_arg_counts 1109 | >>; 1110 | }; 1111 | 1112 | template 1113 | struct _expand_current<_arg_count, cpp_defaulted_param<_Ty, _Init>> { 1114 | static_assert(_arg_count <= 1); 1115 | 1116 | using argument_type = argument_t< 1117 | mp_repeat_c, _arg_count>, 1118 | mp_if_c< _arg_count == 1, 1119 | mp_list<_Ty>, 1120 | mp_list> 1121 | >, 1122 | mp_list, _arg_count == 1, false>> 1123 | >; 1124 | using single_type = mp_list; // generated argument-list 1125 | using type = mp_list; // generated argument-lists 1126 | }; 1127 | 1128 | template 1129 | struct _expand_current<_arg_count, general_defaulted_param<_Ty, _Init>> { 1130 | static_assert(_arg_count <= 1); 1131 | 1132 | using argument_type = argument_t< 1133 | mp_repeat_c, _arg_count>, 1134 | mp_if_c< _arg_count == 1, 1135 | mp_list<_Ty>, 1136 | mp_list> 1137 | >, 1138 | mp_list, _arg_count == 1, true>> 1139 | >; 1140 | using single_type = mp_list; // generated argument-list 1141 | using type = mp_list; // generated argument-lists 1142 | }; 1143 | 1144 | 1145 | 1146 | template 1147 | struct _expand_current<_arg_count, seq<_Tys...>> { 1148 | 1149 | using type = typename _expand_overload_set_impl<_arg_count, mp_list<_Tys...>>::type; 1150 | }; 1151 | 1152 | 1153 | 1154 | 1155 | template 1156 | struct _expand_current<_arg_count, alt<_Tys...>> { 1157 | 1158 | struct build_alt { 1159 | 1160 | template 1161 | using fn = typename _expand_overload_set_impl< _arg_count, mp_list>::type; 1162 | }; 1163 | 1164 | 1165 | // the result 1166 | using type_list = 1167 | mp_transform_q< 1168 | build_alt, 1169 | mp_list<_Tys...> 1170 | >; 1171 | 1172 | using type = mp_fold, mp_append>; 1173 | }; 1174 | 1175 | 1176 | template 1177 | struct is_cpp_defaulted_param_used : mp_false {}; 1178 | 1179 | template 1180 | struct is_cpp_defaulted_param_used<0, cpp_defaulted_param<_Ty, _Init>> : mp_true {}; 1181 | 1182 | 1183 | 1184 | template 1185 | struct _expand_overload_set_helper<_arg_count, mp_list<>, minmax_args_count_list, seen_defaulted> { 1186 | using type = 1187 | mp_if_c<_arg_count == 0 1188 | , mp_list> 1189 | , mp_list<> 1190 | >; 1191 | }; 1192 | 1193 | template 1194 | struct _expand_overload_set_helper<_arg_count, mp_list<_Ty, _TyRest...>, minmax_args_count_list, seen_defaulted> { 1195 | 1196 | using _minmax = mp_front< minmax_args_count_list>; 1197 | static constexpr size_t _min_rest = mp_front<_minmax>::value; 1198 | static constexpr size_t _max_rest = mp_second<_minmax>::value; 1199 | static constexpr size_t _min = mp_at_c<_minmax, 2>::value; 1200 | static constexpr size_t _max = seen_defaulted 1201 | ? 0 // no further args after a defaulted 1202 | : mp_at_c<_minmax, 3>::value; 1203 | 1204 | static constexpr size_t _lower_bound = std::max(_min, minus_non_negative(_arg_count, _max_rest)); 1205 | static constexpr size_t _upper_bound = std::min(_max, minus_non_negative(_arg_count, _min_rest)); 1206 | 1207 | 1208 | using valid_arg_counts = mp_drop_c< 1209 | mp_iota_c<_upper_bound + 1>, 1210 | std::min(_lower_bound, _upper_bound + 1) // attention: mp_drop doesn't work, when dropping more than the dropped-from contains!!! 1211 | >; 1212 | 1213 | template 1214 | using rest_type = typename _expand_overload_set_helper< 1215 | _arg_count - args_used, mp_list<_TyRest...>, 1216 | mp_rest, 1217 | seen_defaulted || is_cpp_defaulted_param_used::value 1218 | >::type; 1219 | 1220 | 1221 | template 1222 | using current_t = _expand_current< args_used, _Ty>; 1223 | 1224 | struct prepend_current_t { 1225 | template 1226 | using fn = 1227 | mp_transform_q< 1228 | mp_bind_back< 1229 | mp_push_front, 1230 | current_t 1231 | >, 1232 | rest_type 1233 | >; 1234 | }; 1235 | 1236 | 1237 | // the result 1238 | using type_list = 1239 | mp_transform_q< 1240 | prepend_current_t, 1241 | valid_arg_counts 1242 | >; 1243 | 1244 | 1245 | using type = mp_fold, mp_append>; 1246 | }; 1247 | 1248 | // _expand_overload_set_impl expands a single overload-entry (_TyIf) into (at least) all valid param-sets 1249 | template 1250 | struct _expand_overload_set_impl { 1251 | using minmax_args_count_list = _minmax_args_count_list<_Ty>; 1252 | using types_unexpanded = typename _expand_overload_set_helper< _arg_count, _Ty, minmax_args_count_list, false>::type; 1253 | 1254 | 1255 | template 1256 | using append_expanded = 1257 | mp_product 1258 | ; 1259 | 1260 | template 1261 | using expand_list = 1262 | mp_fold< 1263 | _Unexpanded, 1264 | mp_list>, 1265 | append_expanded 1266 | >; 1267 | 1268 | using type = 1269 | mp_flatten< 1270 | mp_transform< 1271 | expand_list, 1272 | types_unexpanded 1273 | >>; 1274 | }; 1275 | 1276 | 1277 | // _expand_overload_set expands a single overload-entry (_TyIf) into (at least) all valid param-sets 1278 | template 1279 | struct _expand_overload_set { 1280 | using _Ty = mp_first< _TyIf>; // split from global enable_if condition etc. 1281 | using type_no_If = typename _expand_overload_set_impl<_arg_count, _Ty>::type; 1282 | 1283 | template using rejoin_If_t = 1284 | mp_append, mp_rest< _TyIf>>; 1285 | 1286 | using type = 1287 | mp_transform< 1288 | rejoin_If_t, 1289 | type_no_If 1290 | >; 1291 | }; 1292 | 1293 | 1294 | 1295 | template 1296 | struct _expanded_overload_set_; 1297 | 1298 | template 1299 | struct _expanded_overload_set_<_arg_count, _Idx, mp_list<_TypeIf_list...>> 1300 | : _single_overload<_Idx, _TypeIf_list>... { 1301 | using _single_overload<_Idx, _TypeIf_list>::test...; 1302 | }; 1303 | 1304 | 1305 | template 1306 | struct _expanded_overload_set; 1307 | 1308 | template 1309 | struct _expanded_overload_set<_arg_count, _Idx, mp_list<_TypeIf_list...>> 1310 | : _expanded_overload_set_<_arg_count, _Idx, mp_list<_TypeIf_list...>> { 1311 | 1312 | using _expanded_overload_set_<_arg_count, _Idx, mp_list<_TypeIf_list...>>::test; 1313 | }; 1314 | 1315 | 1316 | 1317 | // an unmatchable type 1318 | struct unmatchable; 1319 | 1320 | // specialization to avoid compilation errors for "using ...::test" when no overload is generated 1321 | // eventually this will result in an error, but it shall be a "no matching overload found" 1322 | template 1323 | struct _expanded_overload_set<_arg_count, _Idx, mp_list<>> { 1324 | template::value, int* > = nullptr > 1325 | constexpr void test(unmatchable) const; 1326 | }; 1327 | 1328 | 1329 | 1330 | // _overload_set expands all overload-entries (_Types) into (at least) all valid param-sets 1331 | // size_t _arg_count: numer of actual arguments 1332 | // size_t... _Indices: index sequence indexing overloads in overload_set 1333 | // _Types...: mp_list, quoted enable-if> 1334 | 1335 | template 1336 | struct _overload_set; 1337 | 1338 | template 1339 | struct _overload_set<_arg_count, std::index_sequence<_Indices...>, _Types...> 1340 | : _expanded_overload_set<_arg_count, _Indices, typename _expand_overload_set<_arg_count, _Types>::type>... { 1341 | using _expanded_overload_set<_arg_count, _Indices, typename _expand_overload_set<_arg_count, _Types>::type>::test...; 1342 | }; 1343 | 1344 | 1345 | 1346 | 1347 | 1348 | 1349 | // information type, contains: 1350 | // user-tag 1351 | // is_from_base information 1352 | struct not_an_adapter; 1353 | 1354 | template 1355 | struct information_t { 1356 | using overload_t = _OL; 1357 | using tag_t = _Tag; 1358 | using is_from_base_t = _Is_from_base; 1359 | using adapter_t = _Adapter; 1360 | }; 1361 | 1362 | using is_from_base = mp_true; 1363 | using is_not_from_base = mp_false; 1364 | 1365 | 1366 | // mp_list, impl::information_t<_Tag, mp_false>, mp_quote<_If>>; 1367 | template 1368 | using make_information_from_base_t = information_t< 1369 | typename _Ty::overload_t, 1370 | typename _Ty::tag_t, 1371 | is_from_base, 1372 | typename _Ty::adapter_t 1373 | >; 1374 | 1375 | template 1376 | using make_ol_from_base_t = 1377 | mp_replace_second<_Ty, make_information_from_base_t>>; 1378 | 1379 | } 1380 | 1381 | 1382 | template 1383 | struct overload_set; 1384 | 1385 | template 1386 | struct overload_set, _arg_count> : impl::_overload_set<_arg_count, std::index_sequence_for<_Overload_Type_set...>, _Overload_Type_set...> {}; 1387 | 1388 | 1389 | 1390 | // helper for inspecting current overload 1391 | static constexpr size_t information_index = 0; 1392 | static constexpr size_t overload_index = 1; 1393 | static constexpr size_t expected_parameter_overload_type_index = 2; // the type-list of the selected overload WITH default-params 1394 | static constexpr size_t default_info_type_index = 3; // the original secification of the selected overload with default-info 1395 | static constexpr size_t formal_parameter_overload_type_index = 4; // the type-list of the selected overload 1396 | static constexpr size_t deduced_local_parameter_overload_type_index = 5; // the local deduced types 1397 | static constexpr size_t deduced_parameter_overload_type_index = 6; // the (global) deduced types 1398 | static constexpr size_t expansion_type_index = 7; // type holding the inductive expansion of the types 1399 | 1400 | template 1401 | using get_tag_type = typename mp_at_c<_Overload, information_index>::tag_t; 1402 | 1403 | template 1404 | using has_tag = std::is_same, _Tag>; 1405 | 1406 | template 1407 | static constexpr bool has_tag_v = has_tag<_Overload, _Tag>::value; 1408 | 1409 | 1410 | template 1411 | using is_from_base = typename mp_at_c<_Overload, information_index>::is_from_base_t; 1412 | 1413 | template 1414 | static constexpr bool is_from_base_v = is_from_base<_Overload>::value; 1415 | 1416 | template 1417 | using is_adapted = mp_not::adapter_t, impl::not_an_adapter>>; 1418 | 1419 | template 1420 | static constexpr bool is_adapted_v = is_adapted<_Overload>::value; 1421 | 1422 | template 1423 | decltype(auto) forward_adapted(_Ts&&... ts) { 1424 | return mp_at_c<_Overload, information_index>::adapter_t::forward(std::forward<_Ts>(ts)...); 1425 | } 1426 | 1427 | template 1428 | using get_overload_set_type = typename mp_at_c<_Overload, information_index>::overload_t; 1429 | 1430 | 1431 | template 1432 | using index = mp_at_c<_Overload, overload_index>; 1433 | 1434 | 1435 | template 1436 | using defaults_specified = 1437 | mp_count_if< 1438 | mp_at_c<_Overload, default_info_type_index>, 1439 | impl::is_default_specified_t 1440 | >; 1441 | 1442 | template 1443 | using formal_parameter_overload_type = mp_at_c<_Overload, formal_parameter_overload_type_index>; 1444 | 1445 | template 1446 | using expected_parameter_overload_type = mp_at_c<_Overload, expected_parameter_overload_type_index>; 1447 | 1448 | template 1449 | using deduced_local_parameter_overload_type = mp_at_c<_Overload, deduced_local_parameter_overload_type_index>; 1450 | 1451 | template 1452 | using deduced_parameter_overload_type = mp_at_c<_Overload, deduced_parameter_overload_type_index>; 1453 | 1454 | 1455 | 1456 | 1457 | template 1458 | constexpr decltype(auto) get_arg_at(Ts&&... ts) { 1459 | return fused::nth(std::forward(ts)...); 1460 | } 1461 | 1462 | // this is ONLY for C++-style default params (NOT for general_defaulted_param) 1463 | template 1464 | constexpr decltype(auto) get_cpp_defaulted_param() { 1465 | using _Ty = get_overload_set_type<_Overload>; 1466 | constexpr auto begin_cpp_defaulted_param = mp_find_if<_Ty, impl::is_cpp_defaulted_param>::value; 1467 | 1468 | return impl::get_init_type_t>{}(); 1469 | } 1470 | 1471 | 1472 | template 1473 | constexpr decltype(auto) get_indexed_defaulted(Ts&&... ts) { 1474 | using _Ty = get_overload_set_type<_Overload>; 1475 | constexpr auto begin_cpp_defaulted_param = mp_find_if<_Ty, impl::is_cpp_defaulted_param>::value; 1476 | if constexpr (sizeof...(Ts) <= begin_cpp_defaulted_param + _DefaultIdx) { 1477 | return get_cpp_defaulted_param<_Overload, _DefaultIdx>(); 1478 | } else { 1479 | return fused::nth(std::forward(ts)...); 1480 | } 1481 | } 1482 | 1483 | namespace impl { 1484 | struct not_a_tag; 1485 | 1486 | template 1487 | struct has_tag_impl :std::false_type{}; 1488 | 1489 | template 1490 | struct has_tag_impl<_Test, tagged_ty<_Tag, _Ty>>:std::disjunction< std::is_same<_Test, _Tag>, has_tag_impl<_Test, _Ty>>{}; 1491 | 1492 | template 1493 | struct has_tag_impl<_Test, repeat<_Ty, _min, _max>>:has_tag_impl<_Test, _Ty> {}; 1494 | 1495 | template 1496 | struct has_tag_impl<_Test, cpp_defaulted_param<_Ty, _Init>> :has_tag_impl<_Test, _Ty> {}; 1497 | 1498 | template 1499 | struct has_tag_impl<_Test, general_defaulted_param<_Ty, _Init>> :has_tag_impl<_Test, _Ty> {}; 1500 | 1501 | 1502 | // specialization used in get_tagged_arg_or 1503 | template 1504 | struct has_tag_impl<_Test, defaulted_type_t<_Ty, _specified, _general>> :has_tag_impl<_Test, _Ty> {}; 1505 | 1506 | 1507 | 1508 | template 1509 | struct has_tag { 1510 | template 1511 | using fn = has_tag_impl<_Tag, T>; 1512 | }; 1513 | 1514 | template 1515 | struct first_has_tag { 1516 | template 1517 | using fn = has_tag_impl<_Tag, mp_first>; 1518 | }; 1519 | 1520 | 1521 | struct first_is_not_defaulted_param { 1522 | template 1523 | using fn = is_not_defaulted_param>; 1524 | }; 1525 | 1526 | 1527 | 1528 | enum class or_behaviour { 1529 | is_a_value, 1530 | is_a_callable, 1531 | result_in_compilation_error 1532 | }; 1533 | 1534 | template 1535 | struct append_actual_index { 1536 | 1537 | using elem = mp_list< T, 1538 | mp_if< 1539 | is_defaulted_param, 1540 | not_a_tag, 1541 | mp_count_if_q 1542 | > 1543 | >; 1544 | using type = mp_push_back; 1545 | }; 1546 | template 1547 | using append_actual_index_t = typename append_actual_index::type; 1548 | 1549 | // get_tagged_arg_or will always go for the first type with a matching tag 1550 | template 1551 | constexpr decltype(auto) get_tagged_arg_or(_Or&& _or, Ts&&... ts) { 1552 | using expected_types = expected_parameter_overload_type<_Overload>; 1553 | using expected_types_with_actual_index = mp_fold< expected_types, mp_list<>, append_actual_index_t>; 1554 | 1555 | using tag_filtered_expected_types = mp_copy_if_q>; 1556 | 1557 | if constexpr (mp_size::value <= tag_index) { 1558 | if constexpr (or_behaviour_ == or_behaviour::is_a_callable) { 1559 | return std::apply(std::forward<_Or>(_or)); 1560 | } else if constexpr (or_behaviour_ == or_behaviour::is_a_value) { 1561 | return std::forward<_Or>(_or); 1562 | } else if constexpr (or_behaviour_ == or_behaviour::result_in_compilation_error) { 1563 | static_assert(dependent_false<_Overload>::value, "tag not found"); 1564 | } else { 1565 | static_assert(dependent_false<_Overload>::value, "hop internal error: missing case"); 1566 | } 1567 | } else if constexpr (is_defaulted_param>>::value) { 1568 | using defaulted_types = mp_copy_if< 1569 | mp_at_c<_Overload, default_info_type_index>, 1570 | impl::is_defaulted_type_t 1571 | >; 1572 | 1573 | constexpr auto defaulted_tag_index = mp_find_if_q>::value; 1574 | constexpr auto defaulted_end = mp_size::value; 1575 | 1576 | //#define ENFORCE_MSVC_COMPILATION_ERROR 1577 | #ifdef ENFORCE_MSVC_COMPILATION_ERROR 1578 | if constexpr (defaulted_tag_index < defaulted_end) { 1579 | #else 1580 | if constexpr (defaulted_end > defaulted_tag_index) { 1581 | #endif 1582 | using defaulted_type = typename mp_at_c::type; 1583 | return impl::get_init_type_t{}(); 1584 | } else { 1585 | if constexpr (or_behaviour_ == or_behaviour::is_a_callable) { 1586 | return std::apply(std::forward<_Or>(_or)); 1587 | } else if constexpr (or_behaviour_ == or_behaviour::is_a_value) { 1588 | return std::forward<_Or>(_or); 1589 | } else if constexpr (or_behaviour_ == or_behaviour::result_in_compilation_error) { 1590 | static_assert(dependent_false<_Overload>::value, "tag not found"); 1591 | } else { 1592 | static_assert(dependent_false<_Overload>::value, "hop internal error: missing case"); 1593 | } 1594 | } 1595 | } else { 1596 | return fused::nth>::value>(std::forward(ts)...); 1597 | } 1598 | } 1599 | } 1600 | // get_tagged_arg_or_call will always go for the first type with a matching tag 1601 | template 1602 | constexpr decltype(auto) get_tagged_arg_or_call(_FnOr&& _fnor, Ts&&... ts) { 1603 | return impl::get_tagged_arg_or<_Overload, _Tag, tag_index, impl::or_behaviour::is_a_callable>(std::forward<_FnOr>(_fnor), std::forward(ts)...); 1604 | } 1605 | 1606 | // get_tagged_arg_or will always go for the first type with a matching tag 1607 | template 1608 | constexpr decltype(auto) get_tagged_arg_or(_Or&& _or, Ts&&... ts) { 1609 | return impl::get_tagged_arg_or<_Overload, _Tag, tag_index, impl::or_behaviour::is_a_value>(std::forward<_Or>(_or), std::forward(ts)...); 1610 | } 1611 | 1612 | 1613 | // get_tagged_arg will always go for the first type with a matching tag 1614 | template 1615 | constexpr decltype(auto) get_tagged_arg(Ts&&... ts) { 1616 | return impl::get_tagged_arg_or<_Overload, _Tag, tag_index, impl::or_behaviour::result_in_compilation_error>(0, std::forward(ts)...); 1617 | } 1618 | 1619 | 1620 | namespace impl { 1621 | 1622 | template 1623 | constexpr decltype(auto) get_args_if_helper() { 1624 | using expected_types = expected_parameter_overload_type<_Overload>; 1625 | 1626 | if constexpr (mp_size::value > index_expected) { 1627 | if constexpr (is_defaulted_param>::value) { 1628 | if constexpr (_If::template fn>::value) { 1629 | return std::tuple_cat( 1630 | std::make_tuple(impl::get_init_type_t>{}()), 1631 | get_args_if_helper<_Overload, _If, index_specified, index_expected + 1>() 1632 | ); 1633 | } else { 1634 | return get_args_if_helper<_Overload, _If, index_specified, index_expected + 1>(); 1635 | } 1636 | } else { 1637 | static_assert(dependent_false<_Overload>::value); // must be a defaulted_param 1638 | return get_args_if_helper<_Overload, _If, index_specified, index_expected + 1>(); 1639 | } 1640 | } else { 1641 | return std::tuple<>{}; 1642 | } 1643 | } 1644 | 1645 | 1646 | template 1647 | constexpr decltype(auto) get_args_if_helper(T&& t, Ts&&... ts) { 1648 | using expected_types = expected_parameter_overload_type<_Overload>; 1649 | 1650 | if constexpr (is_defaulted_param>::value) { 1651 | if constexpr (_If::template fn>::value) { 1652 | return std::tuple_cat( 1653 | std::make_tuple(impl::get_init_type_t>{}()), 1654 | get_args_if_helper<_Overload, _If, index_specified, index_expected + 1>(std::forward(t), std::forward(ts)...) 1655 | ); 1656 | } else { 1657 | return get_args_if_helper<_Overload, _If, index_specified, index_expected + 1>(std::forward(t), std::forward(ts)...); 1658 | } 1659 | } else { 1660 | if constexpr (_If::template fn, index_specified>>::value) { 1661 | return std::tuple_cat(std::forward_as_tuple(std::forward(t)), get_args_if_helper<_Overload, _If, index_specified + 1, index_expected + 1>(std::forward(ts)...)); 1662 | } else { 1663 | return get_args_if_helper<_Overload, _If, index_specified + 1, index_expected + 1>(std::forward(ts)...); 1664 | } 1665 | } 1666 | } 1667 | } 1668 | 1669 | template 1670 | constexpr decltype(auto) get_args_if_q(Ts&&... ts) { 1671 | return impl::get_args_if_helper<_Overload, _If, 0, 0>(std::forward(ts)...); 1672 | } 1673 | 1674 | template class _If, class... Ts> 1675 | constexpr decltype(auto) get_args_if(Ts&&... ts) { 1676 | return get_args_if_q< _Overload, mp_quote<_If>>(std::forward(ts)...); 1677 | } 1678 | 1679 | template 1680 | constexpr decltype(auto) get_args(Ts&&... ts) { 1681 | return get_args_if_q< _Overload, mp_quote>(std::forward(ts)...); 1682 | } 1683 | 1684 | template 1685 | constexpr decltype(auto) get_tagged_args(Ts&&... ts) { 1686 | return get_args_if_q< _Overload, impl::has_tag<_Tag>>(std::forward(ts)...); 1687 | } 1688 | 1689 | 1690 | 1691 | namespace impl { 1692 | 1693 | template 1694 | constexpr size_t get_count_if_helper() { 1695 | using expected_types = expected_parameter_overload_type<_Overload>; 1696 | 1697 | if constexpr (mp_size::value > index_expected) { 1698 | if constexpr (_If::template fn>::value) { 1699 | return 1 + get_count_if_helper<_Overload, _If, index_expected + 1>(); 1700 | } else { 1701 | return get_count_if_helper<_Overload, _If, index_expected + 1>(); 1702 | } 1703 | } else { 1704 | return 0; 1705 | } 1706 | } 1707 | } 1708 | 1709 | 1710 | template 1711 | constexpr size_t get_count_if_q() { 1712 | return impl::get_count_if_helper<_Overload, _If, 0>(); 1713 | } 1714 | 1715 | template class _If> 1716 | constexpr size_t get_count_if() { 1717 | return get_count_if_q< _Overload, mp_quote<_If>>(); 1718 | } 1719 | 1720 | template 1721 | constexpr size_t get_count() { 1722 | return get_count_if_q< _Overload, mp_quote>(); 1723 | } 1724 | 1725 | template 1726 | constexpr size_t get_tagged_count() { 1727 | return get_count_if_q< _Overload, impl::has_tag<_Tag>>(); 1728 | } 1729 | 1730 | 1731 | // applying each element of tuple to function 1732 | 1733 | namespace impl { 1734 | template 1735 | constexpr F tuple_for_each_helper(Tuple&& t, F&& f, std::index_sequence) 1736 | { 1737 | return (void)std::initializer_list{(std::forward(f)(std::get(std::forward(t))), 0)...}, f; 1738 | } 1739 | } 1740 | template 1741 | constexpr F tuple_for_each(Tuple&& t, F&& f) 1742 | { 1743 | return impl::tuple_for_each_helper(std::forward(t), std::forward(f), 1744 | std::make_index_sequence>::value>{}); 1745 | } 1746 | 1747 | 1748 | 1749 | template 1750 | using deduced_local = mp_at_c, index>; 1751 | 1752 | 1753 | template 1754 | using deduced_local_types = 1755 | typename impl::get_tmpl, index>>::type::_if::template deduced>; 1756 | 1757 | template 1758 | using deduced_types = deduced_parameter_overload_type<_Overload>; 1759 | 1760 | 1761 | 1762 | 1763 | template 1764 | using ol_list = mp_list<_Ty...>; 1765 | 1766 | template 1767 | using packed_ol = mp_list<_Tys, impl::information_t<_Tys, _Tag, mp_false, _Adapter>, _If_q>; 1768 | 1769 | 1770 | template class _If, class... _Ty> 1771 | using tagged_ol_if = packed_ol<_Tag, mp_quote<_If>, mp_list<_Ty...>>; 1772 | 1773 | template class _If, class... _Ty> 1774 | using ol_if = tagged_ol_if; 1775 | 1776 | template 1777 | using tagged_ol = tagged_ol_if<_Tag, impl::true_t, _Ty...>; 1778 | 1779 | template 1780 | using ol = ol_if; 1781 | 1782 | template 1783 | using tagged_ol_if_q = packed_ol<_Tag, _If_q, mp_list<_Ty...>>; 1784 | 1785 | 1786 | 1787 | template 1788 | using tagged_adapted = packed_ol<_Tag, impl::adapter_test, mp_list>, Adapter>; 1789 | 1790 | template 1791 | using adapted = tagged_adapted; 1792 | 1793 | 1794 | template 1795 | using adapt = adapted>; 1796 | 1797 | template 1798 | using tagged_adapt = tagged_adapted<_Tag, impl::adapter>; 1799 | 1800 | 1801 | template 1802 | constexpr decltype(overload_set<_Overload_Type_set, sizeof...(_Tys)>{}.template test<_Tys...>(std::declval<_Tys>()...)) enable(); 1803 | 1804 | 1805 | template 1806 | using enable_t = decltype(enable()); 1807 | 1808 | // deprecated: use "enable_t<...>* = nullptr" instead of "enable_test<...> = 0" 1809 | //template 1810 | //using enable_test = decltype((enable()), 0); 1811 | 1812 | template 1813 | constexpr bool ol_has_tag_v = has_tag_v, Tag>; 1814 | 1815 | template 1816 | struct match_tag { 1817 | template 1818 | using has = std::enable_if_t, int>; 1819 | }; 1820 | 1821 | template 1822 | using match_tag_t = typename match_tag::template has; 1823 | 1824 | template 1825 | using ol_matches_tag_t = std::enable_if_t, int>; 1826 | 1827 | 1828 | 1829 | template 1830 | using ol_extend = mp_append, mp_transform>; 1831 | 1832 | 1833 | } 1834 | 1835 | #endif // HOP_HOP_HPP_INCLUDED 1836 | -------------------------------------------------------------------------------- /include/hop_utils.hpp: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////// 2 | // 3 | // hop library 4 | // 5 | // Copyright Tobias Loew 2019. Use, modification and 6 | // distribution is subject to the Boost Software License, Version 7 | // 1.0. (See accompanying file LICENSE_1_0.txt or copy at 8 | // http://www.boost.org/LICENSE_1_0.txt) 9 | // 10 | // For more information, see https://github.com/tobias-loew/hop 11 | // 12 | 13 | #ifndef HOP_HOP_UTILS_HPP_INCLUDED 14 | #define HOP_HOP_UTILS_HPP_INCLUDED 15 | 16 | 17 | #include 18 | #include 19 | 20 | namespace hop { 21 | 22 | // utility functions 23 | namespace utils { 24 | namespace aux { 25 | using std::to_string; 26 | std::string to_string(std::string s) { return s; } 27 | } 28 | 29 | // print out simple values, annotate type 30 | template 31 | inline std::string annotate_type(Arg&& arg) { 32 | return std::string(typeid(arg).name()) 33 | + (std::is_const_v> ? " const" : "") 34 | + (std::is_rvalue_reference_v ? "&&" : std::is_lvalue_reference_v ? "&" : ""); 35 | } 36 | 37 | 38 | // print out simple values, annotate type 39 | template 40 | inline std::string to_string_annotate_type(Arg&& arg) { 41 | return aux::to_string(arg) + ':' + annotate_type(std::forward(arg)); 42 | } 43 | } 44 | 45 | } 46 | 47 | #endif // HOP_HOP_UTILS_HPP_INCLUDED 48 | -------------------------------------------------------------------------------- /pictures/Luna_Meersau.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobias-loew/hop/72e001c14833893213a64e934554fdd1b2106da8/pictures/Luna_Meersau.png -------------------------------------------------------------------------------- /pictures/Luna_Pacino.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobias-loew/hop/72e001c14833893213a64e934554fdd1b2106da8/pictures/Luna_Pacino.jpg -------------------------------------------------------------------------------- /pictures/hop_experts_clipped.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobias-loew/hop/72e001c14833893213a64e934554fdd1b2106da8/pictures/hop_experts_clipped.jpg -------------------------------------------------------------------------------- /pictures/luna.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobias-loew/hop/72e001c14833893213a64e934554fdd1b2106da8/pictures/luna.png -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # CMakeList.txt : CMake project for hop, include source and define 2 | # project specific logic here. 3 | # 4 | #cmake_minimum_required (VERSION 3.8) 5 | 6 | #project(test) 7 | 8 | 9 | # Add source to this project's executable. 10 | #add_executable (hop "hop_test_2.cpp") 11 | 12 | #add_library(test hop_test_2.cpp) 13 | 14 | # TODO: Add tests and install targets if needed. 15 | add_executable(test hop_test_2.cpp) 16 | 17 | set(BOOST_PATH "//mnt/c/loew/boost_1_75_0") 18 | target_include_directories(test PRIVATE "${BOOST_PATH}") 19 | target_include_directories(test PRIVATE "..\\include") 20 | #target_include_directories(test PRIVATE "..\include") 21 | 22 | #enable_testing() 23 | #add_test(NAME mytest COMMAND test) 24 | -------------------------------------------------------------------------------- /test/hop_test.cpp: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////// 2 | // 3 | // hop library 4 | // 5 | // Copyright Tobias Loew 2019. Use, modification and 6 | // distribution is subject to the Boost Software License, Version 7 | // 1.0. (See accompanying file LICENSE_1_0.txt or copy at 8 | // http://www.boost.org/LICENSE_1_0.txt) 9 | // 10 | // For more information, see https://github.com/tobias-loew/hop 11 | // 12 | 13 | #define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "..\include\hop.hpp" 26 | #include "..\include\hop_utils.hpp" 27 | 28 | // intentionally NOT "using namespace hop;" to better show, where hop-types are used 29 | 30 | 31 | auto&& os = std::ofstream("hop_out.txt"); 32 | 33 | template 34 | struct tag_t { 35 | using type = T; 36 | constexpr tag_t() {} 37 | }; 38 | 39 | 40 | std::string ws_to_s(std::wstring const& ws) { 41 | // use very simple ws to s converter (it's deprecated, but works for our purposes) 42 | return std::wstring_convert>().to_bytes(ws); 43 | } 44 | 45 | enum limiters { 46 | bracket, 47 | paren, 48 | brace, 49 | angle 50 | }; 51 | 52 | static constexpr char const* limit_symbol[][2]{ 53 | {"[","]"}, 54 | {"(",")"}, 55 | {"{","}"}, 56 | {"<",">"}, 57 | }; 58 | 59 | template 60 | std::string to_string(T&&); 61 | 62 | template 63 | std::string tuple_to_string(T&&); 64 | 65 | template 66 | decltype(auto) printer(T) { 67 | using type = std::remove_cv_t>; 68 | if constexpr (std::is_same::value) { 69 | // use very simple ws to s converter (it's deprecated, but works for our purposes) 70 | return [](type const& s) {return ws_to_s(s); }; 71 | } else if constexpr (boost::mp11::mp_similar>::value) { 72 | return [](type const& s) { return to_string(s); }; 73 | } else if constexpr (boost::mp11::mp_similar>::value) { 74 | return [](type const& s) { return tuple_to_string(s); }; 75 | } else if constexpr (boost::mp11::mp_similar>::value) { 76 | return [](type const& s) { return tuple_to_string(s); }; 77 | } else if constexpr (boost::mp11::mp_similar>::value) { 78 | return [](type const& s) { return to_string(s); }; 79 | } else if constexpr (boost::mp11::mp_similar>::value) { 80 | return [](type const& s) { return to_string(s); }; 81 | } else if constexpr (boost::mp11::mp_similar>::value) { 82 | return [](type const& s) { return to_string(s); }; 83 | } else { 84 | return [](auto&& t)->decltype(auto) { 85 | return std::forward(t); 86 | }; 87 | } 88 | } 89 | 90 | template 91 | decltype(auto) make_printable(T&& arg) { 92 | return printer(tag_t{})(std::forward(arg)); 93 | } 94 | 95 | template 96 | std::string to_string(T&& cont){ 97 | std::string result = limit_symbol[limit][0]; 98 | bool first = true; 99 | for (auto&& elem : cont) { 100 | if (first) { 101 | first = false; 102 | } else { 103 | result += ","; 104 | } 105 | std::stringstream ss; 106 | ss << make_printable(elem); 107 | result += ss.str(); 108 | } 109 | result += limit_symbol[limit][1]; 110 | return result; 111 | } 112 | 113 | 114 | template 115 | std::string tuple_to_string(T&& t) { 116 | std::string result = limit_symbol[limit][0]; 117 | bool first = true; 118 | 119 | std::apply([&](auto&&... args) {(( 120 | 121 | [&]() { 122 | if (first) { 123 | first = false; 124 | } else { 125 | result += ","; 126 | } 127 | std::stringstream ss; 128 | ss << make_printable(args); 129 | result += ss.str(); 130 | }() 131 | 132 | ), ...); }, std::forward(t)); 133 | 134 | result += limit_symbol[limit][1]; 135 | return result; 136 | } 137 | 138 | 139 | 140 | struct output_args_ { 141 | 142 | 143 | 144 | template 145 | void operator()(Args&& ... args) const { 146 | os << "args:" << std::endl; 147 | // ((os << hop::utils::to_string_annotate_type(std::forward(args))), ...); 148 | ((os << typeid(args).name() << ": " << make_printable(std::forward(args)) << std::endl), ...); 149 | os << std::endl; 150 | } 151 | }; 152 | 153 | output_args_ output_args; 154 | 155 | 156 | namespace ns_test_1 { 157 | // a single overload with a single parameter 158 | 159 | using overloads_t = hop::ol_list < 160 | hop::ol // one std::string 161 | >; 162 | 163 | // alias template to simplify repetitions 164 | template 165 | using enabler = hop::enable_t; 166 | 167 | template* = nullptr > 168 | void foo(Args&& ... args) { 169 | using OL = hop::enable_t; 170 | 171 | output_args(std::forward(args)...); 172 | } 173 | 174 | 175 | void test() { 176 | foo("Hello"); 177 | auto hop = "hop"; 178 | foo(hop); 179 | foo(std::string{ "world!" }); 180 | } 181 | } 182 | 183 | 184 | namespace ns_test_2 { 185 | // a homogenous pack 186 | 187 | using overloads_t = hop::ol_list < 188 | hop::ol> // accept a (possibly empty) list of ints 189 | >; 190 | 191 | template* = nullptr > 192 | void foo(Args&& ... args) { 193 | using OL = hop::enable_t; 194 | 195 | output_args(std::forward(args)...); 196 | } 197 | 198 | 199 | void test() { 200 | foo(); 201 | foo(42); 202 | int n = 5; 203 | foo(n, 2, -4, 4.6, 'c'); 204 | // foo("hello error"); // error: no matching overloaded function found 205 | } 206 | } 207 | 208 | 209 | 210 | namespace ns_test_3 { 211 | // a repeating a type from [min, ... ,max] (or [min, ...) ) 212 | 213 | using overloads_t = hop::ol_list < 214 | hop::ol> // 2 - 5 ints 215 | >; 216 | 217 | template* = nullptr > 218 | void foo(Args&& ... args) { 219 | using OL = hop::enable_t; 220 | 221 | output_args(std::forward(args)...); 222 | } 223 | 224 | 225 | void test() { 226 | //foo(); // error: no matching overloaded function found 227 | //foo(1); // error: no matching overloaded function found 228 | foo(1, 2); 229 | foo(1, 2, 3); 230 | foo(1, 2, 3, 4); 231 | foo(1, 2, 3, 4, 5); 232 | //foo(1, 2, 3, 4, 5, 6); // error: no matching overloaded function found 233 | 234 | } 235 | } 236 | 237 | namespace ns_test_4 { 238 | // additional argument AFTER a pack 239 | 240 | using overloads_t = hop::ol_list < 241 | hop::ol, std::string> // a non-empty list of ints followed by a std::string 242 | >; 243 | 244 | template* = nullptr > 245 | void foo(Args&& ... args) { 246 | using OL = hop::enable_t; 247 | 248 | output_args(std::forward(args)...); 249 | } 250 | 251 | 252 | void test() { 253 | //foo("no ints"); // error: no matching overloaded function found 254 | //foo(1); // error: no matching overloaded function found 255 | foo(42, "hop"); 256 | foo(1, 2, 3, std::string{}); 257 | 258 | } 259 | } 260 | 261 | namespace ns_test_5 { 262 | // trailing defaulted-parameters (C++ style) 263 | 264 | struct init_default { 265 | std::string operator()() const { return "default initialized"; } 266 | }; 267 | 268 | 269 | using overloads_t = hop::ol_list < 270 | hop::ol, hop::cpp_defaulted_param, hop::cpp_defaulted_param> // a non-empty list of ints followed by a defaulted std::string and a defaulted std::wstring 271 | >; 272 | 273 | template* = nullptr > 274 | void foo(Args&& ... args) { 275 | using OL = hop::enable_t; 276 | 277 | // Attention: 278 | // output_args(args...) 279 | // will only output the caller provided arguments. 280 | // to get the parameters with defaulted parameters call 281 | // hop::get_args
            (std::forward(args)...) 282 | // which returns a std::tuple containing the caller provided and defaulted parameters, to output call 283 | // std::apply(output_args, hop::get_args
              (std::forward(args)...)) 284 | 285 | output_args(std::forward(args)...); 286 | std::apply(output_args, hop::get_args
                (std::forward(args)...)); 287 | } 288 | 289 | 290 | void test() { 291 | //foo("no ints"); // error: no matching overloaded function found 292 | foo(1); 293 | foo(42, "hop"); 294 | foo(1, 2, 3, std::string{ "a string" }); 295 | foo(1, 2, 3, std::string{ "a string and a number" }, std::wstring{ L"into the great wide open" }); 296 | // foo(1, 2, 3, L"into the great wide open"); // error: no matching overloaded function found 297 | } 298 | } 299 | 300 | namespace ns_test_6 { 301 | // general defaulted-parameters 302 | 303 | struct init_default { 304 | std::string operator()() const { return "default initialized"; } 305 | }; 306 | 307 | 308 | using overloads_t = hop::ol_list < 309 | hop::ol, hop::pack> // a defaulted std::string followed by a list of ints 310 | >; 311 | 312 | template* = nullptr > 313 | void foo(Args&& ... args) { 314 | using OL = hop::enable_t; 315 | 316 | // Attention: 317 | // output_args(args...) 318 | // will only output the caller provided arguments. 319 | // to get the parameters with defaulted parameters call 320 | // hop::get_args
                  (std::forward(args)...) 321 | // which returns a std::tuple containing the caller provided and defaulted parameters, to output call 322 | // std::apply(output_args, hop::get_args
                    (std::forward(args)...)) 323 | 324 | output_args(std::forward(args)...); 325 | std::apply(output_args, hop::get_args
                      (std::forward(args)...)); 326 | } 327 | 328 | 329 | void test() { 330 | foo("no ints"); // error: no matching overloaded function found 331 | foo(1, 2, 3); 332 | foo("hop", 42); 333 | foo(); 334 | } 335 | } 336 | 337 | namespace ns_test_7 { 338 | // more than one pack... 339 | 340 | using overloads_t = hop::ol_list < 341 | hop::ol, hop::pack> // a list of ints followed by a list of doubles 342 | >; 343 | 344 | template* = nullptr > 345 | void foo(Args&& ... args) { 346 | using OL = hop::enable_t; 347 | 348 | output_args(std::forward(args)...); 349 | } 350 | 351 | 352 | void test() { 353 | foo(); 354 | foo(1, 2.5); 355 | // foo(1, 2.5, 42); // that's ambigous 356 | } 357 | } 358 | 359 | 360 | namespace ns_test_8 { 361 | // tagging types to get a associated args 362 | struct tag_int; 363 | struct tag_double; 364 | 365 | void f(int) {} 366 | void f(double) {} 367 | void foo_plain(int, int, int, int, int) {} 368 | void foo_plain(int, int, int, int, double) {} 369 | void foo_plain(int, int, int, double, double) {} 370 | void foo_plain(int, int, double, double, double) {} 371 | void foo_plain(int, double, double, double, double) {} 372 | void foo_plain(double, double, double, double, double) {} 373 | 374 | using overloads_t = hop::ol_list < 375 | hop::ol>, hop::pack>> // a list of ints followed by a list of doubles 376 | >; 377 | 378 | template* = nullptr > 379 | void foo(Args&& ... args) { 380 | using OL = hop::enable_t; 381 | 382 | auto&& int_args = hop::get_tagged_args(std::forward(args)...); 383 | auto&& double_args = hop::get_tagged_args(std::forward(args)...); 384 | std::apply(output_args, std::forward(int_args)); 385 | std::apply(output_args, std::forward(double_args)); 386 | 387 | static constexpr size_t int_args_count = hop::get_tagged_count(); 388 | os << "int args count: " << int_args_count << std::endl; 389 | static constexpr size_t double_args_count = hop::get_tagged_count(); 390 | os << "double args count: " << double_args_count << std::endl; 391 | } 392 | 393 | 394 | void test() { 395 | f((short)3); 396 | foo_plain(1, 2, (short)3, 3.4, 1.f); 397 | foo(1, 2, (short)3, 3.4, 1.f); 398 | foo(1, 2, 3, 3.4); 399 | foo(1, 2, (short)3, 3.4, 1.f); 400 | } 401 | } 402 | 403 | namespace ns_test_9 { 404 | // forwarding reference 405 | 406 | using overloads_t = hop::ol_list < 407 | hop::ol> // a list of forwarding references 408 | >; 409 | 410 | template* = nullptr > 411 | void foo(Args&& ... args) { 412 | using OL = hop::enable_t; 413 | 414 | output_args(std::forward(args)...); 415 | } 416 | 417 | 418 | void test() { 419 | foo(1, "a text", std::wstring(L"a wide string"), 2.4); 420 | } 421 | } 422 | 423 | namespace ns_test_10 { 424 | // forwarding reference with SFINAE condition 425 | using namespace boost::mp11; 426 | 427 | using overloads_t = hop::ol_list < 428 | hop::ol>>> // an int (no conversion to int) 429 | >; 430 | 431 | template* = nullptr > 432 | void foo(Args&& ... args) { 433 | using OL = hop::enable_t; 434 | 435 | output_args(std::forward(args)...); 436 | } 437 | 438 | 439 | void test() { 440 | foo(1); 441 | //foo(1L); // error: no matching overloaded function found 442 | //foo(42.0); // error: no matching overloaded function found 443 | } 444 | } 445 | 446 | namespace ns_test_11 { 447 | // SFINAE condition on whole parameter-set 448 | using namespace boost::mp11; 449 | 450 | template 451 | struct at_least_4_byte : mp_bool>) >= 4 && sizeof(std::remove_cv_t>) >= 4> {}; 452 | 453 | using overloads_t = hop::ol_list < 454 | hop::ol_if // an int (no conversion to int) 455 | >; 456 | 457 | template* = nullptr > 458 | void foo(Args&& ... args) { 459 | using OL = hop::enable_t; 460 | 461 | output_args(std::forward(args)...); 462 | } 463 | 464 | 465 | void test() { 466 | foo(1, 2); 467 | foo(1LL, 2); 468 | foo(4.2f, 2.4); 469 | // foo(1, (short)2); // error: no matching overloaded function found 470 | // foo('a', 2); // error: no matching overloaded function found 471 | } 472 | } 473 | 474 | namespace ns_test_12 { 475 | // accessing cpp-style default-params (with get_tagged_arg_or or get_indexed_defaulted) 476 | 477 | struct init_hallo { 478 | std::string operator()() const { return "hallo defaulted world"; } 479 | }; 480 | 481 | struct tag_defaulted_string; 482 | struct tag_defaulted_double; 483 | 484 | using overloads_t = hop::ol_list < 485 | hop::ol, init_hallo >, hop::cpp_defaulted_param >> 486 | >; 487 | 488 | template* = nullptr > 489 | void foo(Args&& ... args) { 490 | using OL = hop::enable_t; 491 | 492 | os << "get_tagged_arg_or:" << std::endl; 493 | auto defaulted_param_0 = hop::get_tagged_arg_or(-1, std::forward(args)...); 494 | auto defaulted_param_1 = hop::get_tagged_arg_or(-1, std::forward(args)...); 495 | std::apply(output_args, std::make_tuple(defaulted_param_0, defaulted_param_1)); 496 | 497 | os << "get_indexed_defaulted:" << std::endl; 498 | if constexpr (hop::defaults_specified
                        ::value == 0) { 499 | auto defaulted_param_0 = hop::get_indexed_defaulted(std::forward(args)...); 500 | auto defaulted_param_1 = hop::get_indexed_defaulted(std::forward(args)...); 501 | std::apply(output_args, std::make_tuple(defaulted_param_0, defaulted_param_1)); 502 | } else if constexpr (hop::defaults_specified
                          ::value == 1) { 503 | auto defaulted_param_0 = hop::get_indexed_defaulted(std::forward(args)...); 504 | auto defaulted_param_1 = hop::get_indexed_defaulted(std::forward(args)...); 505 | std::apply(output_args, std::make_tuple(defaulted_param_0, defaulted_param_1)); 506 | } else if constexpr (hop::defaults_specified
                            ::value == 2) { 507 | auto defaulted_param_0 = hop::get_indexed_defaulted(std::forward(args)...); 508 | auto defaulted_param_1 = hop::get_indexed_defaulted(std::forward(args)...); 509 | std::apply(output_args, std::make_tuple(defaulted_param_0, defaulted_param_1)); 510 | } else { 511 | static_assert(hop::dependent_false
                              ::value, "Ooops!"); 512 | } 513 | } 514 | 515 | void test() { 516 | foo(0); 517 | foo(1, "specified"); 518 | foo(2, "specified", 42.0); 519 | } 520 | } 521 | 522 | 523 | namespace ns_test_13 { 524 | // more than one overload 525 | 526 | using overloads_t = hop::ol_list < 527 | hop::ol // index 0 528 | , hop::ol // index 1 529 | , hop::ol> // index 2 530 | >; 531 | 532 | template* = nullptr > 533 | void foo(Args&& ... args) { 534 | using OL = hop::enable_t; 535 | 536 | if constexpr (hop::index
                                ::value == 0) { 537 | os << "overload: (long, long)" << std::endl; 538 | output_args(std::forward(args)...); 539 | } else if constexpr (hop::index
                                  ::value == 1) { 540 | os << "overload: (double, double)" << std::endl; 541 | output_args(std::forward(args)...); 542 | } else if constexpr (hop::index
                                    ::value == 2) { 543 | os << "overload: (std::string...)" << std::endl; 544 | output_args(std::forward(args)...); 545 | } else { 546 | static_assert(hop::dependent_false
                                      ::value, "Ooops!"); 547 | } 548 | } 549 | 550 | 551 | void test() { 552 | foo(1, 3L); 553 | // foo(1, 3LL); // error: no matching overloaded function found 554 | foo(1.0, 3.0); 555 | foo("one", "three"); 556 | } 557 | } 558 | 559 | 560 | 561 | namespace ns_test_14 { 562 | // tagging overloads 563 | 564 | struct tag_longs; 565 | struct tag_doubles; 566 | struct tag_strings; 567 | 568 | using overloads_t = hop::ol_list < 569 | hop::tagged_ol // index 0 570 | , hop::tagged_ol // index 1 571 | , hop::tagged_ol> // index 2 572 | >; 573 | 574 | template* = nullptr > 575 | void foo(Args&& ... args) { 576 | using OL = hop::enable_t; 577 | 578 | if constexpr (hop::has_tag_v) { 579 | os << "overload: (long, long)" << std::endl; 580 | output_args(std::forward(args)...); 581 | } else if constexpr (hop::has_tag_v) { 582 | os << "overload: (double, double)" << std::endl; 583 | output_args(std::forward(args)...); 584 | } else if constexpr (hop::has_tag_v) { 585 | os << "overload: (std::string...)" << std::endl; 586 | output_args(std::forward(args)...); 587 | } else { 588 | static_assert(hop::dependent_false
                                        ::value, "Ooops!"); 589 | } 590 | } 591 | 592 | 593 | void test() { 594 | foo(1, 3L); 595 | // foo(1, 3LL); // error: no matching overloaded function found 596 | foo(1.0, 3.0); 597 | foo("one", "three"); 598 | } 599 | } 600 | 601 | 602 | 603 | namespace ns_test_15 { 604 | // get_tagged_arg_or 605 | 606 | struct tag_long_arg; 607 | 608 | 609 | 610 | using overloads_t = hop::ol_list < 611 | hop::ol> 612 | , hop::ol>, double, double> 613 | , hop::ol 614 | >; 615 | 616 | template* = nullptr > 617 | void foo(Args&& ... args) { 618 | using OL = hop::enable_t; 619 | 620 | std::apply(output_args, hop::get_args
                                          (std::forward(args)...)); 621 | 622 | // hop::get_tagged_arg_or returns the specified tagged-parameter (or its default-value), if there is no parameter with the specified tag, then the 'or' value is returned 623 | auto&& long_arg = hop::get_tagged_arg_or(42.0, std::forward(args)...); 624 | output_args(std::forward(long_arg)); 625 | } 626 | 627 | class foo_class { 628 | public: 629 | template* = nullptr > 630 | foo_class(Args&& ... args) {} 631 | }; 632 | 633 | void test() { 634 | foo("text", 3L); 635 | foo(5, 1.0, 2.0); 636 | foo(11.0, 12.0); 637 | foo(11.0, 12.0, "another text"); 638 | 639 | auto fc1 = foo_class("text", 3L); 640 | auto fc2 = foo_class(5, 1.0, 2.0); 641 | auto fc3 = foo_class(11.0, 12.0); 642 | auto fc4 = foo_class(11.0, 12.0, "another text"); 643 | } 644 | } 645 | 646 | 647 | 648 | namespace ns_test_16 { 649 | // extending an overload set 650 | 651 | 652 | struct tag_ints {}; 653 | struct tag_doubles {}; 654 | struct tag_a_pack {}; 655 | 656 | using overloads = hop::ol_list < 657 | hop::tagged_ol>>, 658 | hop::tagged_ol> 659 | >; 660 | 661 | class base { 662 | public: 663 | 664 | 665 | template* = nullptr > 666 | void foo(Args&& ... args) { 667 | using OL = hop::enable_t; 668 | 669 | if constexpr (hop::has_tag_v) { 670 | os << "overload: base::foo(int, ...)" << std::endl; 671 | output_args(std::forward(args)...); 672 | } else if constexpr (hop::has_tag_v) { 673 | os << "overload: base::foo(double, ...)" << std::endl; 674 | output_args(std::forward(args)...); 675 | } 676 | } 677 | }; 678 | 679 | struct tag_strings {}; 680 | struct tag_floats {}; 681 | 682 | class derived : public base { 683 | public: 684 | 685 | using ext_overloads = hop::ol_extend < 686 | overloads, 687 | hop::tagged_ol>, 688 | hop::tagged_ol> 689 | >; 690 | 691 | template* = nullptr > 692 | void foo(Args&& ... args) { 693 | using OL = hop::enable_t; 694 | 695 | if constexpr (hop::is_from_base_v
                                            ) { 696 | base::foo(std::forward(args)...); 697 | } else if constexpr (hop::has_tag_v) { 698 | os << "overload: derived::foo(std::string, ...)" << std::endl; 699 | output_args(std::forward(args)...); 700 | } else if constexpr (hop::has_tag_v) { 701 | os << "overload: derived::foo(float, ...)" << std::endl; 702 | output_args(std::forward(args)...); 703 | } 704 | } 705 | }; 706 | 707 | 708 | 709 | void test() { 710 | base _base; 711 | _base.foo(42, 17); 712 | _base.foo(-0.4, 12.0); 713 | _base.foo(1.5f, -0.4f, 12.0f); 714 | _base.foo(0.1, 2.5f); 715 | // _base.foo(42, 17.0); // error: no matching overloaded function found 716 | 717 | derived _derived; 718 | _derived.foo(42, 17); 719 | _derived.foo(-0.4, 12.0); 720 | _derived.foo(1.5f, -0.4f, 12.0f); 721 | // _derived.foo(0.1, 2.5f); // error: ambigous 722 | // _base.foo(42, 17.0); // error: no matching overloaded function found 723 | _derived.foo("hello", "extended", "world!"); 724 | } 725 | } 726 | 727 | 728 | namespace ns_test_17 { 729 | // invoking different base-class constructors via constructor delegation 730 | // constructor delegation is needed when base-class constructors with different arities are involved 731 | // (the same technique can be used to invoke different constructors for class members) 732 | 733 | 734 | class base_class { 735 | public: 736 | base_class() {} 737 | base_class(std::string) {} 738 | }; 739 | 740 | 741 | class foo_class : public base_class { 742 | public: 743 | struct tag_ints {}; 744 | struct tag_string {}; 745 | 746 | using overloads_t = hop::ol_list < 747 | hop::tagged_ol> // calls base's default ctor 748 | , hop::tagged_ol // calls base's (std::string) ctor 749 | >; 750 | 751 | 752 | template* = nullptr > 753 | foo_class(Args&& ... args) 754 | // delegate constructor using tag-dispatching 755 | : foo_class(hop::get_tag_type>{}, std::forward(args)...) { 756 | using OL = hop::enable_t; 757 | 758 | output_args(std::forward(args)...); 759 | } 760 | 761 | private: 762 | // helper contructor to create base_class without arg 763 | template* = nullptr > 764 | foo_class(tag_ints, Args&& ... args) 765 | : base_class{} {} 766 | 767 | // helper contructor to create base_class with string arg 768 | template* = nullptr > 769 | foo_class(tag_string, Args&& ... args) 770 | : base_class{ hop::get_arg_at<0>(std::forward(args)...) } {} 771 | }; 772 | 773 | void test() { 774 | // foo("Hello"); 775 | auto foo_ints = foo_class(0, 1, 2, 3); 776 | auto foo_string = foo_class("a string", 0.5, -1.e5); 777 | } 778 | } 779 | 780 | 781 | namespace ns_test_18 { 782 | // global template type argument deduction 783 | 784 | template 785 | using map_alias = std::mapconst&; 786 | 787 | template 788 | using set_alias = std::setconst&; 789 | 790 | struct tag_map_set; 791 | 792 | 793 | using overloads_t = hop::ol_list < 794 | hop::tagged_ol, hop::deduce> 795 | >; 796 | 797 | template* = nullptr > 798 | void foo(Args&& ... args) { 799 | using OL = hop::enable_t; 800 | 801 | if constexpr (hop::has_tag_v) { 802 | output_args(std::forward(args)...); 803 | } 804 | } 805 | 806 | 807 | void test() { 808 | std::map my_map; 809 | std::set my_set; 810 | foo(my_map, my_set); 811 | 812 | std::set another_set; 813 | //foo(my_map, another_set); // error 814 | } 815 | } 816 | 817 | namespace ns_test_19 { 818 | // local template type argument deduction 819 | 820 | template 821 | using vector_alias = std::vectorconst&; 822 | 823 | template 824 | using list_alloc_alias = std::listconst&; 825 | 826 | struct tag_vector; 827 | struct tag_list_alloc; 828 | 829 | 830 | template 831 | void print_deduced(std::integer_sequence) { 832 | ((os << typeid(hop::deduced_local_types).name() << std::endl), ...); 833 | } 834 | 835 | 836 | using overloads_t = hop::ol_list < 837 | hop::tagged_ol>> 838 | , hop::tagged_ol> 839 | >; 840 | 841 | template* = nullptr > 842 | void foo(Args&& ... args) { 843 | using OL = hop::enable_t; 844 | 845 | if constexpr (hop::has_tag_v) { 846 | os << "deduced types\n"; 847 | print_deduced
                                              (std::make_index_sequence{}); 848 | 849 | output_args(std::forward(args)...); 850 | } else if constexpr (hop::has_tag_v) { 851 | using T = hop::deduced_local_types; 852 | os << typeid(T).name() << std::endl; 853 | output_args(std::forward(args)...); 854 | } 855 | } 856 | 857 | 858 | 859 | void test() { 860 | foo(std::list{}); 861 | 862 | std::vector v1; 863 | std::vector v2; 864 | std::vector v3; 865 | foo(v1, v2, v3); 866 | } 867 | } 868 | 869 | namespace ns_test_20 { 870 | // adapting existing functions 871 | 872 | 873 | void bar(int n, std::string s) { 874 | os << "bar called: " << std::endl; 875 | output_args(n, s); 876 | } 877 | 878 | template 879 | void qux(T t, double d1, double d2) { 880 | os << "qux called: " << std::endl; 881 | output_args(t, d1, d2); 882 | } 883 | 884 | // adapt overload-set 'qux' 885 | struct adapt_qux { 886 | template 887 | static decltype(qux(std::declval()...)) forward(Args&&... args) { 888 | return qux(std::forward(args)...); 889 | } 890 | }; 891 | 892 | 893 | void bar_empty(bool but_its_ok = false) { 894 | os << "a very sad function called"; 895 | if (but_its_ok) { 896 | os << ", but it's ok"; 897 | } else { 898 | os << ", bar is empty"; 899 | } 900 | os << std::endl; 901 | } 902 | 903 | 904 | // adapt function with defaulted parameters 'bar_empty' 905 | struct adapt_bar_empty { 906 | template 907 | static decltype(bar_empty(std::declval()...)) forward(Args&&... args) { 908 | return bar_empty(std::forward(args)...); 909 | } 910 | }; 911 | 912 | 913 | using overloads_t = hop::ol_list < 914 | hop::adapt 915 | , hop::adapted 916 | , 917 | hop::adapted 918 | >; 919 | 920 | template* = nullptr > 921 | void foo(Args&& ... args) { 922 | using OL = hop::enable_t; 923 | if constexpr (hop::is_adapted_v
                                                ) { 924 | return hop::forward_adapted
                                                  (std::forward(args)...); 925 | } else { 926 | using t = boost::mp11::mp_first
                                                    ; 927 | 928 | } 929 | } 930 | 931 | 932 | 933 | 934 | 935 | void test() { 936 | foo(0, "Hello"); 937 | foo(std::vector{}, 2, 3); 938 | foo(); 939 | foo(false); 940 | foo(true); 941 | } 942 | } 943 | 944 | 945 | 946 | namespace ns_test_21 { 947 | // alternatives and sequences 948 | 949 | using overloads_t = hop::ol_list < 950 | hop::ol, std::string>> 951 | , hop::ol>>> 952 | >; 953 | 954 | template* = nullptr > 955 | void foo(Args&& ... args) { 956 | using OL = hop::enable_t; 957 | 958 | output_args(std::forward(args)...); 959 | } 960 | 961 | 962 | 963 | 964 | 965 | void test() { 966 | foo("hello"); 967 | foo(42); 968 | foo(42, 1, 1); 969 | foo(42, 1); 970 | foo(42, 1); 971 | foo(); 972 | 973 | foo("a", 1.5, "b", "two", "c", false); 974 | } 975 | } 976 | 977 | 978 | namespace ns_test_22 { 979 | // using match_tag to select (kudos to Quuxplusone) 980 | 981 | struct tag_ints {}; 982 | struct tag_doubles {}; 983 | 984 | using overloads = hop::ol_list < 985 | hop::tagged_ol>, 986 | hop::tagged_ol> 987 | >; 988 | 989 | template = 0 991 | > 992 | void foo(Args&& ... args) { 993 | os << "int overload called: " << std::endl; 994 | output_args(std::forward(args)...); 995 | } 996 | 997 | template = 0 999 | > 1000 | void foo(Args&& ... args) { 1001 | os << "double overload called: " << std::endl; 1002 | output_args(std::forward(args)...); 1003 | } 1004 | 1005 | void test() { 1006 | foo(1, 2); 1007 | foo(1.0, 2); 1008 | foo(1, 2.0); 1009 | foo(1, 2.0, 3.0); 1010 | // foo(1, 2.0, 3); // error ambigous 1011 | } 1012 | } 1013 | 1014 | 1015 | 1016 | 1017 | namespace ns_test_23 { 1018 | // global & local template type argument deduction 1019 | 1020 | using namespace boost::mp11; 1021 | 1022 | struct special_allocator :std::allocator {}; 1023 | 1024 | // T1 is bound with hop::global_deduction_binding, thus all deduced T1 have to be the same 1025 | template 1026 | using vector_alias = std::vectorconst&; 1027 | 1028 | 1029 | struct tag_vector; 1030 | struct tag_vector_value_type; 1031 | 1032 | 1033 | using overloads_t = hop::ol_list < 1034 | hop::tagged_ol>, 1039 | vector_alias>>> 1040 | >; 1041 | 1042 | 1043 | template* = nullptr > 1044 | void foo(Args&& ... args) { 1045 | using OL = hop::enable_t; 1046 | 1047 | if constexpr (hop::has_tag_v) { 1048 | output_args(std::forward(args)...); 1049 | } 1050 | } 1051 | 1052 | 1053 | void test() { 1054 | std::vector vec_1; 1055 | std::vector vec_2; 1056 | foo(vec_1, vec_2); 1057 | 1058 | std::vector vec_3; 1059 | //foo(vec_1, vec_3); // error 1060 | } 1061 | } 1062 | 1063 | 1064 | 1065 | namespace ns_test_24 { 1066 | // global & local template type argument deduction 1067 | 1068 | using namespace boost::mp11; 1069 | 1070 | 1071 | // T1 is bound with hop::global_deduction_binding, thus all deduced T1 have to be the same 1072 | template 1073 | using map_alias = std::mapconst&; 1074 | 1075 | 1076 | struct tag_map; 1077 | struct tag_map_key_type; 1078 | 1079 | 1080 | using overloads_t = hop::ol_list < 1081 | hop::tagged_ol>, 1086 | map_alias>>> 1087 | >; 1088 | 1089 | 1090 | template* = nullptr > 1091 | void foo(Args&& ... args) { 1092 | using OL = hop::enable_t; 1093 | 1094 | if constexpr (hop::has_tag_v) { 1095 | output_args(std::forward(args)...); 1096 | } 1097 | } 1098 | 1099 | 1100 | void test() { 1101 | std::map map_1; 1102 | std::map map_2; 1103 | std::map> map_3; 1104 | foo(map_1, map_2, map_3); 1105 | 1106 | std::map map_4; 1107 | // foo(map_1, map_4); // error 1108 | } 1109 | } 1110 | 1111 | 1112 | 1113 | namespace ns_test_25 { 1114 | // local template type argument deduction with non-type template parameters 1115 | 1116 | // for non-type template parameters, we have to rewrite the core deduction template 1117 | // to deduce the expected types, templates and nont-type parameters 1118 | 1119 | template class Container, class T, size_t N> 1120 | using array_alias = Containerconst&; 1121 | 1122 | // wrapper to store the returned Container template 1123 | template class Container> 1124 | struct ContainerWrapper; 1125 | 1126 | struct tag_array; 1127 | 1128 | 1129 | using namespace boost::mp11; 1130 | 1131 | // look, three 'template' in a row 1132 | template class, class, size_t> class Pattern_> 1133 | struct my_deducer_local { 1134 | 1135 | // deduce types, templates and nont-type parameters and wrap all of them in to types 1136 | template class, class, size_t> class Pattern, template class Container, class T, size_t N> 1137 | static mp_list, T, std::integral_constant>> 1138 | test(Pattern); 1139 | 1140 | 1141 | template class Pattern> 1142 | static mp_list> test(...); 1143 | 1144 | template 1145 | using fn = mp_first(std::declval()))>; 1146 | 1147 | template 1148 | using deduced = mp_second(std::declval()))>; 1149 | }; 1150 | 1151 | 1152 | template class, class, size_t> class Pattern> 1153 | using my_deduce_local = hop::fwd_if_q>; 1154 | 1155 | 1156 | 1157 | using overloads_t = hop::ol_list < 1158 | hop::tagged_ol>> 1159 | >; 1160 | 1161 | template* = nullptr > 1162 | void foo(Args&& ... args) { 1163 | using OL = hop::enable_t; 1164 | 1165 | if constexpr (hop::has_tag_v) { 1166 | os << "array overload called\n"; 1167 | } 1168 | } 1169 | 1170 | 1171 | 1172 | void test() { 1173 | std::array v1; 1174 | std::array v2; 1175 | foo(v1, v2); 1176 | } 1177 | } 1178 | 1179 | 1180 | 1181 | 1182 | int main() { 1183 | 1184 | #define CALL_TEST(n) \ 1185 | os << std::endl << "START TEST " #n << std::endl << std::endl;\ 1186 | ns_test_##n::test(); 1187 | 1188 | CALL_TEST(1); 1189 | CALL_TEST(2); 1190 | CALL_TEST(3); 1191 | CALL_TEST(4); 1192 | CALL_TEST(5); 1193 | CALL_TEST(6); 1194 | CALL_TEST(7); 1195 | CALL_TEST(8); 1196 | CALL_TEST(9); 1197 | CALL_TEST(10); 1198 | CALL_TEST(11); 1199 | CALL_TEST(12); 1200 | CALL_TEST(13); 1201 | CALL_TEST(14); 1202 | CALL_TEST(15); 1203 | CALL_TEST(16); 1204 | CALL_TEST(17); 1205 | CALL_TEST(18); 1206 | CALL_TEST(19); 1207 | CALL_TEST(20); 1208 | CALL_TEST(21); 1209 | CALL_TEST(22); 1210 | CALL_TEST(23); 1211 | CALL_TEST(24); 1212 | 1213 | } 1214 | 1215 | -------------------------------------------------------------------------------- /test/hop_test.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31205.134 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hop_test", "hop_test.vcxproj", "{B3D10AF1-A48C-4A6D-A457-3E40FAD59B67}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {B3D10AF1-A48C-4A6D-A457-3E40FAD59B67}.Debug|x64.ActiveCfg = Debug|x64 17 | {B3D10AF1-A48C-4A6D-A457-3E40FAD59B67}.Debug|x64.Build.0 = Debug|x64 18 | {B3D10AF1-A48C-4A6D-A457-3E40FAD59B67}.Debug|x86.ActiveCfg = Debug|Win32 19 | {B3D10AF1-A48C-4A6D-A457-3E40FAD59B67}.Debug|x86.Build.0 = Debug|Win32 20 | {B3D10AF1-A48C-4A6D-A457-3E40FAD59B67}.Release|x64.ActiveCfg = Release|x64 21 | {B3D10AF1-A48C-4A6D-A457-3E40FAD59B67}.Release|x64.Build.0 = Release|x64 22 | {B3D10AF1-A48C-4A6D-A457-3E40FAD59B67}.Release|x86.ActiveCfg = Release|Win32 23 | {B3D10AF1-A48C-4A6D-A457-3E40FAD59B67}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {84FC103B-D13B-41CE-B480-AC4F0F3F8AF9} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /test/hop_test.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {b3d10af1-a48c-4a6d-a457-3e40fad59b67} 25 | hoptest 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | false 78 | 79 | 80 | true 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Level3 88 | true 89 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 90 | true 91 | stdcpplatest 92 | ../include;C:\loew\boost_1_75_0 93 | 94 | 95 | Console 96 | true 97 | 98 | 99 | 100 | 101 | Level3 102 | true 103 | true 104 | true 105 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 106 | true 107 | stdcpplatest 108 | ../include;C:\loew\boost_1_75_0 109 | 110 | 111 | Console 112 | true 113 | true 114 | true 115 | 116 | 117 | 118 | 119 | Level3 120 | true 121 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 122 | true 123 | stdcpplatest 124 | ../include;C:\loew\boost_1_75_0 125 | 126 | 127 | Console 128 | true 129 | 130 | 131 | 132 | 133 | Level3 134 | true 135 | true 136 | true 137 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 138 | true 139 | stdcpplatest 140 | ../include;C:\loew\boost_1_75_0 141 | 142 | 143 | Console 144 | true 145 | true 146 | true 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /test/hop_test.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | -------------------------------------------------------------------------------- /test/hop_test_2.cpp: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////// 2 | // 3 | // hop library 4 | // 5 | // Copyright Tobias Loew 2019. Use, modification and 6 | // distribution is subject to the Boost Software License, Version 7 | // 1.0. (See accompanying file LICENSE_1_0.txt or copy at 8 | // http://www.boost.org/LICENSE_1_0.txt) 9 | // 10 | // For more information, see https://github.com/tobias-loew/hop 11 | // 12 | 13 | #define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include "hop.hpp" 25 | 26 | 27 | //auto&& os = std::ofstream("hop_out.txt"); 28 | #include 29 | auto&& os = std::cout; 30 | 31 | template 32 | struct tag_t { 33 | using type = T; 34 | constexpr tag_t() {} 35 | }; 36 | 37 | 38 | std::string ws_to_s(std::wstring const& ws) { 39 | // use very simple ws to s converter (it's deprecated, but works for our purposes) 40 | return std::wstring_convert>().to_bytes(ws); 41 | } 42 | 43 | enum limiters { 44 | bracket, 45 | paren, 46 | brace, 47 | angle 48 | }; 49 | 50 | static constexpr char const* limit_symbol[][2]{ 51 | {"[","]"}, 52 | {"(",")"}, 53 | {"{","}"}, 54 | {"<",">"}, 55 | }; 56 | 57 | template 58 | std::string to_string(T&&); 59 | 60 | template 61 | std::string tuple_to_string(T&&); 62 | 63 | template 64 | decltype(auto) printer(T) { 65 | using type = std::remove_cv_t>; 66 | if constexpr (std::is_same::value) { 67 | // use very simple ws to s converter (it's deprecated, but works for our purposes) 68 | return [](type const& s) {return ws_to_s(s); }; 69 | } else if constexpr (boost::mp11::mp_similar>::value) { 70 | return [](type const& s) { return to_string(s); }; 71 | } else if constexpr (boost::mp11::mp_similar>::value) { 72 | return [](type const& s) { return tuple_to_string(s); }; 73 | } else if constexpr (boost::mp11::mp_similar>::value) { 74 | return [](type const& s) { return tuple_to_string(s); }; 75 | } else if constexpr (boost::mp11::mp_similar>::value) { 76 | return [](type const& s) { return to_string(s); }; 77 | } else if constexpr (boost::mp11::mp_similar>::value) { 78 | return [](type const& s) { return to_string(s); }; 79 | } else if constexpr (boost::mp11::mp_similar>::value) { 80 | return [](type const& s) { return to_string(s); }; 81 | } else { 82 | return [](auto&& t)->decltype(auto) { 83 | return std::forward(t); 84 | }; 85 | } 86 | } 87 | 88 | template 89 | decltype(auto) make_printable(T&& arg) { 90 | return printer(tag_t{})(std::forward(arg)); 91 | } 92 | 93 | template 94 | std::string to_string(T&& cont) { 95 | std::string result = limit_symbol[limit][0]; 96 | bool first = true; 97 | for (auto&& elem : cont) { 98 | if (first) { 99 | first = false; 100 | } else { 101 | result += ","; 102 | } 103 | std::stringstream ss; 104 | ss << make_printable(elem); 105 | result += ss.str(); 106 | } 107 | result += limit_symbol[limit][1]; 108 | return result; 109 | } 110 | 111 | 112 | template 113 | std::string tuple_to_string(T&& t) { 114 | std::string result = limit_symbol[limit][0]; 115 | bool first = true; 116 | 117 | std::apply([&](auto&&... args) {(( 118 | 119 | [&]() { 120 | if (first) { 121 | first = false; 122 | } else { 123 | result += ","; 124 | } 125 | std::stringstream ss; 126 | ss << make_printable(args); 127 | result += ss.str(); 128 | }() 129 | 130 | ), ...); }, std::forward(t)); 131 | 132 | result += limit_symbol[limit][1]; 133 | return result; 134 | } 135 | 136 | 137 | 138 | struct output_args_ { 139 | 140 | 141 | 142 | template 143 | void operator()(Ts&& ... ts) const { 144 | os << "args:" << std::endl; 145 | ((os << typeid(ts).name() << ": " << make_printable(std::forward(ts)) << std::endl), ...); 146 | os << std::endl; 147 | } 148 | }; 149 | 150 | output_args_ output_args; 151 | 152 | 153 | 154 | //namespace ns_test_9 { 155 | // // forwarding reference 156 | // template 157 | // concept PlainType = true; 158 | // 159 | // using overloads_t = hop::ol_list < 160 | // hop::ol> // a list of forwarding references 161 | // , hop::ol>> // a list of forwarding references 162 | // >; 163 | // 164 | // template = 0 > 165 | // void foo(Ts&& ... ts) { 166 | // using OL = hop::enable_t; 167 | // 168 | // output_args(std::forward(ts)...); 169 | // } 170 | // 171 | // 172 | // void test() { 173 | // // foo(1, "a text", std::wstring(L"a wide string"), 2.4); 174 | // foo(1, 2); 175 | // } 176 | //} 177 | 178 | namespace ns_test_22 { 179 | // using match_tag to select (kudos to Quuxplusone) 180 | 181 | struct tag_ints {}; 182 | struct tag_doubles {}; 183 | 184 | using overloads = hop::ol_list < 185 | hop::tagged_ol>, 186 | hop::tagged_ol> 187 | >; 188 | 189 | template = 0 191 | > 192 | void foo(Ts&& ... ts) { 193 | os << "ints overload called: " << std::endl; 194 | } 195 | 196 | template = 0 198 | > 199 | void foo(Ts&& ... ts) { 200 | os << "doubles overload called: " << std::endl; 201 | } 202 | 203 | void test() { 204 | foo(1, 2); 205 | foo(1.0, 2.0); 206 | foo(1.0f, 2.0); 207 | foo("",1.0, 2); 208 | //foo(1, 2.0); 209 | //foo(1, 2.0, 3.0); 210 | // foo(1, 2.0, 3); // error ambigous 211 | } 212 | } 213 | 214 | #if 0 215 | namespace ns_test_23 { 216 | // WRONG USAGE !!! 217 | // 218 | // using match_tag to select (kudos to Quuxplusone) 219 | 220 | struct tag_ints {}; 221 | struct tag_doubles {}; 222 | 223 | using overloads_int = hop::ol_list < 224 | hop::tagged_ol> 225 | >; 226 | using overloads_doubles = hop::ol_list < 227 | hop::tagged_ol> 228 | >; 229 | 230 | template = 0 > 231 | void foo(Ts&& ... ts) { 232 | os << "ints overload called: " << std::endl; 233 | } 234 | 235 | template = 0 > 236 | void foo(Ts&& ... ts) { 237 | os << "doubles overload called: " << std::endl; 238 | } 239 | 240 | void test() { 241 | foo(1, 2); 242 | foo(1.0, 2.0); 243 | foo(1.0f, 2.0); 244 | //foo(1.0, 2); 245 | //foo(1, 2.0); 246 | //foo(1, 2.0, 3.0); 247 | // foo(1, 2.0, 3); // error ambigous 248 | } 249 | } 250 | #endif 251 | 252 | int main() { 253 | 254 | #define CALL_TEST(n) \ 255 | os << std::endl << "START TEST " #n << std::endl << std::endl;\ 256 | ns_test_##n::test(); 257 | 258 | // CALL_TEST(9); 259 | CALL_TEST(22); 260 | 261 | } 262 | 263 | --------------------------------------------------------------------------------