1 #pragma once
2 
3 #include <systemd/sd-bus.h>
4 
5 #include <sdbusplus/message/native_types.hpp>
6 #include <sdbusplus/utility/container_traits.hpp>
7 #include <sdbusplus/utility/type_traits.hpp>
8 
9 #include <map>
10 #include <set>
11 #include <string>
12 #include <tuple>
13 #include <variant>
14 #include <vector>
15 
16 namespace sdbusplus
17 {
18 
19 namespace message
20 {
21 
22 namespace types
23 {
24 
25 /** @fn type_id()
26  *  @brief Get a tuple containing the dbus type character(s) for a
27  *         sequence of C++ types.
28  *
29  *  @tparam Args - Types to get the type_id sequence for.
30  *  @returns A tuple of characters representing the dbus types for ...Args.
31  *
32  *  The design uses a tuple because a tuple can, at compile-time, be easily
33  *  appended to, concatenated, and converted to an array (representing a
34  *  C-string).  There are other options to create a string of characters but
35  *  they require runtime processing and memory allocation.  By performing all
36  *  options at compile-time the use of type-deduced dbus strings is equal to
37  *  the cost of hard-coded type string constants.
38  */
39 template <typename... Args>
40 constexpr auto type_id();
41 
42 /** @fn type_id_nonull()
43  *  @brief A non-null-terminated version of type_id.
44  *
45  *  This is useful when type-ids may need to be concatenated.
46  */
47 template <typename... Args>
48 constexpr auto type_id_nonull();
49 
50 namespace details
51 {
52 
53 /** @brief Downcast type submembers.
54  *
55  * This allows std::tuple and std::pair members to be downcast to their
56  * non-const, nonref versions of themselves to limit duplication in template
57  * specializations
58  *
59  *  1. Remove references.
60  *  2. Remove 'const' and 'volatile'.
61  *  3. Convert 'char[N]' to 'char*'.
62  */
63 template <typename T>
64 struct downcast_members
65 {
66     using type = T;
67 };
68 template <typename... Args>
69 struct downcast_members<std::pair<Args...>>
70 {
71     using type = std::pair<utility::array_to_ptr_t<
72         char, std::remove_cv_t<std::remove_reference_t<Args>>>...>;
73 };
74 
75 template <typename... Args>
76 struct downcast_members<std::tuple<Args...>>
77 {
78     using type = std::tuple<utility::array_to_ptr_t<
79         char, std::remove_cv_t<std::remove_reference_t<Args>>>...>;
80 };
81 
82 template <typename T>
83 using downcast_members_t = typename downcast_members<T>::type;
84 
85 /** @brief Convert some C++ types to others for 'type_id' conversion purposes.
86  *
87  *  Similar C++ types have the same dbus type-id, so 'downcast' those to limit
88  *  duplication in type_id template specializations.
89  *
90  *  1. Remove references.
91  *  2. Remove 'const' and 'volatile'.
92  *  3. Convert 'char[N]' to 'char*'.
93  */
94 template <typename T>
95 struct type_id_downcast
96 {
97     using type = utility::array_to_ptr_t<
98         char, downcast_members_t<std::remove_cv_t<std::remove_reference_t<T>>>>;
99 };
100 
101 template <typename T>
102 using type_id_downcast_t = typename type_id_downcast<T>::type;
103 
104 /** @struct undefined_type_id
105  *  @brief Special type indicating no dbus-type_id is defined for a C++ type.
106  */
107 struct undefined_type_id
108 {
109     /** An empty tuple indicating no type-characters. */
110     // We want this to be tuple so that we can use operations like
111     // tuple_cat on it without cascading compile failures.  There is a
112     // static_assert in type_id_single to ensure this is never used, but to
113     // keep the compile failures as clean as possible.
114     static constexpr auto value = std::make_tuple();
115 };
116 
117 /** @brief Special type indicating a tuple of dbus-type_id's.
118  *
119  *  @tparam C1 - The first dbus type character character.
120  *  @tparam C - The remaining sequence of dbus type characters.
121  *
122  *  A tuple_type_id must be one or more characters.  The C1 template param
123  *  ensures at least one is present.
124  */
125 template <char C1, char... C>
126 struct tuple_type_id
127 {
128 /* This version check is required because a fix for auto is in 5.2+.
129  * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66421
130  */
131 /** A tuple containing the type-characters. */
132 #if (__GNUC__ > 5) || (__GNUC__ == 5 && (__GNUC_MINOR__ >= 2))
133     static constexpr auto value = std::make_tuple(C1, C...);
134 #else
135     static constexpr decltype(std::make_tuple(C1, C...)) value =
136         std::make_tuple(C1, C...);
137 #endif
138 };
139 
140 template <char... Chars>
141 inline constexpr auto tuple_type_id_v = tuple_type_id<Chars...>::value;
142 
143 /** @fn type_id_single()
144  *  @brief Get a tuple containing the dbus type character(s) for a C++ type.
145  *
146  *  @tparam T - The type to get the dbus type character(s) for.
147  */
148 template <typename T>
149 constexpr auto type_id_single();
150 
151 /** @fn type_id_multiple()
152  *  @brief Get a tuple containing the dbus type characters for a sequence of
153  *         C++ types.
154  *
155  *  @tparam T - The first type to get the dbus type character(s) for.
156  *  @tparam Args - The remaining types.
157  */
158 template <typename T, typename... Args>
159 constexpr auto type_id_multiple();
160 
161 /** @brief Defined dbus type tuple for a C++ type.
162  *
163  *  @tparam T - C++ type.
164  *
165  *  Struct must have a 'value' tuple containing the dbus type.  The default
166  *  value is an empty tuple, which is used to indicate an unsupported type.
167  *
168  *  Enums are converted to strings on the dbus by some special conversion
169  *  routines.
170  */
171 template <typename T, typename Enable = void>
172 struct type_id :
173     public std::conditional_t<
174         std::is_enum_v<T>, tuple_type_id<SD_BUS_TYPE_STRING>, undefined_type_id>
175 {};
176 
177 template <typename... Args>
178 inline constexpr auto type_id_v = type_id<Args...>::value;
179 
180 // Specializations for built-in types.
181 template <>
182 struct type_id<bool> : tuple_type_id<SD_BUS_TYPE_BOOLEAN>
183 {};
184 template <>
185 struct type_id<uint8_t> : tuple_type_id<SD_BUS_TYPE_BYTE>
186 {};
187 // int8_t isn't supported by dbus.
188 template <>
189 struct type_id<uint16_t> : tuple_type_id<SD_BUS_TYPE_UINT16>
190 {};
191 template <>
192 struct type_id<int16_t> : tuple_type_id<SD_BUS_TYPE_INT16>
193 {};
194 template <>
195 struct type_id<uint32_t> : tuple_type_id<SD_BUS_TYPE_UINT32>
196 {};
197 template <>
198 struct type_id<int32_t> : tuple_type_id<SD_BUS_TYPE_INT32>
199 {};
200 template <>
201 struct type_id<uint64_t> : tuple_type_id<SD_BUS_TYPE_UINT64>
202 {};
203 template <>
204 struct type_id<int64_t> : tuple_type_id<SD_BUS_TYPE_INT64>
205 {};
206 // float isn't supported by dbus.
207 template <>
208 struct type_id<double> : tuple_type_id<SD_BUS_TYPE_DOUBLE>
209 {};
210 template <>
211 struct type_id<const char*> : tuple_type_id<SD_BUS_TYPE_STRING>
212 {};
213 template <>
214 struct type_id<char*> : tuple_type_id<SD_BUS_TYPE_STRING>
215 {};
216 template <>
217 struct type_id<unix_fd> : tuple_type_id<SD_BUS_TYPE_UNIX_FD>
218 {};
219 template <>
220 struct type_id<std::string> : tuple_type_id<SD_BUS_TYPE_STRING>
221 {};
222 template <>
223 struct type_id<std::string_view> : tuple_type_id<SD_BUS_TYPE_STRING>
224 {};
225 template <>
226 struct type_id<object_path> : tuple_type_id<SD_BUS_TYPE_OBJECT_PATH>
227 {};
228 template <>
229 struct type_id<signature> : tuple_type_id<SD_BUS_TYPE_SIGNATURE>
230 {};
231 
232 template <typename T>
233 struct type_id<T, std::enable_if_t<utility::has_const_iterator_v<T>>> :
234     std::false_type
235 {
236     static constexpr auto value =
237         std::tuple_cat(tuple_type_id_v<SD_BUS_TYPE_ARRAY>,
238                        type_id_v<type_id_downcast_t<typename T::value_type>>);
239 };
240 
241 template <typename T1, typename T2>
242 struct type_id<std::pair<T1, T2>>
243 {
244     static constexpr auto value = std::tuple_cat(
245         tuple_type_id_v<SD_BUS_TYPE_DICT_ENTRY_BEGIN>,
246         type_id_v<type_id_downcast_t<T1>>, type_id_v<type_id_downcast_t<T2>>,
247         tuple_type_id_v<SD_BUS_TYPE_DICT_ENTRY_END>);
248 };
249 
250 template <typename... Args>
251 struct type_id<std::tuple<Args...>>
252 {
253     static constexpr auto value =
254         std::tuple_cat(tuple_type_id_v<SD_BUS_TYPE_STRUCT_BEGIN>,
255                        type_id_v<type_id_downcast_t<Args>>...,
256                        tuple_type_id_v<SD_BUS_TYPE_STRUCT_END>);
257 };
258 
259 template <typename... Args>
260 struct type_id<std::variant<Args...>> : tuple_type_id<SD_BUS_TYPE_VARIANT>
261 {};
262 
263 template <>
264 struct type_id<void>
265 {
266     constexpr static auto value = std::make_tuple('\0');
267 };
268 
269 template <>
270 struct type_id<std::monostate>
271 {
272     constexpr static auto value = std::make_tuple('\0');
273 };
274 
275 template <typename T>
type_id_single()276 constexpr auto type_id_single()
277 {
278     static_assert(!std::is_base_of_v<undefined_type_id, type_id<T>>,
279                   "No dbus type conversion provided for type.");
280     return type_id_v<T>;
281 }
282 
283 template <typename T, typename... Args>
type_id_multiple()284 constexpr auto type_id_multiple()
285 {
286     return std::tuple_cat(type_id_single<T>(), type_id_single<Args>()...);
287 }
288 
289 template <typename T, std::size_t... I>
type_id_tuple(std::index_sequence<I...>)290 constexpr auto type_id_tuple(std::index_sequence<I...>)
291 {
292     return type_id_multiple<
293         type_id_downcast_t<std::tuple_element_t<I, T>>...>();
294 }
295 
296 } // namespace details
297 
298 template <typename... Args>
type_id()299 constexpr auto type_id()
300 {
301     return std::tuple_cat(
302         details::type_id_multiple<details::type_id_downcast_t<Args>...>(),
303         std::make_tuple('\0') /* null terminator for C-string */);
304 }
305 
306 // Special case for empty type.
307 template <>
type_id()308 constexpr auto type_id()
309 {
310     return std::make_tuple('\0');
311 }
312 
313 template <typename... Args>
type_id_nonull()314 constexpr auto type_id_nonull()
315 {
316     return details::type_id_multiple<details::type_id_downcast_t<Args>...>();
317 }
318 
319 template <typename T>
type_id_tuple()320 constexpr auto type_id_tuple()
321 {
322     return std::tuple_cat(
323         details::type_id_tuple<T>(
324             std::make_index_sequence<
325                 std::tuple_size_v<details::type_id_downcast_t<T>>>()),
326         std::make_tuple('\0'));
327 }
328 
329 } // namespace types
330 
331 } // namespace message
332 
333 } // namespace sdbusplus
334