165 lines
4.2 KiB
C++
165 lines
4.2 KiB
C++
//
|
|
// Copyright (c) 2019-2024 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_FORMAT_SQL_HPP
|
|
#define BOOST_MYSQL_DETAIL_FORMAT_SQL_HPP
|
|
|
|
#include <boost/mysql/field_view.hpp>
|
|
#include <boost/mysql/string_view.hpp>
|
|
|
|
#include <boost/mysql/detail/writable_field_traits.hpp>
|
|
|
|
#include <iterator>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
#ifdef BOOST_MYSQL_HAS_CONCEPTS
|
|
#include <concepts>
|
|
#endif
|
|
|
|
namespace boost {
|
|
namespace mysql {
|
|
|
|
// Forward decls
|
|
template <class T>
|
|
struct formatter;
|
|
|
|
class format_context_base;
|
|
class formattable_ref;
|
|
|
|
namespace detail {
|
|
|
|
class format_state;
|
|
|
|
struct formatter_is_unspecialized
|
|
{
|
|
};
|
|
|
|
template <class T>
|
|
constexpr bool has_specialized_formatter()
|
|
{
|
|
return !std::is_base_of<formatter_is_unspecialized, formatter<typename std::decay<T>::type>>::value;
|
|
}
|
|
|
|
template <class T>
|
|
struct is_writable_field_ref : is_writable_field<typename std::decay<T>::type>
|
|
{
|
|
};
|
|
|
|
template <class T>
|
|
struct is_formattable_ref : std::is_same<typename std::decay<T>::type, formattable_ref>
|
|
{
|
|
};
|
|
|
|
// Is T suitable for being the element type of a formattable range?
|
|
template <class T>
|
|
constexpr bool is_formattable_range_elm_type()
|
|
{
|
|
return is_writable_field_ref<T>::value || has_specialized_formatter<T>() || is_formattable_ref<T>::value;
|
|
}
|
|
|
|
template <class T, class = void>
|
|
struct is_formattable_range : std::false_type
|
|
{
|
|
};
|
|
|
|
// Note: T might be a reference.
|
|
// Using T& + reference collapsing gets the right semantics for non-const ranges
|
|
template <class T>
|
|
struct is_formattable_range<
|
|
T,
|
|
typename std::enable_if<
|
|
// std::begin and std::end can be called on it, and we can compare values
|
|
std::is_convertible<decltype(std::begin(std::declval<T&>()) != std::end(std::declval<T&>())), bool>::
|
|
value &&
|
|
|
|
// value_type is either a writable field or a type with a specialized formatter.
|
|
// We don't support sequences of sequences out of the box (no known use case)
|
|
is_formattable_range_elm_type<decltype(*std::begin(std::declval<T&>()))>()
|
|
|
|
// end of conditions
|
|
>::type> : std::true_type
|
|
{
|
|
};
|
|
|
|
template <class T>
|
|
constexpr bool is_formattable_type()
|
|
{
|
|
return is_formattable_range_elm_type<T>() || is_formattable_range<T>::value;
|
|
}
|
|
|
|
#ifdef BOOST_MYSQL_HAS_CONCEPTS
|
|
|
|
// If you're getting an error referencing this concept,
|
|
// it means that you are attempting to format a type that doesn't support it.
|
|
template <class T>
|
|
concept formattable =
|
|
// This covers basic types and optionals
|
|
is_writable_field_ref<T>::value ||
|
|
// This covers custom types that specialized boost::mysql::formatter
|
|
has_specialized_formatter<T>() ||
|
|
// This covers ranges of formattable types
|
|
is_formattable_range<T>::value ||
|
|
// This covers passing formattable_ref as a format argument
|
|
is_formattable_ref<T>::value;
|
|
|
|
template <class FormatFn, class Range>
|
|
concept format_fn_for_range = requires(const FormatFn& format_fn, Range&& range, format_context_base& ctx) {
|
|
{ std::begin(range) != std::end(range) } -> std::convertible_to<bool>;
|
|
format_fn(*std::begin(range), ctx);
|
|
std::end(range);
|
|
};
|
|
|
|
#define BOOST_MYSQL_FORMATTABLE ::boost::mysql::detail::formattable
|
|
|
|
#else
|
|
|
|
#define BOOST_MYSQL_FORMATTABLE class
|
|
|
|
#endif
|
|
|
|
// A type-erased argument passed to format. Built-in types are passed
|
|
// directly in the struct (as a field_view), instead of by pointer,
|
|
// to reduce the number of do_format instantiations
|
|
struct formattable_ref_impl
|
|
{
|
|
enum class type_t
|
|
{
|
|
field,
|
|
field_with_specs,
|
|
fn_and_ptr
|
|
};
|
|
|
|
struct fn_and_ptr
|
|
{
|
|
const void* obj;
|
|
bool (*format_fn)(const void*, const char*, const char*, format_context_base&);
|
|
};
|
|
|
|
union data_t
|
|
{
|
|
field_view fv;
|
|
fn_and_ptr custom;
|
|
|
|
data_t(field_view fv) noexcept : fv(fv) {}
|
|
data_t(fn_and_ptr v) noexcept : custom(v) {}
|
|
};
|
|
|
|
type_t type;
|
|
data_t data;
|
|
};
|
|
|
|
// Create a type-erased formattable_ref_impl from a formattable value
|
|
template <class T>
|
|
formattable_ref_impl make_formattable_ref(T&& v);
|
|
|
|
} // namespace detail
|
|
} // namespace mysql
|
|
} // namespace boost
|
|
|
|
#endif
|