Skip to content

Commit 2cd53b3

Browse files
author
Pierre-Luc Gagné
committed
Remove code duplication to retrieve name from type using compiler function signature
1 parent 83c1dd6 commit 2cd53b3

3 files changed

Lines changed: 114 additions & 136 deletions

File tree

lib/include/ds_mysql/column_field.hpp

Lines changed: 1 addition & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <type_traits>
99
#include <utility>
1010

11+
#include "ds_mysql/name_reflection.hpp"
1112
#include "ds_mysql/sql_temporal.hpp"
1213
#include "ds_mysql/varchar_field.hpp"
1314

@@ -24,99 +25,6 @@ struct column_field_tag {};
2425
template <typename Tag, typename T>
2526
struct column_field;
2627

27-
// ===================================================================
28-
// detail::tag_to_column_name<Tag>
29-
// ===================================================================
30-
31-
namespace detail {
32-
33-
template <typename>
34-
inline constexpr bool dependent_false_v = false;
35-
36-
/**
37-
* min_find_pos — returns the earlier of two string_view::find() positions.
38-
* Treats npos as "not found": if only one is valid it is returned; if neither
39-
* is valid npos is returned.
40-
*/
41-
[[nodiscard]] constexpr std::size_t min_find_pos(std::size_t p1, std::size_t p2) noexcept {
42-
constexpr auto npos = std::string_view::npos;
43-
return (p1 != npos && p2 != npos) ? (p1 < p2 ? p1 : p2) : (p1 != npos ? p1 : p2);
44-
}
45-
46-
/**
47-
* strip_type_qualifiers — given a raw type-name substring from a compiler
48-
* function-signature intrinsic, returns the final unqualified identifier:
49-
*
50-
* - (MSVC only) strips a leading 'struct ' or 'class ' keyword
51-
* - strips any leading namespace/scope qualifier up to the last '::'
52-
*
53-
* Example inputs → outputs:
54-
* "struct `anonymous-namespace'::ticker_tag" → "ticker_tag" (MSVC)
55-
* "(anonymous namespace)::ticker_tag" → "ticker_tag" (GCC/Clang)
56-
* "child_table_cascade" → "child_table_cascade"
57-
*/
58-
[[nodiscard]] constexpr std::string_view strip_type_qualifiers(std::string_view name) noexcept {
59-
#if defined(_MSC_VER)
60-
if (name.starts_with("struct "))
61-
name = name.substr(7);
62-
else if (name.starts_with("class "))
63-
name = name.substr(6);
64-
#endif
65-
auto const scope = name.rfind("::");
66-
if (scope != std::string_view::npos && scope + 2 < name.size())
67-
name = name.substr(scope + 2);
68-
return name;
69-
}
70-
71-
/**
72-
* tag_to_column_name<Tag> — derive a SQL column name from a tag type at compile time.
73-
*
74-
* Extracts the unqualified type name of Tag and strips a trailing "_tag" suffix:
75-
*
76-
* tag_to_column_name<id_tag>() → "id"
77-
* tag_to_column_name<ticker_tag>() → "ticker"
78-
* tag_to_column_name<my_col>() → "my_col"
79-
*/
80-
template <typename Tag>
81-
consteval std::string_view tag_to_column_name() noexcept {
82-
#if defined(__clang__) || defined(__GNUC__)
83-
constexpr std::string_view sig = __PRETTY_FUNCTION__;
84-
constexpr std::string_view key = "Tag = ";
85-
#elif defined(_MSC_VER)
86-
constexpr std::string_view sig = __FUNCSIG__;
87-
constexpr std::string_view key = "tag_to_column_name<";
88-
#else
89-
static_assert(dependent_false_v<Tag>, "tag_to_column_name requires Clang, GCC, or MSVC");
90-
return {};
91-
#endif
92-
constexpr auto npos = std::string_view::npos;
93-
constexpr auto key_pos = sig.find(key);
94-
if constexpr (key_pos == npos) {
95-
static_assert(dependent_false_v<Tag>, "Failed to parse Tag name from compiler function signature");
96-
return {};
97-
}
98-
constexpr auto start = key_pos + key.size();
99-
#if defined(_MSC_VER)
100-
// MSVC __FUNCSIG__: "...tag_to_column_name<struct `anonymous-namespace'::tag>(void)..."
101-
// The tag type ends at '>' (template arg close); '(' is a secondary fallback.
102-
constexpr auto end = min_find_pos(sig.find('>', start), sig.find('(', start));
103-
#else
104-
constexpr auto end = min_find_pos(sig.find(';', start), sig.find(']', start));
105-
#endif
106-
if constexpr (end == npos || end <= start) {
107-
static_assert(dependent_false_v<Tag>, "Failed to locate end of Tag name in compiler function signature");
108-
return {};
109-
}
110-
auto name = strip_type_qualifiers(sig.substr(start, end - start));
111-
constexpr std::string_view suffix = "_tag";
112-
if (name.size() > suffix.size() && name.substr(name.size() - suffix.size()) == suffix) {
113-
return name.substr(0, name.size() - suffix.size());
114-
}
115-
return name;
116-
}
117-
118-
} // namespace detail
119-
12028
// ===================================================================
12129
// column_field_detail — internal base specializations
12230
//
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
#pragma once
2+
3+
#include <string_view>
4+
5+
namespace ds_mysql {
6+
7+
namespace detail {
8+
9+
/**
10+
* min_find_pos — returns the earlier of two string_view::find() positions.
11+
* Treats npos as "not found": if only one is valid it is returned; if neither
12+
* is valid npos is returned.
13+
*/
14+
[[nodiscard]] constexpr std::size_t min_find_pos(std::size_t p1, std::size_t p2) noexcept {
15+
constexpr auto npos = std::string_view::npos;
16+
return (p1 != npos && p2 != npos) ? (p1 < p2 ? p1 : p2) : (p1 != npos ? p1 : p2);
17+
}
18+
19+
/**
20+
* strip_type_qualifiers — given a raw type-name substring from a compiler
21+
* function-signature intrinsic, returns the final unqualified identifier:
22+
*
23+
* - (MSVC only) strips a leading 'struct ' or 'class ' keyword
24+
* - strips any leading namespace/scope qualifier up to the last '::'
25+
*
26+
* Example inputs → outputs:
27+
* "struct `anonymous-namespace'::ticker_tag" → "ticker_tag" (MSVC)
28+
* "(anonymous namespace)::ticker_tag" → "ticker_tag" (GCC/Clang)
29+
* "child_table_cascade" → "child_table_cascade"
30+
*/
31+
[[nodiscard]] constexpr std::string_view strip_type_qualifiers(std::string_view name) noexcept {
32+
#if defined(_MSC_VER)
33+
if (name.starts_with("struct "))
34+
name = name.substr(7);
35+
else if (name.starts_with("class "))
36+
name = name.substr(6);
37+
#endif
38+
39+
auto const scope = name.rfind("::");
40+
if (scope != std::string_view::npos && scope + 2 < name.size())
41+
name = name.substr(scope + 2);
42+
return name;
43+
}
44+
45+
/**
46+
* Extract struct type name from compiler intrinsics (__PRETTY_FUNCTION__ or __FUNCSIG__)
47+
*/
48+
template <typename T>
49+
consteval std::string_view extract_type_name() {
50+
#if defined(__clang__) || defined(__GNUC__)
51+
constexpr std::string_view signature = __PRETTY_FUNCTION__;
52+
constexpr std::string_view marker = "T = ";
53+
#elif defined(_MSC_VER)
54+
constexpr std::string_view signature = __FUNCSIG__;
55+
constexpr std::string_view marker = "extract_type_name<";
56+
#else
57+
#error ("extract_type_name requires Clang, GCC, or MSVC")
58+
#endif
59+
60+
constexpr auto npos = std::string_view::npos;
61+
constexpr auto marker_pos = signature.find(marker);
62+
if constexpr (marker_pos == npos) {
63+
static_assert(false, "Failed to locate marker in compiler function signature");
64+
return {};
65+
}
66+
67+
constexpr auto start = marker_pos + marker.size();
68+
69+
#if defined(__clang__) || defined(__GNUC__)
70+
// GCC/Clang format: [with T = TYPE] or [with T = TYPE; ALIAS = EXPANSION, ...]
71+
constexpr auto end = min_find_pos(signature.find(';', start), signature.find(']', start));
72+
#elif defined(_MSC_VER)
73+
// MSVC format: "...extract_type_name<TYPE>(void)..."
74+
// The type ends at '>' (template arg close) or ',' (comma in multi-param templates).
75+
constexpr auto end = min_find_pos(signature.find('>', start), signature.find(',', start));
76+
#else
77+
#error ("extract_type_name requires Clang, GCC, or MSVC")
78+
#endif
79+
80+
if constexpr (end == npos || end <= start) {
81+
static_assert(false, "Failed to locate end of type name in compiler function signature");
82+
return {};
83+
}
84+
85+
return strip_type_qualifiers(signature.substr(start, end - start));
86+
}
87+
88+
consteval std::string_view remove_suffix(std::string_view name, std::string_view suffix) noexcept {
89+
if (name.size() >= suffix.size() && name.substr(name.size() - suffix.size()) == suffix)
90+
return name.substr(0, name.size() - suffix.size());
91+
else {
92+
return name;
93+
}
94+
}
95+
96+
/**
97+
* tag_to_column_name<Tag> — derive a SQL column name from a tag type at compile time.
98+
*
99+
* Extracts the unqualified type name of Tag and strips a trailing "_tag" suffix:
100+
*
101+
* tag_to_column_name<id_tag>() → "id"
102+
* tag_to_column_name<ticker_tag>() → "ticker"
103+
* tag_to_column_name<my_col>() → "my_col"
104+
*/
105+
template <typename Tag>
106+
consteval std::string_view tag_to_column_name() noexcept {
107+
return remove_suffix(extract_type_name<Tag>(), "_tag");
108+
}
109+
110+
} // namespace detail
111+
112+
} // namespace ds_mysql

