format_sql.hpp 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. //
  2. // Copyright (c) 2019-2024 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 BOOST_MYSQL_DETAIL_FORMAT_SQL_HPP
  8. #define BOOST_MYSQL_DETAIL_FORMAT_SQL_HPP
  9. #include <boost/mysql/field_view.hpp>
  10. #include <boost/mysql/string_view.hpp>
  11. #include <boost/mysql/detail/writable_field_traits.hpp>
  12. #include <iterator>
  13. #include <type_traits>
  14. #include <utility>
  15. #ifdef BOOST_MYSQL_HAS_CONCEPTS
  16. #include <concepts>
  17. #endif
  18. namespace boost {
  19. namespace mysql {
  20. // Forward decls
  21. template <class T>
  22. struct formatter;
  23. class format_context_base;
  24. class formattable_ref;
  25. namespace detail {
  26. class format_state;
  27. struct formatter_is_unspecialized
  28. {
  29. };
  30. template <class T>
  31. constexpr bool has_specialized_formatter()
  32. {
  33. return !std::is_base_of<formatter_is_unspecialized, formatter<typename std::decay<T>::type>>::value;
  34. }
  35. template <class T>
  36. struct is_writable_field_ref : is_writable_field<typename std::decay<T>::type>
  37. {
  38. };
  39. template <class T>
  40. struct is_formattable_ref : std::is_same<typename std::decay<T>::type, formattable_ref>
  41. {
  42. };
  43. // Is T suitable for being the element type of a formattable range?
  44. template <class T>
  45. constexpr bool is_formattable_range_elm_type()
  46. {
  47. return is_writable_field_ref<T>::value || has_specialized_formatter<T>() || is_formattable_ref<T>::value;
  48. }
  49. template <class T, class = void>
  50. struct is_formattable_range : std::false_type
  51. {
  52. };
  53. // Note: T might be a reference.
  54. // Using T& + reference collapsing gets the right semantics for non-const ranges
  55. template <class T>
  56. struct is_formattable_range<
  57. T,
  58. typename std::enable_if<
  59. // std::begin and std::end can be called on it, and we can compare values
  60. std::is_convertible<decltype(std::begin(std::declval<T&>()) != std::end(std::declval<T&>())), bool>::
  61. value &&
  62. // value_type is either a writable field or a type with a specialized formatter.
  63. // We don't support sequences of sequences out of the box (no known use case)
  64. is_formattable_range_elm_type<decltype(*std::begin(std::declval<T&>()))>()
  65. // end of conditions
  66. >::type> : std::true_type
  67. {
  68. };
  69. template <class T>
  70. constexpr bool is_formattable_type()
  71. {
  72. return is_formattable_range_elm_type<T>() || is_formattable_range<T>::value;
  73. }
  74. #ifdef BOOST_MYSQL_HAS_CONCEPTS
  75. // If you're getting an error referencing this concept,
  76. // it means that you are attempting to format a type that doesn't support it.
  77. template <class T>
  78. concept formattable =
  79. // This covers basic types and optionals
  80. is_writable_field_ref<T>::value ||
  81. // This covers custom types that specialized boost::mysql::formatter
  82. has_specialized_formatter<T>() ||
  83. // This covers ranges of formattable types
  84. is_formattable_range<T>::value ||
  85. // This covers passing formattable_ref as a format argument
  86. is_formattable_ref<T>::value;
  87. template <class FormatFn, class Range>
  88. concept format_fn_for_range = requires(const FormatFn& format_fn, Range&& range, format_context_base& ctx) {
  89. { std::begin(range) != std::end(range) } -> std::convertible_to<bool>;
  90. format_fn(*std::begin(range), ctx);
  91. std::end(range);
  92. };
  93. #define BOOST_MYSQL_FORMATTABLE ::boost::mysql::detail::formattable
  94. #else
  95. #define BOOST_MYSQL_FORMATTABLE class
  96. #endif
  97. // A type-erased argument passed to format. Built-in types are passed
  98. // directly in the struct (as a field_view), instead of by pointer,
  99. // to reduce the number of do_format instantiations
  100. struct formattable_ref_impl
  101. {
  102. enum class type_t
  103. {
  104. field,
  105. field_with_specs,
  106. fn_and_ptr
  107. };
  108. struct fn_and_ptr
  109. {
  110. const void* obj;
  111. bool (*format_fn)(const void*, const char*, const char*, format_context_base&);
  112. };
  113. union data_t
  114. {
  115. field_view fv;
  116. fn_and_ptr custom;
  117. data_t(field_view fv) noexcept : fv(fv) {}
  118. data_t(fn_and_ptr v) noexcept : custom(v) {}
  119. };
  120. type_t type;
  121. data_t data;
  122. };
  123. // Create a type-erased formattable_ref_impl from a formattable value
  124. template <class T>
  125. formattable_ref_impl make_formattable_ref(T&& v);
  126. } // namespace detail
  127. } // namespace mysql
  128. } // namespace boost
  129. #endif