Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-05 08:39:51

0001 //
0002 // Copyright (c) 2019-2024 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
0003 //
0004 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0005 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
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 // Base templates. Every StaticRow type must specialize the row_traits class,
0045 // providing the following members:
0046 //
0047 //    // type of the actual row to be parsed. This supports marker types, like pfr_by_name
0048 //    using underlying_row_type = /* */;
0049 //
0050 //    // MP11 type list with the row's member types
0051 //    using field_types = /* */;
0052 //
0053 //    static constexpr name_table_t name_table() noexcept; // field names
0054 //
0055 //    template <class F> /* Apply F to each member */
0056 //    static void for_each_member(underlying_row_t<StaticRow>& to, F&& function);
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 // Describe structs
0071 //
0072 // Workaround std::array::data not being constexpr in C++14
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_;  // allow empty brace initialization
0087 
0088     constexpr boost::span<const T> span() const noexcept { return boost::span<const T>(); }
0089 };
0090 
0091 // Workaround for char_traits::length not being constexpr in C++14
0092 // Only used to retrieve Describe member name lengths
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     // clang-format off
0119     template <class D>
0120     using descriptor_to_type = typename
0121         std::remove_reference<decltype(std::declval<DescribeStruct>().*std::declval<D>().pointer)>::type;
0122     // clang-format on
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 // Tuples
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 // Helpers to implement the external interface section
0160 //
0161 
0162 // Helpers to check that all the fields satisfy ReadableField
0163 // and produce meaningful error messages, with the offending field type, at least
0164 // Workaround clang 3.6 not liking generic lambdas in the below constexpr function
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 // Centralize where check_readable_field happens
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 // Meta checking
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 // Useful for testing
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 // Parsing
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 // Using this instead of a lambda reduces the number of generated instantiations
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 // External interface. Other Boost.MySQL components should never use row_traits
0261 // directly, but the functions below, instead.
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 // Note that static_row only inspects the shape of the row only (i.e. it's a tuple vs. it's nothing we know),
0270 // and not individual fields. These are static_assert-ed in individual row_traits. This gives us an error
0271 // message that contains the offending types, at least.
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 // For multi-resultset
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 }  // namespace detail
0336 }  // namespace mysql
0337 }  // namespace boost
0338 
0339 #endif  // BOOST_MYSQL_CXX14
0340 
0341 #endif