|
1 | | - |
| 1 | +magic-cpp is a header-only C++ library. It aims to make it easier for you to use C++, including a series of functions such as visualizing type names, reflection of structs and enumerations, etc. It can help you get rid of the compiler's error messages which are difficult to read |
2 | 2 |
|
3 | | -magic-cpp is C++20 header-only library provides static reflection for type, struct and enum, no external dependencies, no registration, no macro, no code generation. |
4 | 3 |
|
5 | | -# Documentation |
6 | | -* [Reference](doc/reference.md) |
7 | | -* [Limitations](doc/limitations.md) |
8 | | -* [Integration](#Integration) |
| 4 | +- [Visualizing type](#visualizing-type) |
| 5 | + - [Basic usage](#basic-usage) |
| 6 | + - [Custom type name](#custom-type-name) |
| 7 | + - [Configurability](#configurability) |
| 8 | + - [Other features](#other-features) |
9 | 9 |
|
10 | | -# [Examples](example/) |
11 | | -## type |
12 | 10 |
|
13 | | -**just include `magic/type.h` to use the features below** |
| 11 | +# Visualizing type |
| 12 | +`#include <magic/visualize.h>`to use features below, `C++17` is minimum required |
14 | 13 |
|
15 | | -- *retrieve the full name of a type*: |
| 14 | +## Basic usage |
| 15 | +when using template programming, you often encounter the dilemma of type mismatch, especially when using libraries like `ranges`, templates are often deeply nested and difficult to read. Do not worry, `magic-cpp` can help you visualize the type, making it easier for human to understand the type. Consider the following example |
16 | 16 | ```cpp |
17 | | -struct Point{}; |
18 | | - |
19 | | -constexpr auto name = magic::full_name_of<Point>(); |
20 | | -// name => "Point" |
| 17 | +using T = int (*(*(*)(int*))[4])(int*); // hard to understand |
| 18 | +std::cout << magic::visualize<T>() << std::endl; |
| 19 | +``` |
| 20 | +Output: |
21 | 21 |
|
22 | | -constexpr auto name2 = magic::full_name_of<std::vector<int>>(); |
23 | | -// name2 => "std::vector<int>" |
| 22 | + |
24 | 23 |
|
25 | | -constexpr auto name3 = magic::full_name_of<std::size_t>(); |
26 | | -// name3 => "unsigned long long int" |
27 | | -``` |
28 | | -- *retrieve the display name of a type* |
| 24 | +Or you may encounter this when writing code |
29 | 25 | ```cpp |
30 | | -// specialize for type to customize the display name |
31 | | -template<> |
32 | | -struct magic::type_info<std::size_t> |
33 | | -{ |
34 | | - constexpr static auto value = "std::size_t"; |
35 | | -}; |
| 26 | +using T = std::function<int(const std::vector<int>&, std::tuple<int, int, int>)>; // hard to understand |
| 27 | +std::cout << magic::visualize<T>() << std::endl; |
| 28 | +``` |
| 29 | +Output: |
| 30 | + |
| 31 | + |
| 32 | + |
| 33 | +Almost all commonly used templates can be converted into such a tree representation, which is clear at a glance. Of course, the full expansion of some types is very long and not what you expected, for example, the output effect of `std::string` on `gcc` is like this |
| 34 | + |
| 35 | + |
36 | 36 |
|
| 37 | +## Custom type name |
| 38 | +As you can see, there is a lot of information we don't want to see. It doesn't matter! Provide a custom type name through explicit specialization |
| 39 | +```cpp |
37 | 40 | template<> |
38 | 41 | struct magic::type_info<std::string> |
39 | 42 | { |
40 | | - constexpr static auto value = "std::string"; |
| 43 | + inline static std::string name = "std::string"; |
41 | 44 | }; |
42 | | - |
43 | | -// use name_of to get the display name |
44 | | -constexpr auto name = magic::name_of<std::size_t>(); |
45 | | -// name => "std::size_t" |
46 | | - |
47 | | -constexpr auto name2 = magic::name_of<std::string>(); |
48 | | -// name => "std::string" |
49 | 45 | ``` |
| 46 | +In this way, when encountering `std::string`, only the following will be displayed |
50 | 47 |
|
51 | | -- *visualize type* |
52 | | -```cpp |
53 | | -// for some complex type, it is hard to understand for human |
54 | | -// we can visualize it as a tree to make it more readable |
55 | | - |
56 | | -using T = int (*(*(*)(int*))[4])(int*); // hard to understand |
57 | | -std::cout << magic::full_tree_of<T>() << std::endl; |
58 | | -``` |
59 | | -*Output:* |
| 48 | + |
60 | 49 |
|
61 | | - |
| 50 | +It's the custom name, isn't it convenient? I have pre-defined some commonly used type aliases such as `std::size_t`, `std::string`, `std::vector` in `customization.h`. If you need it, you can try to modify or add it yourself |
62 | 51 |
|
63 | | ----------------- |
| 52 | +## Configurability |
| 53 | +Considering that some terminals do not support color, or do not support `utf` characters, the display will appear garbled, so we provide options to turn off these functions |
64 | 54 | ```cpp |
65 | | -using T = std::map<int, std::string>; |
66 | | -std::cout << magic::full_tree_of<T>() << std::endl; |
| 55 | +magic::VisualizeOption option; |
| 56 | +option.UTF8_SUPPORT = false; // do not use utf8 characters |
| 57 | +option.COLORFUL_SUPPORT = false; // turn off color support |
| 58 | +option.FULL_NAME = true; // use full name instead of custom alias |
| 59 | +std::cout << magic::visualize<std::string>(option) << std::endl; |
67 | 60 | ``` |
68 | | -*Output*: |
69 | | - |
| 61 | +Output: |
70 | 62 |
|
71 | | -you can also use `tree_of` to get a more compact form, more details see [type](docs/type.md). |
72 | | - |
73 | | -## [aggregate class](https://en.cppreference.com/w/cpp/language/aggregate_initialization#Definitions) |
74 | | -**just include `magic/struct.h` to use these features** |
| 63 | + |
75 | 64 |
|
| 65 | +if you want to customize the color scheme, you can use the `HighlightConfig` structure |
76 | 66 | ```cpp |
77 | | -struct Complex |
| 67 | +struct HighlightConfig |
78 | 68 | { |
79 | | - std::string name; |
80 | | - std::vector<int> vec; |
81 | | -}; // Complex is an aggregate class |
82 | | -``` |
| 69 | + std::uint32_t type; // type: int, double, ... |
| 70 | + std::uint32_t nttp; // non type template parameter: 1, 2, ... |
| 71 | + std::uint32_t tmpl; // template: std::vector, ... |
| 72 | + std::uint32_t builtin; // built-in compound type: ptr, ref... |
| 73 | + std::uint32_t modifier; // modifier: const, volatile, ... |
| 74 | + std::uint32_t tag; // tag: R: , M: , ... |
| 75 | +}; |
83 | 76 |
|
84 | | -- *retrieve field count of an aggregate class* |
85 | | -```cpp |
86 | | -constexpr auto count = magic::field_count_of<Complex>(); |
87 | | -// count => 2 |
88 | | -``` |
89 | | -- *retrieve the field types for an aggregate class* |
90 | | -```cpp |
91 | | -using Types = magic::field_types_of<Complex>; |
92 | | -// Types => std::tuple<std::string, std::vector<int>> |
93 | | -``` |
94 | | -- *retrieve the field type at a given index for an aggregate class* |
95 | | -```cpp |
96 | | -using First = magic::field_type_of<Complex, 0>; |
97 | | -// First => std::string |
| 77 | +// 默认采用的配色方案是 Dark |
| 78 | +constexpr static inline HighlightConfig Dark = { |
| 79 | + .type = 0xE5C07B, // yellow |
| 80 | + .nttp = 0xD19A66, // orange |
| 81 | + .tmpl = 0x0087CE, // blue |
| 82 | + .builtin = 0xC678DD, // purple |
| 83 | + .modifier = 0x98C379, // green |
| 84 | + .tag = 0x5C6370, // gray |
| 85 | +}; |
98 | 86 |
|
99 | | -using Second = magic::field_type_of<Complex, 1>; |
100 | | -// Second => std::vector<int> |
| 87 | +// there is also a built-in Light style color scheme |
101 | 88 | ``` |
102 | | -- *retrieve the field names for an aggregate class* |
| 89 | +You can also customize the color scheme yourself, and then pass it to the `visualize` function |
103 | 90 | ```cpp |
104 | | -constexpr auto names = magic::field_names_of<Complex>(); |
105 | | -// names => std::array{"name", "vec"} |
| 91 | +magic::VisualizeOption option; // default option |
| 92 | +std::cout << magic::visualize<std::string>(option, magic::Light) << std::endl; |
106 | 93 | ``` |
| 94 | +## Other features |
| 95 | +Besides visualizing types, we also support some other operations |
107 | 96 |
|
108 | | -- *retrieve the field name at a given index for an aggregate class* |
| 97 | +retrieving a type's `display_name` |
109 | 98 | ```cpp |
110 | | -constexpr auto name1 = magic::field_name_of<Complex>(0); |
111 | | -// name1 => "name" |
112 | 99 |
|
113 | | -constexpr auto name2 = magic::field_name_of<Complex>(1); |
114 | | -// name2 => "vec" |
115 | | -``` |
116 | | - |
117 | | -- *retrieve the field reference at a given index for an aggregate class* |
118 | | -```cpp |
119 | | -Complex c{"hello", {1, 2, 3}}; |
120 | | - |
121 | | -auto& value = magic::field_of<0>(c); |
122 | | -// value => "hello" |
123 | 100 |
|
124 | | -auto& value2 = magic::field_of<1>(c); |
125 | | -// value2 => std::vector{1, 2, 3} |
126 | 101 | ``` |
127 | 102 |
|
128 | | -- *traverse all fields of an aggregate class* |
| 103 | +retrieving `raw_name` in compile time |
129 | 104 | ```cpp |
130 | | -auto f = [](auto field) |
| 105 | +template<typename T> |
| 106 | +struct Point |
131 | 107 | { |
132 | | - constexpr auto index = field.index(); |
133 | | - constexpr auto name = field.name(); |
134 | | - using type = decltype(field)::type; |
135 | | - if constexpr (name == "name") |
136 | | - { |
137 | | - std::cout << "index: " << index |
138 | | - << ", name: " << name |
139 | | - << ", value: " << field.value() << std::endl; |
140 | | - } |
141 | | - else if constexpr (name == "vec") |
142 | | - { |
143 | | - std::cout << "index: " << index |
144 | | - << ", name: " << name |
145 | | - << ", value: " << field.value()[0] << std::endl; |
146 | | - } |
| 108 | + T start; |
| 109 | + T end; |
147 | 110 | }; |
148 | 111 |
|
149 | | -magic::foreach (complex, f); |
150 | | -// out => |
151 | | -// index: 0, name: name, value: hello |
152 | | -// index: 1, name: vec, value: 1 |
153 | | -``` |
154 | | -## enum |
155 | | -**just include `magic/enum.h` to use these features** |
156 | | -```cpp |
157 | | -enum Color |
158 | | -{ |
159 | | - RED, |
160 | | - GREEN, |
161 | | - BLUE |
162 | | -}; |
163 | | -``` |
164 | | -- *retrieve the enum count* |
165 | | -```cpp |
166 | | -constexpr auto count = magic::enum_count_of<Color>(); |
167 | | -// count => 3 |
168 | | -``` |
169 | | -- *retrieve the enum names* |
170 | | -```cpp |
171 | | -constexpr auto& names = magic::enum_names_of<Color>(); |
172 | | -// names => std::array{"RED", "GREEN", "BLUE"} |
173 | | -``` |
174 | | -- *retrieve the enum name* |
175 | | -```cpp |
176 | | -constexpr auto name = magic::name_of<Color::RED>(); |
177 | | -// name => "RED" |
| 112 | +// retrieving a type's raw_name |
| 113 | +constexpr auto name = magic::raw_name_of<Point<int>>(); |
| 114 | +// name => "Point<int>" |
178 | 115 |
|
179 | | -Color color = Color::GREEN; |
180 | | -auto name2 = magic::name_of(color); |
181 | | -// name => "GREEN" |
182 | | -``` |
183 | | -- *retrieve the bit field enum value* |
184 | | -```cpp |
| 116 | +// retrieving a non-type template parameter's raw_name |
| 117 | +constexpr auto name2 = magic::raw_name_of<1>(); |
| 118 | +// name2 => "1" |
185 | 119 |
|
| 120 | +// retrieving a template's raw_name |
| 121 | +constexpr auto name3 = magic::raw_name_of_template<Point<int>>(); |
| 122 | +// name3 => "Point" |
186 | 123 |
|
187 | | -``` |
| 124 | +// retrieving a member's raw_name, C++20 or higher is required |
| 125 | +Point point; |
| 126 | +constexpr auto name4 = magic::raw_name_of_member<&point.start>(); |
| 127 | +// name4 => "start" |
188 | 128 |
|
189 | | -- *traverse all enum values* |
190 | | -```cpp |
| 129 | +constexpr auto name5 = magic::raw_name_of_member<&point.end>(); |
| 130 | +// name5 => "end" |
191 | 131 |
|
192 | | -``` |
193 | 132 |
|
194 | | -# Limitations |
| 133 | +enum class Color |
| 134 | +{ |
| 135 | + RED, |
| 136 | + GREEN, |
| 137 | + BLUE, |
| 138 | +}; |
195 | 139 |
|
196 | | -# Support |
197 | | -- `gcc` > 12 |
198 | | -- `clang` > 15 |
199 | | -- `msvc` > 19.29 |
| 140 | +// retrieving an enumeration's raw_name |
| 141 | +constexpr auto name6 = magic::raw_name_of<Color::RED>(); |
| 142 | +// name6 => "RED" |
| 143 | +``` |
| 144 | +Please note that the content obtained by these methods may be different on different compilers, please do not use them to build the core part of the code. |
0 commit comments