File indexing completed on 2025-07-05 08:39:51
0001
0002
0003
0004
0005
0006
0007
0008 #ifndef BOOST_MYSQL_DETAIL_TYPING_ROW_TRAITS_HPP
0009 #define BOOST_MYSQL_DETAIL_TYPING_ROW_TRAITS_HPP
0010
0011 #include <boost/mysql/detail/config.hpp>
0012
0013 #ifdef BOOST_MYSQL_CXX14
0014
0015 #include <boost/mysql/client_errc.hpp>
0016 #include <boost/mysql/diagnostics.hpp>
0017 #include <boost/mysql/error_code.hpp>
0018 #include <boost/mysql/field_view.hpp>
0019 #include <boost/mysql/metadata.hpp>
0020 #include <boost/mysql/metadata_collection_view.hpp>
0021 #include <boost/mysql/string_view.hpp>
0022
0023 #include <boost/mysql/detail/config.hpp>
0024 #include <boost/mysql/detail/typing/meta_check_context.hpp>
0025 #include <boost/mysql/detail/typing/pos_map.hpp>
0026 #include <boost/mysql/detail/typing/readable_field_traits.hpp>
0027
0028 #include <boost/assert.hpp>
0029 #include <boost/describe/members.hpp>
0030 #include <boost/mp11/algorithm.hpp>
0031 #include <boost/mp11/list.hpp>
0032 #include <boost/mp11/utility.hpp>
0033
0034 #include <cstddef>
0035 #include <tuple>
0036 #include <type_traits>
0037 #include <utility>
0038
0039 namespace boost {
0040 namespace mysql {
0041 namespace detail {
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060 struct row_traits_is_unspecialized
0061 {
0062 };
0063
0064 template <class T, bool is_describe_struct = describe::has_describe_members<T>::value>
0065 class row_traits : public row_traits_is_unspecialized
0066 {
0067 };
0068
0069
0070
0071
0072
0073 template <class T, std::size_t N>
0074 struct array_wrapper
0075 {
0076 T data_[N];
0077
0078 constexpr boost::span<const T> span() const noexcept { return boost::span<const T>(data_); }
0079 };
0080
0081 template <class T>
0082 struct array_wrapper<T, 0>
0083 {
0084 struct
0085 {
0086 } data_;
0087
0088 constexpr boost::span<const T> span() const noexcept { return boost::span<const T>(); }
0089 };
0090
0091
0092
0093 constexpr std::size_t get_length(const char* s) noexcept
0094 {
0095 const char* p = s;
0096 while (*p)
0097 ++p;
0098 return p - s;
0099 }
0100
0101 template <class DescribeStruct>
0102 using row_members = describe::
0103 describe_members<DescribeStruct, describe::mod_public | describe::mod_inherited>;
0104
0105 template <template <class...> class ListType, class... MemberDescriptor>
0106 constexpr array_wrapper<string_view, sizeof...(MemberDescriptor)> get_describe_names(ListType<
0107 MemberDescriptor...>)
0108 {
0109 return {{string_view(MemberDescriptor::name, get_length(MemberDescriptor::name))...}};
0110 }
0111
0112 template <class DescribeStruct>
0113 BOOST_INLINE_CONSTEXPR auto describe_names_storage = get_describe_names(row_members<DescribeStruct>{});
0114
0115 template <class DescribeStruct>
0116 class row_traits<DescribeStruct, true>
0117 {
0118
0119 template <class D>
0120 using descriptor_to_type = typename
0121 std::remove_reference<decltype(std::declval<DescribeStruct>().*std::declval<D>().pointer)>::type;
0122
0123
0124 public:
0125 using underlying_row_type = DescribeStruct;
0126 using field_types = mp11::mp_transform<descriptor_to_type, row_members<DescribeStruct>>;
0127
0128 static constexpr name_table_t name_table() noexcept
0129 {
0130 return describe_names_storage<DescribeStruct>.span();
0131 }
0132
0133 template <class F>
0134 static void for_each_member(DescribeStruct& to, F&& function)
0135 {
0136 mp11::mp_for_each<row_members<DescribeStruct>>([function, &to](auto D) { function(to.*D.pointer); });
0137 }
0138 };
0139
0140
0141
0142
0143 template <class... ReadableField>
0144 class row_traits<std::tuple<ReadableField...>, false>
0145 {
0146 public:
0147 using underlying_row_type = std::tuple<ReadableField...>;
0148 using field_types = std::tuple<ReadableField...>;
0149 static constexpr name_table_t name_table() noexcept { return name_table_t(); }
0150
0151 template <class F>
0152 static void for_each_member(underlying_row_type& to, F&& function)
0153 {
0154 mp11::tuple_for_each(to, std::forward<F>(function));
0155 }
0156 };
0157
0158
0159
0160
0161
0162
0163
0164
0165 struct readable_field_checker
0166 {
0167 template <class TypeIdentity>
0168 constexpr void operator()(TypeIdentity) const noexcept
0169 {
0170 using T = typename TypeIdentity::type;
0171 static_assert(
0172 is_readable_field<T>::value,
0173 "You're trying to use an unsupported field type in a row type. Review your row type definitions."
0174 );
0175 }
0176 };
0177
0178 template <class TypeList>
0179 static constexpr bool check_readable_field() noexcept
0180 {
0181 mp11::mp_for_each<mp11::mp_transform<mp11::mp_identity, TypeList>>(readable_field_checker{});
0182 return true;
0183 }
0184
0185
0186 template <class StaticRow>
0187 struct row_traits_with_check : row_traits<StaticRow>
0188 {
0189 static_assert(check_readable_field<typename row_traits<StaticRow>::field_types>(), "");
0190 };
0191
0192
0193 struct meta_check_field_fn
0194 {
0195 meta_check_context& ctx;
0196
0197 template <class TypeIdentity>
0198 void operator()(TypeIdentity)
0199 {
0200 meta_check_field<typename TypeIdentity::type>(ctx);
0201 }
0202 };
0203
0204
0205 template <class ReadableFieldList>
0206 error_code meta_check_impl(
0207 name_table_t name_table,
0208 span<const std::size_t> pos_map,
0209 metadata_collection_view meta,
0210 diagnostics& diag
0211 )
0212 {
0213 BOOST_ASSERT(pos_map.size() == mp11::mp_size<ReadableFieldList>::value);
0214 meta_check_context ctx(pos_map, name_table, meta);
0215 mp11::mp_for_each<mp11::mp_transform<mp11::mp_identity, ReadableFieldList>>(meta_check_field_fn{ctx});
0216 return ctx.check_errors(diag);
0217 }
0218
0219
0220 class parse_context
0221 {
0222 span<const std::size_t> pos_map_;
0223 span<const field_view> fields_;
0224 std::size_t index_{};
0225 error_code ec_;
0226
0227 public:
0228 parse_context(span<const std::size_t> pos_map, span<const field_view> fields) noexcept
0229 : pos_map_(pos_map), fields_(fields)
0230 {
0231 }
0232
0233 template <class ReadableField>
0234 void parse(ReadableField& output)
0235 {
0236 auto ec = readable_field_traits<ReadableField>::parse(
0237 map_field_view(pos_map_, index_++, fields_),
0238 output
0239 );
0240 if (!ec_)
0241 ec_ = ec;
0242 }
0243
0244 error_code error() const noexcept { return ec_; }
0245 };
0246
0247
0248 struct parse_functor
0249 {
0250 parse_context& ctx;
0251
0252 template <class ReadableField>
0253 void operator()(ReadableField& output) const
0254 {
0255 ctx.parse(output);
0256 }
0257 };
0258
0259
0260
0261
0262
0263 template <class T>
0264 BOOST_INLINE_CONSTEXPR bool
0265 is_static_row = !std::is_base_of<row_traits_is_unspecialized, row_traits<T>>::value;
0266
0267 #ifdef BOOST_MYSQL_HAS_CONCEPTS
0268
0269
0270
0271
0272 template <class T>
0273 concept static_row = is_static_row<T>;
0274
0275 #define BOOST_MYSQL_STATIC_ROW ::boost::mysql::detail::static_row
0276
0277 #else
0278 #define BOOST_MYSQL_STATIC_ROW class
0279 #endif
0280
0281 template <BOOST_MYSQL_STATIC_ROW StaticRow>
0282 using underlying_row_t = typename row_traits_with_check<StaticRow>::underlying_row_type;
0283
0284 template <BOOST_MYSQL_STATIC_ROW StaticRow>
0285 constexpr std::size_t get_row_size() noexcept
0286 {
0287 return mp11::mp_size<typename row_traits_with_check<StaticRow>::field_types>::value;
0288 }
0289
0290 template <BOOST_MYSQL_STATIC_ROW StaticRow>
0291 constexpr name_table_t get_row_name_table() noexcept
0292 {
0293 return row_traits_with_check<StaticRow>::name_table();
0294 }
0295
0296 template <BOOST_MYSQL_STATIC_ROW StaticRow>
0297 error_code meta_check(span<const std::size_t> pos_map, metadata_collection_view meta, diagnostics& diag)
0298 {
0299 using field_types = typename row_traits_with_check<StaticRow>::field_types;
0300 BOOST_ASSERT(pos_map.size() == get_row_size<StaticRow>());
0301 return meta_check_impl<field_types>(get_row_name_table<StaticRow>(), pos_map, meta, diag);
0302 }
0303
0304 template <BOOST_MYSQL_STATIC_ROW StaticRow>
0305 error_code parse(
0306 span<const std::size_t> pos_map,
0307 span<const field_view> from,
0308 underlying_row_t<StaticRow>& to
0309 )
0310 {
0311 BOOST_ASSERT(pos_map.size() == get_row_size<StaticRow>());
0312 BOOST_ASSERT(from.size() >= get_row_size<StaticRow>());
0313 parse_context ctx(pos_map, from);
0314 row_traits_with_check<StaticRow>::for_each_member(to, parse_functor{ctx});
0315 return ctx.error();
0316 }
0317
0318 using meta_check_fn_t =
0319 error_code (*)(span<const std::size_t> field_map, metadata_collection_view meta, diagnostics& diag);
0320
0321
0322 template <class... StaticRow>
0323 BOOST_INLINE_CONSTEXPR std::size_t max_num_columns = (std::max)({get_row_size<StaticRow>()...});
0324
0325 BOOST_INLINE_CONSTEXPR std::size_t index_not_found = static_cast<std::size_t>(-1);
0326
0327 template <class UnderlyingRowType, class... RowType>
0328 constexpr std::size_t get_type_index() noexcept
0329 {
0330 using lunique = mp11::mp_unique<mp11::mp_list<underlying_row_t<RowType>...>>;
0331 using index_t = mp11::mp_find<lunique, UnderlyingRowType>;
0332 return index_t::value < mp11::mp_size<lunique>::value ? index_t::value : index_not_found;
0333 }
0334
0335 }
0336 }
0337 }
0338
0339 #endif
0340
0341 #endif