6#include "Description.hpp"
8#include <reflection-cpp/reflection.hpp>
17#include <source_location>
20#include <system_error>
22#include <unordered_map>
27#if !(defined(__cpp_lib_to_chars) && __cpp_lib_to_chars >= 201611L)
29 #if defined(__APPLE__)
38#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
39 #include <experimental/meta>
48 template <
typename T,
typename... Comps>
49 concept OneOf = (std::same_as<T, Comps> || ...);
52 constexpr auto AlwaysFalse = std::false_type::value;
54 constexpr auto Finally(
auto&& cleanupRoutine)
noexcept
59 std::remove_cvref_t<
decltype(cleanupRoutine)> cleanup;
65 return Finally { std::forward<decltype(cleanupRoutine)>(cleanupRoutine) };
80 requires std::is_floating_point_v<T>
81 [[nodiscard]]
inline std::optional<T> ParseFloat(
char const* first,
char const* last)
noexcept
85#if defined(__cpp_lib_to_chars) && __cpp_lib_to_chars >= 201611L
89 auto const [ptr, ec] = std::from_chars(first, last, value);
90 if (ec != std::errc {} || ptr != last)
98 static ::locale_t
const cLocale = ::newlocale(LC_NUMERIC_MASK,
"C",
static_cast<::locale_t
>(
nullptr));
99 auto const length =
static_cast<std::size_t
>(last - first);
100 std::array<char, 64> stackBuffer {};
101 std::string heapBuffer;
102 char const* text =
nullptr;
103 if (length < stackBuffer.size())
105 std::ranges::copy(first, last, stackBuffer.begin());
106 stackBuffer[length] =
'\0';
107 text = stackBuffer.data();
111 heapBuffer.assign(first, last);
112 text = heapBuffer.c_str();
115 char* parseEnd =
nullptr;
118 if constexpr (std::is_same_v<T, float>)
119 value = ::strtof_l(text, &parseEnd, cLocale);
120 else if constexpr (std::is_same_v<T, long double>)
121 value = ::strtold_l(text, &parseEnd, cLocale);
123 value =
static_cast<T
>(::strtod_l(text, &parseEnd, cLocale));
125 if (errno == ERANGE || parseEnd != text + length)
134 template <
template <
typename...>
class T,
typename U>
135 struct is_specialization_of: std::false_type
139 template <
template <
typename...>
class T,
typename... Us>
140 struct is_specialization_of<T, T<Us...>>: std::true_type
144 template <
typename T>
145 struct MemberClassTypeHelper;
147 template <
typename M,
typename T>
148 struct MemberClassTypeHelper<M T::*>
150 using type = std::remove_cvref_t<T>;
153 template <
typename Record>
154 struct RecordTableNameImpl
156 static constexpr std::string_view Value = []() {
157 if constexpr (
requires { Record::TableName; })
158 return Record::TableName;
161#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
162 return std::meta::identifier_of(^^Record);
164 auto const typeName = Reflection::TypeNameOf<Record>;
165 if (
auto const i = typeName.rfind(
':'); i != std::string_view::npos)
166 return typeName.substr(i + 1);
176 template <
typename First,
typename Second>
177 struct RecordTableNameImpl<std::tuple<First, Second>>
179 static constexpr std::string_view Value = []() {
180 if constexpr (
requires { First::TableName; })
181 return First::TableName;
184#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
185 return std::meta::identifier_of(^^First);
187 auto const typeName = Reflection::TypeNameOf<First>;
188 if (
auto const i = typeName.rfind(
':'); i != std::string_view::npos)
189 return typeName.substr(i + 1);
196 template <
typename FieldType>
197 constexpr auto ColumnNameOverride = []()
consteval {
198 if constexpr (
requires { FieldType::ColumnNameOverride; })
199 return FieldType::ColumnNameOverride;
201 return std::string_view {};
203#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
204 template <auto reflection>
205 struct FieldNameOfImpl
207 using R =
typename[:std::meta::type_of(reflection):];
208 static constexpr std::string_view value = []()
constexpr -> std::string_view {
209 if constexpr (
requires { R::ColumnNameOverride; })
211 if constexpr (!R::ColumnNameOverride.empty())
212 return R::ColumnNameOverride;
214 return std::meta::identifier_of(reflection);
218 template <
typename ReferencedFieldType, auto F>
219 struct FieldNameOfImpl;
221 template <
typename T, auto F,
typename R>
222 struct FieldNameOfImpl<R T::*, F>
224 static constexpr std::string_view value = []()
constexpr -> std::string_view {
225 if constexpr (
requires { R::ColumnNameOverride; })
227 if constexpr (!R::ColumnNameOverride.empty())
228 return R::ColumnNameOverride;
230 return Reflection::NameOf<F>;
235 template <std::
size_t I,
typename Record>
240 if constexpr (HasDescription<Record>)
242 return Description<Record>::FieldNames[I];
246 using FieldType = Reflection::MemberTypeOf<I, Record>;
248 if constexpr (!std::string_view(ColumnNameOverride<FieldType>).empty())
250 return FieldType::ColumnNameOverride;
252 return Reflection::MemberNameOf<I, Record>;
260template <std::
size_t I,
typename Record>
261constexpr inline std::string_view
FieldNameAt = detail::FieldNameAt<I, Record>();
266template <
typename Record>
267constexpr std::string_view
RecordTableName = detail::RecordTableNameImpl<Record>::Value;
269template <
template <
typename...>
class S,
class T>
270concept IsSpecializationOf = detail::is_specialization_of<S, T>::value;
272#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
277template <std::meta::info ReflectionOfField>
278constexpr inline std::string_view FieldNameOf = detail::FieldNameOfImpl<ReflectionOfField>::value;
280template <auto Member>
281using MemberClassType =
typename[:std::meta::parent_of(Member):];
283template <auto Member>
284constexpr size_t MemberIndexOf = []()
consteval ->
size_t {
286 auto members = nonstatic_data_members_of(parent_of(Member), std::meta::access_context::current());
287 if (
auto it = std::ranges::find(members, Member); it != members.end())
289 index = std::distance(members.begin(), it);
290 return static_cast<size_t>(index);
299template <auto ReferencedField>
300constexpr inline std::string_view FieldNameOf = detail::FieldNameOfImpl<
decltype(ReferencedField), ReferencedField>::value;
302template <auto Member>
303constexpr size_t MemberIndexOf = Reflection::MemberIndexOf<Member>;
306using MemberClassType = detail::MemberClassTypeHelper<T>::type;
342template <auto ReferencedField>
344#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
347 .tableName =
RecordTableName<MemberClassType<
decltype(ReferencedField)>>,
349 .columnName = FieldNameOf<ReferencedField>,
354 template <auto ReferencedField>
355 struct FullyQualifiedQuotedNameOfImpl
357#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
358 static constexpr auto ClassName =
RecordTableName<
typename[:std::meta::parent_of(ReferencedField):]>;
360 static constexpr auto ClassName =
RecordTableName<MemberClassType<
decltype(ReferencedField)>>;
362 static constexpr auto FieldName = FieldNameOf<ReferencedField>;
363 static constexpr auto StorageSize = ClassName.size() + FieldName.size() + 6;
366 static constexpr auto Storage = []()
constexpr -> std::array<char, StorageSize> {
368 auto storage = std::array<char, StorageSize> {};
369 std::ranges::copy(
"\"", storage.begin());
370 std::ranges::copy(ClassName, storage.begin() + 1);
371 std::ranges::copy(
"\".\"", storage.begin() + 1 + ClassName.size());
372 std::ranges::copy(FieldName, storage.begin() + 1 + ClassName.size() + 3);
373 std::ranges::copy(
"\"", storage.begin() + 1 + ClassName.size() + 3 + FieldName.size());
374 storage.back() =
'\0';
378 static constexpr auto value = std::string_view(Storage.data(), Storage.size() - 1);
381 template <auto ReferencedField>
382 constexpr inline auto FullyQualifiedQuotedNameOf = FullyQualifiedQuotedNameOfImpl<ReferencedField>::value;
384 template <
auto... ReferencedFields>
385 struct FullyQualifiedNamesOfImpl
387 static constexpr auto StorageSize =
388 1 + (2 * (
sizeof...(ReferencedFields) - 1)) + (0 + ... + FullyQualifiedQuotedNameOf<ReferencedFields>.size());
390 static constexpr std::array<char, StorageSize> Storage = []()
consteval {
391 auto result = std::array<char, StorageSize> {};
397 constexpr auto Delimiter = std::string_view(
", ");
398 std::ranges::copy(Delimiter, result.begin() + offset);
399 offset += Delimiter.size();
401 std::ranges::copy(FullyQualifiedQuotedNameOf<ReferencedFields>, result.begin() + offset);
402 offset += FullyQualifiedQuotedNameOf<ReferencedFields>.size();
405 result.back() =
'\0';
409 static constexpr auto value = std::string_view(Storage.data(), Storage.size() - 1);
414 template <
auto... ReferencedFields>
415 constexpr inline auto FullyQualifiedNamesOf = FullyQualifiedNamesOfImpl<ReferencedFields...>::value;
419LIGHTWEIGHT_API
void LogIfFailed(SQLHSTMT hStmt, SQLRETURN error, std::source_location sourceLocation);
421LIGHTWEIGHT_API
void RequireSuccess(SQLHSTMT hStmt,
423 std::source_location sourceLocation = std::source_location::current());
426enum class FormatType : uint8_t
439LIGHTWEIGHT_API std::string FormatName(std::string
const& name, FormatType formatType);
442LIGHTWEIGHT_API std::string FormatName(std::string_view name, FormatType formatType);
449 [[nodiscard]] LIGHTWEIGHT_API
bool IsColliding(std::string
const& name)
const noexcept;
452 [[nodiscard]] LIGHTWEIGHT_API std::optional<std::string>
TryDeclareName(std::string name);
455 [[nodiscard]] LIGHTWEIGHT_API std::string
DeclareName(std::string name);
458 std::unordered_map<std::string, size_t> _collisionMap;
Maintains collisions to create unique names.
LIGHTWEIGHT_API std::string DeclareName(std::string name)
Creates a name that is definitely not colliding.
LIGHTWEIGHT_API bool IsColliding(std::string const &name) const noexcept
Tests if the given name is already registered.
LIGHTWEIGHT_API std::optional< std::string > TryDeclareName(std::string name)
Tries to declare a name and returns it, otherwise returns std::nullopt.
constexpr std::string_view RecordTableName
Holds the SQL tabl ename for the given record type.
constexpr std::string_view FieldNameAt
Returns the SQL field name of the given field index in the record.
constexpr auto FullyQualifiedNameOf
Holds the fully qualified column reference (table + column) for the given field.
SqlQualifiedTableColumnName represents a column name qualified with a table name.
std::string_view tableName
The table name.
constexpr std::weak_ordering operator<=>(SqlQualifiedTableColumnName const &) const noexcept=default
Three-way comparison operator.
std::string_view columnName
The column name.