| 1 | /*! |
| 2 | @file |
| 3 | Forward declares `boost::hana::tag_of` and `boost::hana::tag_of_t`. |
| 4 | |
| 5 | Copyright Louis Dionne 2013-2022 |
| 6 | Distributed under the Boost Software License, Version 1.0. |
| 7 | (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) |
| 8 | */ |
| 9 | |
| 10 | #ifndef BOOST_HANA_FWD_CORE_TAG_OF_HPP |
| 11 | #define BOOST_HANA_FWD_CORE_TAG_OF_HPP |
| 12 | |
| 13 | #include <boost/hana/config.hpp> |
| 14 | |
| 15 | |
| 16 | namespace boost { namespace hana { |
| 17 | //! @ingroup group-core |
| 18 | //! %Metafunction returning the tag associated to `T`. |
| 19 | //! |
| 20 | //! There are several ways to specify the tag of a C++ type. If it's a |
| 21 | //! user-defined type, one can define a nested `hana_tag` alias: |
| 22 | //! @code |
| 23 | //! struct MyUserDefinedType { |
| 24 | //! using hana_tag = MyTag; |
| 25 | //! }; |
| 26 | //! @endcode |
| 27 | //! |
| 28 | //! Sometimes, however, the C++ type can't be modified (if it's in a |
| 29 | //! foreign library) or simply can't have nested types (if it's not a |
| 30 | //! struct or class). In those cases, using a nested alias is impossible |
| 31 | //! and so ad-hoc customization is also supported by specializing |
| 32 | //! `tag_of` in the `boost::hana` namespace: |
| 33 | //! @code |
| 34 | //! struct i_cant_modify_this; |
| 35 | //! |
| 36 | //! namespace boost { namespace hana { |
| 37 | //! template <> |
| 38 | //! struct tag_of<i_cant_modify_this> { |
| 39 | //! using type = MyTag; |
| 40 | //! }; |
| 41 | //! }} |
| 42 | //! @endcode |
| 43 | //! |
| 44 | //! `tag_of` can also be specialized for all C++ types satisfying some |
| 45 | //! boolean condition using `when`. `when` accepts a single compile-time |
| 46 | //! boolean and enables the specialization of `tag_of` if and only if |
| 47 | //! that boolean is `true`. This is similar to the well known C++ idiom |
| 48 | //! of using a dummy template parameter with `std::enable_if` and relying |
| 49 | //! on SFINAE. For example, we could specify the tag of all |
| 50 | //! `fusion::vector`s by doing: |
| 51 | //! @code |
| 52 | //! struct BoostFusionVector; |
| 53 | //! |
| 54 | //! namespace boost { namespace hana { |
| 55 | //! template <typename T> |
| 56 | //! struct tag_of<T, when< |
| 57 | //! std::is_same< |
| 58 | //! typename fusion::traits::tag_of<T>::type, |
| 59 | //! fusion::traits::tag_of<fusion::vector<>>::type |
| 60 | //! >::value |
| 61 | //! >> { |
| 62 | //! using type = BoostFusionVector; |
| 63 | //! }; |
| 64 | //! }} |
| 65 | //! @endcode |
| 66 | //! |
| 67 | //! Also, when it is not specialized and when the given C++ type does not |
| 68 | //! have a nested `hana_tag` alias, `tag_of<T>` returns `T` itself. This |
| 69 | //! makes tags a simple extension of normal C++ types. This is _super_ |
| 70 | //! useful, mainly for two reasons. First, this allows Hana to adopt a |
| 71 | //! reasonable default behavior for some operations involving types that |
| 72 | //! have no notion of tags. For example, Hana allows comparing with `equal` |
| 73 | //! any two objects for which a valid `operator==` is defined, and that |
| 74 | //! without any work on the user side. Second, it also means that you can |
| 75 | //! ignore tags completely if you don't need their functionality; just use |
| 76 | //! the normal C++ type of your objects and everything will "just work". |
| 77 | //! |
| 78 | //! Finally, also note that `tag_of<T>` is always equivalent to `tag_of<U>`, |
| 79 | //! where `U` is the type `T` after being stripped of all references and |
| 80 | //! cv-qualifiers. This makes it unnecessary to specialize `tag_of` for |
| 81 | //! all reference and cv combinations, which would be a real pain. Also, |
| 82 | //! `tag_of` is required to be idempotent. In other words, it must always |
| 83 | //! be the case that `tag_of<tag_of<T>::%type>::%type` is equivalent to |
| 84 | //! `tag_of<T>::%type`. |
| 85 | //! |
| 86 | //! > __Tip 1__\n |
| 87 | //! > If compile-time performance is a serious concern, consider |
| 88 | //! > specializing the `tag_of` metafunction in Hana's namespace. |
| 89 | //! > When unspecialized, the metafunction has to use SFINAE, which |
| 90 | //! > tends to incur a larger compile-time overhead. For heavily used |
| 91 | //! > templated types, this can potentially make a difference. |
| 92 | //! |
| 93 | //! > __Tip 2__\n |
| 94 | //! > Consider using `tag_of_t` alias instead of `tag_of`, which |
| 95 | //! > reduces the amount of typing in dependent contexts. |
| 96 | //! |
| 97 | //! |
| 98 | //! Example |
| 99 | //! ------- |
| 100 | //! @include example/core/tag_of.cpp |
| 101 | #ifdef BOOST_HANA_DOXYGEN_INVOKED |
| 102 | template <typename T, optional when-based enabler> |
| 103 | struct tag_of { unspecified }; |
| 104 | #else |
| 105 | template <typename T, typename = void> |
| 106 | struct tag_of; |
| 107 | #endif |
| 108 | |
| 109 | //! @ingroup group-core |
| 110 | //! Alias to `tag_of<T>::%type`, provided for convenience. |
| 111 | //! |
| 112 | //! |
| 113 | //! Example |
| 114 | //! ------- |
| 115 | //! @include example/core/tag_of_t.cpp |
| 116 | template <typename T> |
| 117 | using tag_of_t = typename hana::tag_of<T>::type; |
| 118 | }} // end namespace boost::hana |
| 119 | |
| 120 | #endif // !BOOST_HANA_FWD_CORE_TAG_OF_HPP |
| 121 | |