Added thirdparty: boost library

This commit is contained in:
Viacheslav Demydiuk
2024-01-06 19:55:56 +02:00
parent bf49f439e1
commit bccd1e7051
15683 changed files with 3239840 additions and 0 deletions
+32
View File
@@ -0,0 +1,32 @@
//
// Copyright (c) 2019-2023 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOST_MYSQL_DETAIL_TYPING_GET_TYPE_INDEX_HPP
#define BOOST_MYSQL_DETAIL_TYPING_GET_TYPE_INDEX_HPP
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/list.hpp>
namespace boost {
namespace mysql {
namespace detail {
constexpr std::size_t index_not_found = static_cast<std::size_t>(-1);
template <class SpanRowType, class... RowType>
constexpr std::size_t get_type_index() noexcept
{
using lunique = mp11::mp_unique<mp11::mp_list<RowType...>>;
using index_t = mp11::mp_find<lunique, SpanRowType>;
return index_t::value < mp11::mp_size<lunique>::value ? index_t::value : index_not_found;
}
} // namespace detail
} // namespace mysql
} // namespace boost
#endif
+135
View File
@@ -0,0 +1,135 @@
//
// Copyright (c) 2019-2023 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOST_MYSQL_DETAIL_TYPING_META_CHECK_CONTEXT_HPP
#define BOOST_MYSQL_DETAIL_TYPING_META_CHECK_CONTEXT_HPP
#include <boost/mysql/client_errc.hpp>
#include <boost/mysql/column_type.hpp>
#include <boost/mysql/diagnostics.hpp>
#include <boost/mysql/error_code.hpp>
#include <boost/mysql/metadata.hpp>
#include <boost/mysql/metadata_collection_view.hpp>
#include <boost/mysql/string_view.hpp>
#include <boost/mysql/detail/access.hpp>
#include <boost/mysql/detail/config.hpp>
#include <boost/mysql/detail/typing/pos_map.hpp>
#include <memory>
#include <sstream>
namespace boost {
namespace mysql {
namespace detail {
inline const char* column_type_to_str(const metadata& meta) noexcept
{
switch (meta.type())
{
case column_type::tinyint: return meta.is_unsigned() ? "TINYINT UNSIGNED" : "TINYINT";
case column_type::smallint: return meta.is_unsigned() ? "SMALLINT UNSIGNED" : "SMALLINT";
case column_type::mediumint: return meta.is_unsigned() ? "MEDIUMINT UNSIGNED" : "MEDIUMINT";
case column_type::int_: return meta.is_unsigned() ? "INT UNSIGNED" : "INT";
case column_type::bigint: return meta.is_unsigned() ? "BIGINT UNSIGNED" : "BIGINT";
case column_type::float_: return "FLOAT";
case column_type::double_: return "DOUBLE";
case column_type::decimal: return "DECIMAL";
case column_type::bit: return "BIT";
case column_type::year: return "YEAR";
case column_type::time: return "TIME";
case column_type::date: return "DATE";
case column_type::datetime: return "DATETIME";
case column_type::timestamp: return "TIMESTAMP";
case column_type::char_: return "CHAR";
case column_type::varchar: return "VARCHAR";
case column_type::binary: return "BINARY";
case column_type::varbinary: return "VARBINARY";
case column_type::text: return "TEXT";
case column_type::blob: return "BLOB";
case column_type::enum_: return "ENUM";
case column_type::set: return "SET";
case column_type::json: return "JSON";
case column_type::geometry: return "GEOMETRY";
default: return "<unknown column type>";
}
}
class meta_check_context
{
std::unique_ptr<std::ostringstream> errors_;
std::size_t current_index_{};
span<const std::size_t> pos_map_;
name_table_t name_table_;
metadata_collection_view meta_{};
bool nullability_checked_{};
std::ostringstream& add_error()
{
if (!errors_)
errors_.reset(new std::ostringstream);
else
*errors_ << '\n';
return *errors_;
}
void insert_field_name(std::ostringstream& os)
{
if (has_field_names(name_table_))
os << "'" << name_table_[current_index_] << "'";
else
os << "in position " << current_index_;
}
public:
meta_check_context(
span<const std::size_t> pos_map,
name_table_t name_table,
metadata_collection_view meta
) noexcept
: pos_map_(pos_map), name_table_(name_table), meta_(meta)
{
}
// Accessors
const metadata& current_meta() const noexcept { return map_metadata(pos_map_, current_index_, meta_); }
bool is_current_field_absent() const noexcept { return pos_map_[current_index_] == pos_absent; }
// Iteration
void advance() noexcept
{
nullability_checked_ = false;
++current_index_;
}
// Nullability
void set_nullability_checked() noexcept { nullability_checked_ = true; }
bool nullability_checked() const noexcept { return nullability_checked_; }
// Error reporting
BOOST_MYSQL_DECL
void add_field_absent_error();
BOOST_MYSQL_DECL
void add_type_mismatch_error(const char* cpp_type_name);
BOOST_MYSQL_DECL
void add_nullability_error();
BOOST_MYSQL_DECL
error_code check_errors(diagnostics& diag) const;
};
} // namespace detail
} // namespace mysql
} // namespace boost
#ifdef BOOST_MYSQL_HEADER_ONLY
#include <boost/mysql/impl/meta_check_context.ipp>
#endif
#endif
+92
View File
@@ -0,0 +1,92 @@
//
// Copyright (c) 2019-2023 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOST_MYSQL_DETAIL_TYPING_POS_MAP_HPP
#define BOOST_MYSQL_DETAIL_TYPING_POS_MAP_HPP
#include <boost/mysql/field_view.hpp>
#include <boost/mysql/metadata.hpp>
#include <boost/mysql/metadata_collection_view.hpp>
#include <boost/mysql/string_view.hpp>
#include <boost/assert.hpp>
#include <boost/core/span.hpp>
#include <cstddef>
namespace boost {
namespace mysql {
namespace detail {
// These functions map C++ type positions to positions to positions in the DB query
constexpr std::size_t pos_absent = static_cast<std::size_t>(-1);
using name_table_t = boost::span<const string_view>;
inline bool has_field_names(name_table_t name_table) noexcept { return !name_table.empty(); }
inline void pos_map_reset(span<std::size_t> self) noexcept
{
for (std::size_t i = 0; i < self.size(); ++i)
self.data()[i] = pos_absent;
}
inline void pos_map_add_field(
span<std::size_t> self,
name_table_t name_table,
std::size_t db_index,
string_view field_name
) noexcept
{
if (has_field_names(name_table))
{
BOOST_ASSERT(self.size() == name_table.size());
// We're mapping fields by name. Try to find where in our target struct
// is the current field located
auto it = std::find(name_table.begin(), name_table.end(), field_name);
if (it != name_table.end())
{
std::size_t cpp_index = it - name_table.begin();
self[cpp_index] = db_index;
}
}
else
{
// We're mapping by position. Any extra trailing fields are discarded
if (db_index < self.size())
{
self[db_index] = db_index;
}
}
}
inline field_view map_field_view(
span<const std::size_t> self,
std::size_t cpp_index,
span<const field_view> array
) noexcept
{
BOOST_ASSERT(cpp_index < self.size());
return array[self[cpp_index]];
}
inline const metadata& map_metadata(
span<const std::size_t> self,
std::size_t cpp_index,
metadata_collection_view meta
) noexcept
{
BOOST_ASSERT(cpp_index < self.size());
return meta[self[cpp_index]];
}
} // namespace detail
} // namespace mysql
} // namespace boost
#endif
+622
View File
@@ -0,0 +1,622 @@
//
// Copyright (c) 2019-2023 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOST_MYSQL_DETAIL_TYPING_READABLE_FIELD_TRAITS_HPP
#define BOOST_MYSQL_DETAIL_TYPING_READABLE_FIELD_TRAITS_HPP
#include <boost/mysql/client_errc.hpp>
#include <boost/mysql/date.hpp>
#include <boost/mysql/datetime.hpp>
#include <boost/mysql/diagnostics.hpp>
#include <boost/mysql/error_code.hpp>
#include <boost/mysql/field_kind.hpp>
#include <boost/mysql/field_view.hpp>
#include <boost/mysql/metadata.hpp>
#include <boost/mysql/metadata_collection_view.hpp>
#include <boost/mysql/string_view.hpp>
#include <boost/mysql/time.hpp>
#include <boost/mysql/detail/config.hpp>
#include <boost/mysql/detail/typing/meta_check_context.hpp>
#include <boost/mysql/detail/typing/pos_map.hpp>
#include <boost/mysql/detail/void_t.hpp>
#include <cstdint>
#include <limits>
#include <string>
#include <type_traits>
namespace boost {
namespace mysql {
namespace detail {
// Helpers for integers
template <class SignedInt>
error_code parse_signed_int(field_view input, SignedInt& output)
{
using unsigned_t = typename std::make_unsigned<SignedInt>::type;
using limits_t = std::numeric_limits<SignedInt>;
auto kind = input.kind();
if (kind == field_kind::int64)
{
auto v = input.get_int64();
if (v < (limits_t::min)() || v > (limits_t::max)())
{
return client_errc::static_row_parsing_error;
}
output = static_cast<SignedInt>(v);
return error_code();
}
else if (kind == field_kind::uint64)
{
auto v = input.get_uint64();
if (v > static_cast<unsigned_t>((limits_t::max)()))
{
return client_errc::static_row_parsing_error;
}
output = static_cast<SignedInt>(v);
return error_code();
}
else
{
return client_errc::static_row_parsing_error;
}
}
template <class UnsignedInt>
error_code parse_unsigned_int(field_view input, UnsignedInt& output)
{
if (input.kind() != field_kind::uint64)
{
return client_errc::static_row_parsing_error;
}
auto v = input.get_uint64();
if (v > (std::numeric_limits<UnsignedInt>::max)())
{
return client_errc::static_row_parsing_error;
}
output = static_cast<UnsignedInt>(v);
return error_code();
}
// We want all integer types to be allowed as fields. Some integers
// may have the same width as others, but different type (e.g. long and long long
// may both be 64-bit, but different types). Auxiliar int_traits to allow this to work
template <class T, bool is_signed = std::is_signed<T>::value, std::size_t width = sizeof(T)>
struct int_traits
{
static constexpr bool is_supported = false;
};
template <class T>
struct int_traits<T, true, 1>
{
static constexpr bool is_supported = true;
static constexpr const char* type_name = "int8_t";
static bool meta_check(meta_check_context& ctx)
{
switch (ctx.current_meta().type())
{
case column_type::tinyint: return !ctx.current_meta().is_unsigned();
default: return false;
}
}
static error_code parse(field_view input, T& output) { return parse_signed_int(input, output); }
};
template <class T>
struct int_traits<T, false, 1>
{
static constexpr bool is_supported = true;
static constexpr const char* type_name = "uint8_t";
static bool meta_check(meta_check_context& ctx)
{
switch (ctx.current_meta().type())
{
case column_type::tinyint: return ctx.current_meta().is_unsigned();
default: return false;
}
}
static error_code parse(field_view input, T& output) { return parse_unsigned_int(input, output); }
};
template <class T>
struct int_traits<T, true, 2>
{
static constexpr bool is_supported = true;
static constexpr const char* type_name = "int16_t";
static bool meta_check(meta_check_context& ctx)
{
switch (ctx.current_meta().type())
{
case column_type::tinyint: return true;
case column_type::smallint:
case column_type::year: return !ctx.current_meta().is_unsigned();
default: return false;
}
}
static error_code parse(field_view input, T& output) { return parse_signed_int(input, output); }
};
template <class T>
struct int_traits<T, false, 2>
{
static constexpr bool is_supported = true;
static constexpr const char* type_name = "uint16_t";
static bool meta_check(meta_check_context& ctx)
{
switch (ctx.current_meta().type())
{
case column_type::tinyint:
case column_type::smallint:
case column_type::year: return ctx.current_meta().is_unsigned();
default: return false;
}
}
static error_code parse(field_view input, T& output) { return parse_unsigned_int(input, output); }
};
template <class T>
struct int_traits<T, true, 4>
{
static constexpr bool is_supported = true;
static constexpr const char* type_name = "int32_t";
static bool meta_check(meta_check_context& ctx)
{
switch (ctx.current_meta().type())
{
case column_type::tinyint:
case column_type::smallint:
case column_type::year:
case column_type::mediumint: return true;
case column_type::int_: return !ctx.current_meta().is_unsigned();
default: return false;
}
}
static error_code parse(field_view input, T& output) { return parse_signed_int(input, output); }
};
template <class T>
struct int_traits<T, false, 4>
{
static constexpr bool is_supported = true;
static constexpr const char* type_name = "uint32_t";
static bool meta_check(meta_check_context& ctx)
{
switch (ctx.current_meta().type())
{
case column_type::tinyint:
case column_type::smallint:
case column_type::year:
case column_type::mediumint:
case column_type::int_: return ctx.current_meta().is_unsigned();
default: return false;
}
}
static error_code parse(field_view input, T& output) { return parse_unsigned_int(input, output); }
};
template <class T>
struct int_traits<T, true, 8>
{
static constexpr bool is_supported = true;
static constexpr const char* type_name = "int64_t";
static bool meta_check(meta_check_context& ctx)
{
switch (ctx.current_meta().type())
{
case column_type::tinyint:
case column_type::smallint:
case column_type::year:
case column_type::mediumint:
case column_type::int_: return true;
case column_type::bigint: return !ctx.current_meta().is_unsigned();
default: return false;
}
}
static error_code parse(field_view input, T& output) { return parse_signed_int(input, output); }
};
template <class T>
struct int_traits<T, false, 8>
{
static constexpr bool is_supported = true;
static constexpr const char* type_name = "uint64_t";
static bool meta_check(meta_check_context& ctx)
{
switch (ctx.current_meta().type())
{
case column_type::tinyint:
case column_type::smallint:
case column_type::year:
case column_type::mediumint:
case column_type::int_:
case column_type::bigint: return ctx.current_meta().is_unsigned();
case column_type::bit: return true;
default: return false;
}
}
static error_code parse(field_view input, std::uint64_t& output)
{
return parse_unsigned_int(input, output);
}
};
// Traits
template <typename T, class EnableIf = void>
struct readable_field_traits
{
static constexpr bool is_supported = false;
};
template <>
struct readable_field_traits<char, void> : int_traits<char>
{
};
template <>
struct readable_field_traits<signed char, void> : int_traits<signed char>
{
};
template <>
struct readable_field_traits<unsigned char, void> : int_traits<unsigned char>
{
};
template <>
struct readable_field_traits<short, void> : int_traits<short>
{
};
template <>
struct readable_field_traits<unsigned short, void> : int_traits<unsigned short>
{
};
template <>
struct readable_field_traits<int, void> : int_traits<int>
{
};
template <>
struct readable_field_traits<unsigned int, void> : int_traits<unsigned int>
{
};
template <>
struct readable_field_traits<long, void> : int_traits<long>
{
};
template <>
struct readable_field_traits<unsigned long, void> : int_traits<unsigned long>
{
};
template <>
struct readable_field_traits<long long, void> : int_traits<long long>
{
};
template <>
struct readable_field_traits<unsigned long long, void> : int_traits<unsigned long long>
{
};
template <>
struct readable_field_traits<bool, void>
{
static constexpr bool is_supported = true;
static constexpr const char* type_name = "bool";
static bool meta_check(meta_check_context& ctx)
{
return ctx.current_meta().type() == column_type::tinyint && !ctx.current_meta().is_unsigned();
}
static error_code parse(field_view input, bool& output)
{
if (input.kind() != field_kind::int64)
{
return client_errc::static_row_parsing_error;
}
output = input.get_int64() != 0;
return error_code();
}
};
template <>
struct readable_field_traits<float, void>
{
static constexpr bool is_supported = true;
static constexpr const char* type_name = "float";
static bool meta_check(meta_check_context& ctx)
{
return ctx.current_meta().type() == column_type::float_;
}
static error_code parse(field_view input, float& output)
{
if (input.kind() != field_kind::float_)
{
return client_errc::static_row_parsing_error;
}
output = input.get_float();
return error_code();
}
};
template <>
struct readable_field_traits<double, void>
{
static constexpr bool is_supported = true;
static constexpr const char* type_name = "double";
static bool meta_check(meta_check_context& ctx)
{
switch (ctx.current_meta().type())
{
case column_type::float_:
case column_type::double_: return true;
default: return false;
}
}
static error_code parse(field_view input, double& output)
{
auto kind = input.kind();
if (kind == field_kind::float_)
{
output = input.get_float();
return error_code();
}
else if (kind == field_kind::double_)
{
output = input.get_double();
return error_code();
}
else
{
return client_errc::static_row_parsing_error;
}
}
};
template <class Allocator>
struct readable_field_traits<std::basic_string<char, std::char_traits<char>, Allocator>, void>
{
static constexpr bool is_supported = true;
static constexpr const char* type_name = "string";
static bool meta_check(meta_check_context& ctx)
{
switch (ctx.current_meta().type())
{
case column_type::decimal:
case column_type::char_:
case column_type::varchar:
case column_type::text:
case column_type::enum_:
case column_type::set:
case column_type::json: return true;
default: return false;
}
}
static error_code parse(
field_view input,
std::basic_string<char, std::char_traits<char>, Allocator>& output
)
{
if (input.kind() != field_kind::string)
{
return client_errc::static_row_parsing_error;
}
output = input.get_string();
return error_code();
}
};
template <class Allocator>
struct readable_field_traits<std::vector<unsigned char, Allocator>, void>
{
static constexpr bool is_supported = true;
static constexpr const char* type_name = "blob";
static bool meta_check(meta_check_context& ctx)
{
switch (ctx.current_meta().type())
{
case column_type::binary:
case column_type::varbinary:
case column_type::blob:
case column_type::geometry:
case column_type::unknown: return true;
default: return false;
}
}
static error_code parse(field_view input, std::vector<unsigned char, Allocator>& output)
{
if (input.kind() != field_kind::blob)
{
return client_errc::static_row_parsing_error;
}
auto view = input.get_blob();
output.assign(view.begin(), view.end());
return error_code();
}
};
template <>
struct readable_field_traits<date, void>
{
static constexpr bool is_supported = true;
static constexpr const char* type_name = "date";
static bool meta_check(meta_check_context& ctx) { return ctx.current_meta().type() == column_type::date; }
static error_code parse(field_view input, date& output)
{
if (input.kind() != field_kind::date)
{
return client_errc::static_row_parsing_error;
}
output = input.get_date();
return error_code();
}
};
template <>
struct readable_field_traits<datetime, void>
{
static constexpr bool is_supported = true;
static constexpr const char* type_name = "datetime";
static bool meta_check(meta_check_context& ctx)
{
switch (ctx.current_meta().type())
{
case column_type::datetime:
case column_type::timestamp: return true;
default: return false;
}
}
static error_code parse(field_view input, datetime& output)
{
if (input.kind() != field_kind::datetime)
{
return client_errc::static_row_parsing_error;
}
output = input.get_datetime();
return error_code();
}
};
template <>
struct readable_field_traits<time, void>
{
static constexpr bool is_supported = true;
static constexpr const char* type_name = "time";
static bool meta_check(meta_check_context& ctx) { return ctx.current_meta().type() == column_type::time; }
static error_code parse(field_view input, time& output)
{
if (input.kind() != field_kind::time)
{
return client_errc::static_row_parsing_error;
}
output = input.get_time();
return error_code();
}
};
// std::optional<T> and boost::optional<T>. To avoid dependencies,
// this is achieved through a "concept"
template <class T, class = void>
struct is_readable_optional : std::false_type
{
};
template <class T>
struct is_readable_optional<
T,
void_t<
typename std::enable_if<
std::is_same<decltype(std::declval<T&>().value()), typename T::value_type&>::value>::type,
decltype(std::declval<T&>().emplace()), // T should be default constructible
decltype(std::declval<T&>().reset())>> : std::true_type
{
};
template <class T>
struct readable_field_traits<
T,
typename std::enable_if<
is_readable_optional<T>::value && readable_field_traits<typename T::value_type>::is_supported>::type>
{
using value_type = typename T::value_type;
static constexpr bool is_supported = true;
static constexpr const char* type_name = readable_field_traits<value_type>::type_name;
static bool meta_check(meta_check_context& ctx)
{
ctx.set_nullability_checked();
return readable_field_traits<value_type>::meta_check(ctx);
}
static error_code parse(field_view input, T& output)
{
if (input.is_null())
{
output.reset();
return error_code();
}
else
{
output.emplace();
return readable_field_traits<value_type>::parse(input, output.value());
}
}
};
template <class T>
struct is_readable_field
{
static constexpr bool value = readable_field_traits<T>::is_supported;
};
template <typename ReadableField>
void meta_check_field_impl(meta_check_context& ctx)
{
using traits_t = readable_field_traits<ReadableField>;
// Verify that the field is present
if (ctx.is_current_field_absent())
{
ctx.add_field_absent_error();
return;
}
// Perform the check
bool ok = traits_t::meta_check(ctx);
if (!ok)
{
ctx.add_type_mismatch_error(traits_t::type_name);
}
// Check nullability
if (!ctx.nullability_checked() && !ctx.current_meta().is_not_null())
{
ctx.add_nullability_error();
}
}
template <typename ReadableField>
void meta_check_field(meta_check_context& ctx)
{
static_assert(is_readable_field<ReadableField>::value, "Should be a ReadableField");
meta_check_field_impl<ReadableField>(ctx);
ctx.advance();
}
struct meta_check_field_fn
{
meta_check_context ctx;
template <class T>
void operator()(T)
{
meta_check_field<typename T::type>(ctx);
}
};
template <typename ReadableFieldList>
error_code meta_check_field_type_list(
span<const std::size_t> field_map,
name_table_t name_table,
metadata_collection_view meta,
diagnostics& diag
)
{
meta_check_field_fn fn{meta_check_context(field_map, name_table, meta)};
boost::mp11::mp_for_each<ReadableFieldList>(fn);
return fn.ctx.check_errors(diag);
}
} // namespace detail
} // namespace mysql
} // namespace boost
#endif
+269
View File
@@ -0,0 +1,269 @@
//
// Copyright (c) 2019-2023 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOST_MYSQL_DETAIL_TYPING_ROW_TRAITS_HPP
#define BOOST_MYSQL_DETAIL_TYPING_ROW_TRAITS_HPP
#include <boost/mysql/detail/config.hpp>
#ifdef BOOST_MYSQL_CXX14
#include <boost/mysql/client_errc.hpp>
#include <boost/mysql/diagnostics.hpp>
#include <boost/mysql/error_code.hpp>
#include <boost/mysql/field_view.hpp>
#include <boost/mysql/metadata.hpp>
#include <boost/mysql/metadata_collection_view.hpp>
#include <boost/mysql/string_view.hpp>
#include <boost/mysql/detail/config.hpp>
#include <boost/mysql/detail/typing/meta_check_context.hpp>
#include <boost/mysql/detail/typing/pos_map.hpp>
#include <boost/mysql/detail/typing/readable_field_traits.hpp>
#include <boost/assert.hpp>
#include <boost/describe/members.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/utility.hpp>
#include <cstddef>
#include <tuple>
#include <type_traits>
#include <utility>
namespace boost {
namespace mysql {
namespace detail {
// Helpers to check that all the fields satisfy ReadableField
// and produce meaningful error messages, with the offending field type, at least
// Workaround clang 3.6 not liking generic lambdas in the below constexpr function
struct readable_field_checker
{
template <class TypeIdentity>
constexpr void operator()(TypeIdentity) const noexcept
{
using T = typename TypeIdentity::type;
static_assert(
is_readable_field<T>::value,
"You're trying to use an unsupported field type in a row type. Review your row type definitions."
);
}
};
template <class TypeList>
static constexpr bool check_readable_field() noexcept
{
mp11::mp_for_each<TypeList>(readable_field_checker{});
return true;
}
// Workaround std::array::data not being constexpr in C++14
template <class T, std::size_t N>
struct array_wrapper
{
T data_[N];
constexpr boost::span<const T> span() const noexcept { return boost::span<const T>(data_); }
};
template <class T>
struct array_wrapper<T, 0>
{
struct
{
} data_; // allow empty brace initialization
constexpr boost::span<const T> span() const noexcept { return boost::span<const T>(); }
};
// Workaround for char_traits::length not being constexpr in C++14
// Only used to retrieve Describe member name lengths
constexpr std::size_t get_length(const char* s) noexcept
{
const char* p = s;
while (*p)
++p;
return p - s;
}
// Helpers
class parse_functor
{
span<const std::size_t> pos_map_;
span<const field_view> fields_;
std::size_t index_{};
error_code ec_;
public:
parse_functor(span<const std::size_t> pos_map, span<const field_view> fields) noexcept
: pos_map_(pos_map), fields_(fields)
{
}
template <class ReadableField>
void operator()(ReadableField& output)
{
auto ec = readable_field_traits<ReadableField>::parse(
map_field_view(pos_map_, index_++, fields_),
output
);
if (!ec_)
ec_ = ec;
}
error_code error() const noexcept { return ec_; }
};
// Base template
template <class T, bool is_describe_struct = boost::describe::has_describe_members<T>::value>
class row_traits;
// Describe structs
template <class DescribeStruct>
using row_members = boost::describe::
describe_members<DescribeStruct, boost::describe::mod_public | boost::describe::mod_inherited>;
template <class MemberDescriptor>
constexpr string_view get_member_name(MemberDescriptor d) noexcept
{
return string_view(d.name, get_length(d.name));
}
template <template <class...> class ListType, class... MemberDescriptor>
constexpr array_wrapper<string_view, sizeof...(MemberDescriptor)> get_describe_names(ListType<
MemberDescriptor...>)
{
return {{get_member_name(MemberDescriptor())...}};
}
template <class DescribeStruct>
constexpr auto describe_names_storage = get_describe_names(row_members<DescribeStruct>{});
template <class DescribeStruct>
class row_traits<DescribeStruct, true>
{
using members = row_members<DescribeStruct>;
template <class D>
struct descriptor_to_type
{
using helper = decltype(std::declval<DescribeStruct>().*std::declval<D>().pointer);
using type = typename std::remove_reference<helper>::type;
};
using member_types = mp11::mp_transform<descriptor_to_type, members>;
static_assert(check_readable_field<member_types>(), "");
public:
using types = member_types;
static constexpr std::size_t size() noexcept { return boost::mp11::mp_size<members>::value; }
static constexpr name_table_t name_table() noexcept
{
return describe_names_storage<DescribeStruct>.span();
}
static void parse(parse_functor& parser, DescribeStruct& to)
{
boost::mp11::mp_for_each<members>([&](auto D) { parser(to.*D.pointer); });
}
};
// Tuples
template <class T>
struct is_tuple : std::false_type
{
};
template <class... T>
struct is_tuple<std::tuple<T...>> : std::true_type
{
};
template <class... ReadableField>
class row_traits<std::tuple<ReadableField...>, false>
{
using tuple_type = std::tuple<ReadableField...>;
using field_types = boost::mp11::mp_list<boost::mp11::mp_identity<ReadableField>...>;
static_assert(check_readable_field<field_types>(), "");
public:
using types = field_types;
static constexpr std::size_t size() noexcept { return std::tuple_size<tuple_type>::value; }
static constexpr name_table_t name_table() noexcept { return name_table_t(); }
static void parse(parse_functor& parser, tuple_type& to) { boost::mp11::tuple_for_each(to, parser); }
};
// 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),
// and not individual fields. These are static_assert-ed in individual row_traits. This gives us an error
// message that contains the offending types, at least.
template <class T>
struct is_static_row
{
static constexpr bool value = is_tuple<T>::value || describe::has_describe_members<T>::value;
};
#ifdef BOOST_MYSQL_HAS_CONCEPTS
template <class T>
concept static_row = is_static_row<T>::value;
#define BOOST_MYSQL_STATIC_ROW ::boost::mysql::detail::static_row
#else
#define BOOST_MYSQL_STATIC_ROW class
#endif
// External interface
template <BOOST_MYSQL_STATIC_ROW StaticRow>
constexpr std::size_t get_row_size()
{
return row_traits<StaticRow>::size();
}
template <BOOST_MYSQL_STATIC_ROW StaticRow>
constexpr name_table_t get_row_name_table()
{
return row_traits<StaticRow>::name_table();
}
template <BOOST_MYSQL_STATIC_ROW StaticRow>
error_code meta_check(span<const std::size_t> pos_map, metadata_collection_view meta, diagnostics& diag)
{
using fields = typename row_traits<StaticRow>::types;
BOOST_ASSERT(pos_map.size() == get_row_size<StaticRow>());
return meta_check_field_type_list<fields>(pos_map, get_row_name_table<StaticRow>(), meta, diag);
}
template <BOOST_MYSQL_STATIC_ROW StaticRow>
error_code parse(span<const std::size_t> pos_map, span<const field_view> from, StaticRow& to)
{
BOOST_ASSERT(pos_map.size() == get_row_size<StaticRow>());
BOOST_ASSERT(from.size() >= get_row_size<StaticRow>());
parse_functor ctx(pos_map, from);
row_traits<StaticRow>::parse(ctx, to);
return ctx.error();
}
using meta_check_fn_t =
error_code (*)(span<const std::size_t> field_map, metadata_collection_view meta, diagnostics& diag);
// For multi-resultset - helper
template <class... StaticRow>
constexpr std::size_t max_num_columns = (std::max)({get_row_size<StaticRow>()...});
} // namespace detail
} // namespace mysql
} // namespace boost
#endif // BOOST_MYSQL_CXX14
#endif