lib/include/ds_mysql/schema_generator.hpp

Lines changed: 1 addition & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <type_traits>
1212

1313
#include "ds_mysql/column_field.hpp"
14+
#include "ds_mysql/name_reflection.hpp"
1415
#include "ds_mysql/sql_identifiers.hpp"
1516
#include "ds_mysql/sql_temporal.hpp"
1617
#include "ds_mysql/text_field.hpp"
@@ -168,49 +169,6 @@ namespace sql_type_format {
168169
// Table Schema - Table Name via Reflection
169170
// ===================================================================
170171

171-
namespace detail {
172-
173-
/**
174-
* Extract struct type name from compiler intrinsics (__PRETTY_FUNCTION__ or __FUNCSIG__)
175-
*/
176-
template <typename T>
177-
consteval std::string_view extract_type_name() {
178-
#if defined(__clang__) || defined(__GNUC__)
179-
constexpr std::string_view signature = __PRETTY_FUNCTION__;
180-
constexpr std::string_view marker = "T = ";
181-
#elif defined(_MSC_VER)
182-
constexpr std::string_view signature = __FUNCSIG__;
183-
constexpr std::string_view marker = "extract_type_name<";
184-
#else
185-
return "unknown";
186-
#endif
187-
188-
constexpr auto npos = std::string_view::npos;
189-
constexpr auto marker_pos = signature.find(marker);
190-
if constexpr (marker_pos == npos) {
191-
return "unknown";
192-
}
193-
194-
constexpr auto start = marker_pos + marker.size();
195-
196-
#if defined(__clang__) || defined(__GNUC__)
197-
// GCC/Clang format: [with T = TYPE] or [with T = TYPE; ALIAS = EXPANSION, ...]
198-
constexpr auto end = min_find_pos(signature.find(';', start), signature.find(']', start));
199-
#else
200-
// MSVC format: "...extract_type_name<TYPE>(void)..."
201-
// The type ends at '>' (template arg close) or ',' (comma in multi-param templates).
202-
constexpr auto end = min_find_pos(signature.find('>', start), signature.find(',', start));
203-
#endif
204-
205-
if constexpr (end == npos || end <= start) {
206-
return "unknown";
207-
}
208-
209-
return strip_type_qualifiers(signature.substr(start, end - start));
210-
}
211-
212-
} // namespace detail
213-
214172
/**
215173
* table_name_for - Automatically derive table name from struct type name via reflection
216174
*

0 commit comments

Comments
 (0)