welder 0.1.0
Bindings for annotated C++ types, from C++26 reflection
Loading...
Searching...
No Matches
naming.hpp
Go to the documentation of this file.
1#pragma once
2#include <meta>
3#include <stdexcept> // name_of_or: missing-override diagnostic
4#include <string>
5#include <string_view>
6#include <vector>
7
8#include <welder/concepts.hpp> // the name_style concept (welder::naming::name_style)
9
45
46namespace welder::inline v0::naming {
47
48// --- identifier word-splitting ----------------------------------------------
49
63consteval std::vector<std::string> split_words(std::string_view id) {
64 auto is_upper = [](char c) { return c >= 'A' && c <= 'Z'; };
65 auto is_lower = [](char c) { return c >= 'a' && c <= 'z'; };
66 auto is_digit = [](char c) { return c >= '0' && c <= '9'; };
67 auto to_lower = [&](char c) { return is_upper(c) ? char(c - 'A' + 'a') : c; };
68
69 std::vector<std::string> words{};
70 std::string cur{};
71 auto flush = [&] {
72 if (!cur.empty()) {
73 words.push_back(cur);
74 cur.clear();
75 }
76 };
77 for (std::string::size_type i{0}; i < id.size(); ++i) {
78 const char c{id[i]};
79 if (c == '_' || c == '-' || c == ' ') {
80 flush();
81 continue;
82 }
83 if (!cur.empty()) {
84 const char prev{id[i - 1]};
85 const bool hump{is_upper(c) && (is_lower(prev) || is_digit(prev))};
86 const bool acronym_end{is_upper(c) && is_upper(prev) &&
87 i + 1 < id.size() && is_lower(id[i + 1])};
88 if (hump || acronym_end)
89 flush();
90 }
91 cur.push_back(to_lower(c));
92 }
93 flush();
94 return words;
95}
96
97// --- word joiners (the common conventions) ----------------------------------
98
107
112consteval std::string join_words(const std::vector<std::string>& words,
113 case_kind kind) {
114 auto upper = [](char c) { return (c >= 'a' && c <= 'z') ? char(c - 'a' + 'A') : c; };
115 auto cap = [&](std::string w) {
116 if (!w.empty())
117 w[0] = upper(w[0]);
118 return w;
119 };
120 std::string out{};
121 for (std::string::size_type i{0}; i < words.size(); ++i) {
122 switch (kind) {
123 case case_kind::snake:
124 if (i)
125 out += '_';
126 out += words[i];
127 break;
129 if (i)
130 out += '_';
131 for (char c : words[i])
132 out += upper(c);
133 break;
134 case case_kind::kebab:
135 if (i)
136 out += '-';
137 out += words[i];
138 break;
139 case case_kind::camel:
140 out += i ? cap(words[i]) : words[i];
141 break;
143 out += cap(words[i]);
144 break;
145 }
146 }
147 return out;
148}
149
157consteval std::string restyle(std::string_view id, case_kind kind) {
158 std::string::size_type lead{0};
159 while (lead < id.size() && id[lead] == '_')
160 ++lead;
161 if (lead == id.size()) // empty or all underscores: nothing to restyle
162 return std::string{id};
163 std::string::size_type trail{0};
164 while (id[id.size() - 1 - trail] == '_')
165 ++trail;
166 return std::string(lead, '_') + join_words(split_words(id), kind) +
167 std::string(trail, '_');
168}
169
181consteval case_kind detect_case(std::string_view id) {
182 bool has_underscore{false}, has_hyphen{false}, has_lower{false},
183 has_upper{false};
184 bool first_alpha_upper{false}, seen_alpha{false};
185 for (char c : id) {
186 if (c == '_')
187 has_underscore = true;
188 else if (c == '-')
189 has_hyphen = true;
190 else if (c >= 'a' && c <= 'z')
191 has_lower = true;
192 else if (c >= 'A' && c <= 'Z')
193 has_upper = true;
194 if (!seen_alpha && ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))) {
195 seen_alpha = true;
196 first_alpha_upper = (c >= 'A' && c <= 'Z');
197 }
198 }
199 if (has_hyphen)
200 return case_kind::kebab;
201 if (has_underscore || !has_upper)
202 return has_lower || !has_upper ? case_kind::snake
204 return first_alpha_upper ? case_kind::pascal : case_kind::camel;
205}
206
218consteval std::vector<std::string> accessor_property_words(std::string_view id) {
219 std::vector<std::string> words{split_words(id)};
220 if (words.size() >= 2 && (words[0] == "get" || words[0] == "set"))
221 words.erase(words.begin());
222 return words;
223}
224
237consteval std::string strip_accessor_word(std::string_view styled) {
238 std::string::size_type lead{0};
239 while (lead < styled.size() && styled[lead] == '_')
240 ++lead;
241 if (lead == styled.size())
242 return std::string{styled};
243 std::string::size_type trail{0};
244 while (styled[styled.size() - 1 - trail] == '_')
245 ++trail;
246 return std::string(lead, '_') +
248 std::string(trail, '_');
249}
250
251// --- the style customization point ------------------------------------------
252//
253// The `name_style` concept — the per-kind hook contract a style below implements —
254// lives in <welder/concepts.hpp> (with welder's other interface concepts).
255
259struct none {
260 static consteval std::string transform_class(std::meta::info e) { return id(e); }
261 static consteval std::string transform_enum(std::meta::info e) { return id(e); }
262 static consteval std::string transform_enumerator(std::meta::info e) { return id(e); }
263 static consteval std::string transform_method(std::meta::info e) { return id(e); }
264 static consteval std::string transform_static_method(std::meta::info e) { return id(e); }
265 static consteval std::string transform_function(std::meta::info e) { return id(e); }
266 static consteval std::string transform_field(std::meta::info e) { return id(e); }
267 static consteval std::string transform_variable(std::meta::info e) { return id(e); }
268 static consteval std::string transform_submodule(std::meta::info e) { return id(e); }
269
270 private:
271 static consteval std::string id(std::meta::info e) {
272 return std::string{std::meta::identifier_of(e)};
273 }
274};
275static_assert(name_style<none>, "welder: naming::none is not a name_style");
276
281template <case_kind Kind>
282struct uniform {
283 static consteval std::string transform_class(std::meta::info e) { return in(e); }
284 static consteval std::string transform_enum(std::meta::info e) { return in(e); }
285 static consteval std::string transform_enumerator(std::meta::info e) { return in(e); }
286 static consteval std::string transform_method(std::meta::info e) { return in(e); }
287 static consteval std::string transform_static_method(std::meta::info e) { return in(e); }
288 static consteval std::string transform_function(std::meta::info e) { return in(e); }
289 static consteval std::string transform_field(std::meta::info e) { return in(e); }
290 static consteval std::string transform_variable(std::meta::info e) { return in(e); }
291 static consteval std::string transform_submodule(std::meta::info e) { return in(e); }
292
293 private:
294 static consteval std::string in(std::meta::info e) {
295 return restyle(std::meta::identifier_of(e), Kind);
296 }
297};
298
299// Each predefined style is checked against the `name_style` concept right at its
300// definition, so a hook that drifts out of contract (a missing or wrongly-typed
301// transform_*) fails loudly here rather than at some rod's call site.
303static_assert(name_style<snake_case>, "welder: naming::snake_case is not a name_style");
306 "welder: naming::screaming_snake_case is not a name_style");
308static_assert(name_style<kebab_case>, "welder: naming::kebab_case is not a name_style");
310static_assert(name_style<camel_case>, "welder: naming::camel_case is not a name_style");
312static_assert(name_style<pascal_case>, "welder: naming::pascal_case is not a name_style");
313
314} // namespace welder::naming
315
316namespace welder::inline v0 {
317
331
342template <std::meta::info Ent, lang L>
343consteval const char* weld_as_of() {
344 template for (constexpr auto a :
345 std::define_static_array(std::meta::annotations_of(Ent))) {
346 constexpr std::meta::info t{std::meta::type_of(a)};
347 if constexpr (std::meta::has_template_arguments(t) &&
348 std::meta::template_of(t) == ^^detail::weld_as_spec) {
349 using spec_type = [:t:];
350 constexpr auto spec{std::meta::extract<spec_type>(a)};
351 if constexpr (spec.mask == 0 || (spec.mask & lang_bit(L)) != 0)
352 return std::define_static_string(spec.name.data);
353 }
354 }
355 return nullptr;
356}
357
374template <std::meta::info Ent, lang L, class Style, ent_kind K>
375consteval const char* name_of() {
376 if (const char* forced{weld_as_of<Ent, L>()})
377 return forced;
378 if constexpr (K == ent_kind::class_)
379 return std::define_static_string(Style::transform_class(Ent));
380 else if constexpr (K == ent_kind::enum_)
381 return std::define_static_string(Style::transform_enum(Ent));
382 else if constexpr (K == ent_kind::enumerator)
383 return std::define_static_string(Style::transform_enumerator(Ent));
384 else if constexpr (K == ent_kind::method)
385 return std::define_static_string(Style::transform_method(Ent));
386 else if constexpr (K == ent_kind::static_method)
387 return std::define_static_string(Style::transform_static_method(Ent));
388 else if constexpr (K == ent_kind::function)
389 return std::define_static_string(Style::transform_function(Ent));
390 else if constexpr (K == ent_kind::field)
391 return std::define_static_string(Style::transform_field(Ent));
392 else if constexpr (K == ent_kind::variable)
393 return std::define_static_string(Style::transform_variable(Ent));
394 else // ent_kind::submodule
395 return std::define_static_string(Style::transform_submodule(Ent));
396}
397
423template <std::meta::info Ent, lang L, class Style, ent_kind K>
424constexpr const char* name_of_or(const char* override_) {
425 if (override_)
426 return override_;
427 if constexpr (weld_as_of<Ent, L>() != nullptr || std::meta::has_identifier(Ent)) {
429 } else {
430 throw std::invalid_argument{
431 "welder: this entity has no identifier (a template instantiation?) and "
432 "no [[=welder::weld_as]] — pass an explicit name at the weld_* call"};
433 }
434}
435
436} // namespace welder
The core interface concepts welder's static polymorphism rests on, gathered in one catalogue: the cus...
A name style names every kind of entity welder can bind, through one hook per kind.
Definition concepts.hpp:456
consteval std::string restyle(std::string_view id, case_kind kind)
Re-spell identifier id in convention kind, preserving any leading and trailing underscore run (_priva...
Definition naming.hpp:157
uniform< case_kind::camel > camel_case
fooBar everywhere
Definition naming.hpp:309
uniform< case_kind::pascal > pascal_case
FooBar everywhere
Definition naming.hpp:311
consteval std::string strip_accessor_word(std::string_view styled)
Strip the accessor word off an already styled accessor name, re-joining the remainder in styled's own...
Definition naming.hpp:237
case_kind
The naming convention a joiner emits.
Definition naming.hpp:100
consteval std::vector< std::string > accessor_property_words(std::string_view id)
The words of an accessor's property: id split, with a leading get / set word stripped when at least o...
Definition naming.hpp:218
consteval std::vector< std::string > split_words(std::string_view id)
Split an identifier into lower-cased words, however it was spelled.
Definition naming.hpp:63
uniform< case_kind::kebab > kebab_case
foo-bar everywhere
Definition naming.hpp:307
uniform< case_kind::screaming_snake > screaming_snake_case
FOO_BAR everywhere
Definition naming.hpp:304
consteval std::string join_words(const std::vector< std::string > &words, case_kind kind)
Join already-lower-cased words in convention kind.
Definition naming.hpp:112
uniform< case_kind::snake > snake_case
foo_bar everywhere
Definition naming.hpp:302
consteval case_kind detect_case(std::string_view id)
The convention an identifier already reads as — the inverse guess a re-speller needs when it must pre...
Definition naming.hpp:181
ent_kind
The nameable entity kinds welder distinguishes, one per name-style hook — picked by the driver/rod so...
Definition naming.hpp:320
@ field
a data member → transform_field.
Definition naming.hpp:327
@ submodule
a namespace bound as a submodule → transform_submodule.
Definition naming.hpp:329
@ static_method
a static member function → transform_static_method.
Definition naming.hpp:325
@ class_
a class/struct type → transform_class.
Definition naming.hpp:321
@ function
a free function → transform_function.
Definition naming.hpp:326
@ variable
a namespace variable → transform_variable.
Definition naming.hpp:328
@ method
a member function → transform_method.
Definition naming.hpp:324
@ enumerator
an enumerator → transform_enumerator.
Definition naming.hpp:323
@ enum_
an enum type → transform_enum.
Definition naming.hpp:322
consteval const char * weld_as_of()
The verbatim weld_as name forced on Ent for language L, or nullptr.
Definition naming.hpp:343
consteval unsigned lang_bit(lang l)
The single-language bit for l within a language mask.
constexpr const char * name_of_or(const char *override_)
Resolve a bound name with a call-site override: override_ wins verbatim, nullptr falls back to name_o...
Definition naming.hpp:424
consteval const char * name_of()
The final bound name of Ent (a K-kind entity) for language L under name style Style.
Definition naming.hpp:375
The stored form of a weld_as annotation: a forced target-language name.
The identity style: bind every C++ identifier unchanged.
Definition naming.hpp:259
static consteval std::string transform_enumerator(std::meta::info e)
Definition naming.hpp:262
static consteval std::string transform_field(std::meta::info e)
Definition naming.hpp:266
static consteval std::string transform_variable(std::meta::info e)
Definition naming.hpp:267
static consteval std::string transform_method(std::meta::info e)
Definition naming.hpp:263
static consteval std::string transform_enum(std::meta::info e)
Definition naming.hpp:261
static consteval std::string id(std::meta::info e)
Definition naming.hpp:271
static consteval std::string transform_function(std::meta::info e)
Definition naming.hpp:265
static consteval std::string transform_submodule(std::meta::info e)
Definition naming.hpp:268
static consteval std::string transform_class(std::meta::info e)
Definition naming.hpp:260
static consteval std::string transform_static_method(std::meta::info e)
Definition naming.hpp:264
A single-convention style: reshape every kind to convention Kind, whatever the source spelling.
Definition naming.hpp:282
static consteval std::string transform_method(std::meta::info e)
Definition naming.hpp:286
static consteval std::string transform_function(std::meta::info e)
Definition naming.hpp:288
static consteval std::string transform_static_method(std::meta::info e)
Definition naming.hpp:287
static consteval std::string transform_submodule(std::meta::info e)
Definition naming.hpp:291
static consteval std::string transform_variable(std::meta::info e)
Definition naming.hpp:290
static consteval std::string in(std::meta::info e)
Definition naming.hpp:294
static consteval std::string transform_field(std::meta::info e)
Definition naming.hpp:289
static consteval std::string transform_class(std::meta::info e)
Definition naming.hpp:283
static consteval std::string transform_enumerator(std::meta::info e)
Definition naming.hpp:285
static consteval std::string transform_enum(std::meta::info e)
Definition naming.hpp:284