row_traits.hpp 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. //
  2. // Copyright (c) 2019-2023 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
  3. //
  4. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  5. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. //
  7. #ifndef BHO_MYSQL_DETAIL_TYPING_ROW_TRAITS_HPP
  8. #define BHO_MYSQL_DETAIL_TYPING_ROW_TRAITS_HPP
  9. #include <asio2/bho/mysql/detail/config.hpp>
  10. #ifdef BHO_MYSQL_CXX14
  11. #include <asio2/bho/mysql/client_errc.hpp>
  12. #include <asio2/bho/mysql/diagnostics.hpp>
  13. #include <asio2/bho/mysql/error_code.hpp>
  14. #include <asio2/bho/mysql/field_view.hpp>
  15. #include <asio2/bho/mysql/metadata.hpp>
  16. #include <asio2/bho/mysql/metadata_collection_view.hpp>
  17. #include <asio2/bho/mysql/string_view.hpp>
  18. #include <asio2/bho/mysql/detail/config.hpp>
  19. #include <asio2/bho/mysql/detail/typing/meta_check_context.hpp>
  20. #include <asio2/bho/mysql/detail/typing/pos_map.hpp>
  21. #include <asio2/bho/mysql/detail/typing/readable_field_traits.hpp>
  22. #include <asio2/bho/assert.hpp>
  23. #include <asio2/bho/describe/members.hpp>
  24. #include <asio2/bho/mp11/algorithm.hpp>
  25. #include <asio2/bho/mp11/utility.hpp>
  26. #include <cstddef>
  27. #include <tuple>
  28. #include <type_traits>
  29. #include <utility>
  30. namespace bho {
  31. namespace mysql {
  32. namespace detail {
  33. // Helpers to check that all the fields satisfy ReadableField
  34. // and produce meaningful error messages, with the offending field type, at least
  35. // Workaround clang 3.6 not liking generic lambdas in the below constexpr function
  36. struct readable_field_checker
  37. {
  38. template <class TypeIdentity>
  39. constexpr void operator()(TypeIdentity) const noexcept
  40. {
  41. using T = typename TypeIdentity::type;
  42. static_assert(
  43. is_readable_field<T>::value,
  44. "You're trying to use an unsupported field type in a row type. Review your row type definitions."
  45. );
  46. }
  47. };
  48. template <class TypeList>
  49. static constexpr bool check_readable_field() noexcept
  50. {
  51. mp11::mp_for_each<TypeList>(readable_field_checker{});
  52. return true;
  53. }
  54. // Workaround std::array::data not being constexpr in C++14
  55. template <class T, std::size_t N>
  56. struct array_wrapper
  57. {
  58. T data_[N];
  59. constexpr bho::span<const T> span() const noexcept { return bho::span<const T>(data_); }
  60. };
  61. template <class T>
  62. struct array_wrapper<T, 0>
  63. {
  64. struct
  65. {
  66. } data_; // allow empty brace initialization
  67. constexpr bho::span<const T> span() const noexcept { return bho::span<const T>(); }
  68. };
  69. // Workaround for char_traits::length not being constexpr in C++14
  70. // Only used to retrieve Describe member name lengths
  71. constexpr std::size_t get_length(const char* s) noexcept
  72. {
  73. const char* p = s;
  74. while (*p)
  75. ++p;
  76. return p - s;
  77. }
  78. // Helpers
  79. class parse_functor
  80. {
  81. span<const std::size_t> pos_map_;
  82. span<const field_view> fields_;
  83. std::size_t index_{};
  84. error_code ec_;
  85. public:
  86. parse_functor(span<const std::size_t> pos_map, span<const field_view> fields) noexcept
  87. : pos_map_(pos_map), fields_(fields)
  88. {
  89. }
  90. template <class ReadableField>
  91. void operator()(ReadableField& output)
  92. {
  93. auto ec = readable_field_traits<ReadableField>::parse(
  94. map_field_view(pos_map_, index_++, fields_),
  95. output
  96. );
  97. if (!ec_)
  98. ec_ = ec;
  99. }
  100. error_code error() const noexcept { return ec_; }
  101. };
  102. // Base template
  103. template <class T, bool is_describe_struct = bho::describe::has_describe_members<T>::value>
  104. class row_traits;
  105. // Describe structs
  106. template <class DescribeStruct>
  107. using row_members = bho::describe::
  108. describe_members<DescribeStruct, bho::describe::mod_public | bho::describe::mod_inherited>;
  109. template <class MemberDescriptor>
  110. constexpr string_view get_member_name(MemberDescriptor d) noexcept
  111. {
  112. return string_view(d.name, get_length(d.name));
  113. }
  114. template <template <class...> class ListType, class... MemberDescriptor>
  115. constexpr array_wrapper<string_view, sizeof...(MemberDescriptor)> get_describe_names(ListType<
  116. MemberDescriptor...>)
  117. {
  118. return {{get_member_name(MemberDescriptor())...}};
  119. }
  120. template <class DescribeStruct>
  121. constexpr auto describe_names_storage = get_describe_names(row_members<DescribeStruct>{});
  122. template <class DescribeStruct>
  123. class row_traits<DescribeStruct, true>
  124. {
  125. using members = row_members<DescribeStruct>;
  126. template <class D>
  127. struct descriptor_to_type
  128. {
  129. using helper = decltype(std::declval<DescribeStruct>().*std::declval<D>().pointer);
  130. using type = typename std::remove_reference<helper>::type;
  131. };
  132. using member_types = mp11::mp_transform<descriptor_to_type, members>;
  133. static_assert(check_readable_field<member_types>(), "");
  134. public:
  135. using types = member_types;
  136. static constexpr std::size_t size() noexcept { return bho::mp11::mp_size<members>::value; }
  137. static constexpr name_table_t name_table() noexcept
  138. {
  139. return describe_names_storage<DescribeStruct>.span();
  140. }
  141. static void parse(parse_functor& parser, DescribeStruct& to)
  142. {
  143. bho::mp11::mp_for_each<members>([&](auto D) { parser(to.*D.pointer); });
  144. }
  145. };
  146. // Tuples
  147. template <class T>
  148. struct is_tuple : std::false_type
  149. {
  150. };
  151. template <class... T>
  152. struct is_tuple<std::tuple<T...>> : std::true_type
  153. {
  154. };
  155. template <class... ReadableField>
  156. class row_traits<std::tuple<ReadableField...>, false>
  157. {
  158. using tuple_type = std::tuple<ReadableField...>;
  159. using field_types = bho::mp11::mp_list<bho::mp11::mp_identity<ReadableField>...>;
  160. static_assert(check_readable_field<field_types>(), "");
  161. public:
  162. using types = field_types;
  163. static constexpr std::size_t size() noexcept { return std::tuple_size<tuple_type>::value; }
  164. static constexpr name_table_t name_table() noexcept { return name_table_t(); }
  165. static void parse(parse_functor& parser, tuple_type& to) { bho::mp11::tuple_for_each(to, parser); }
  166. };
  167. // We want is_static_row to only inspect the shape of the row (i.e. it's a tuple vs. it's nothing we know),
  168. // and not individual fields. These are static_assert-ed in individual row_traits. This gives us an error
  169. // message that contains the offending types, at least.
  170. template <class T>
  171. struct is_static_row
  172. {
  173. static constexpr bool value = is_tuple<T>::value || describe::has_describe_members<T>::value;
  174. };
  175. #ifdef BHO_MYSQL_HAS_CONCEPTS
  176. template <class T>
  177. concept static_row = is_static_row<T>::value;
  178. #define BHO_MYSQL_STATIC_ROW ::bho::mysql::detail::static_row
  179. #else
  180. #define BHO_MYSQL_STATIC_ROW class
  181. #endif
  182. // External interface
  183. template <BHO_MYSQL_STATIC_ROW StaticRow>
  184. constexpr std::size_t get_row_size()
  185. {
  186. return row_traits<StaticRow>::size();
  187. }
  188. template <BHO_MYSQL_STATIC_ROW StaticRow>
  189. constexpr name_table_t get_row_name_table()
  190. {
  191. return row_traits<StaticRow>::name_table();
  192. }
  193. template <BHO_MYSQL_STATIC_ROW StaticRow>
  194. error_code meta_check(span<const std::size_t> pos_map, metadata_collection_view meta, diagnostics& diag)
  195. {
  196. using fields = typename row_traits<StaticRow>::types;
  197. BHO_ASSERT(pos_map.size() == get_row_size<StaticRow>());
  198. return meta_check_field_type_list<fields>(pos_map, get_row_name_table<StaticRow>(), meta, diag);
  199. }
  200. template <BHO_MYSQL_STATIC_ROW StaticRow>
  201. error_code parse(span<const std::size_t> pos_map, span<const field_view> from, StaticRow& to)
  202. {
  203. BHO_ASSERT(pos_map.size() == get_row_size<StaticRow>());
  204. BHO_ASSERT(from.size() >= get_row_size<StaticRow>());
  205. parse_functor ctx(pos_map, from);
  206. row_traits<StaticRow>::parse(ctx, to);
  207. return ctx.error();
  208. }
  209. using meta_check_fn_t =
  210. error_code (*)(span<const std::size_t> field_map, metadata_collection_view meta, diagnostics& diag);
  211. // For multi-resultset - helper
  212. template <class... StaticRow>
  213. constexpr std::size_t max_num_columns = (std::max)({get_row_size<StaticRow>()...});
  214. } // namespace detail
  215. } // namespace mysql
  216. } // namespace bho
  217. #endif // BHO_MYSQL_CXX14
  218. #endif