welder 0.1.0
Bindings for annotated C++ types, from C++26 reflection
Loading...
Searching...
No Matches
array_interface.hpp
Go to the documentation of this file.
1#pragma once
2#include <array>
3#include <cstddef>
4#include <meta>
5#include <string>
6#include <type_traits>
7#include <utility>
8
28
29namespace welder::inline v0::rods::python {
30
32consteval std::string ai_uint_string(std::size_t n) {
33 if (n == 0)
34 return "0";
35 std::string s{};
36 for (; n; n /= 10)
37 s.insert(s.begin(), char('0' + n % 10));
38 return s;
39}
40
43consteval bool ai_is_unsigned(std::meta::info t) {
44 t = std::meta::dealias(t);
45 for (std::meta::info u :
46 {^^unsigned char, ^^unsigned short, ^^unsigned int, ^^unsigned long,
47 ^^unsigned long long, ^^char8_t, ^^char16_t, ^^char32_t})
48 if (t == u)
49 return true;
50 return false;
51}
52
63consteval std::string numpy_typestr(std::meta::info t) {
64 t = std::meta::dealias(t);
65 if (std::meta::is_enum_type(t))
66 t = std::meta::dealias(std::meta::underlying_type(t));
67 if (t == ^^bool)
68 return "|b1";
69 if (t == ^^float)
70 return "<f4";
71 if (t == ^^double)
72 return "<f8";
73 if (!std::meta::is_arithmetic_type(t) || t == ^^long double)
74 return ""; // no portable NumPy scalar
75 const std::size_t sz{std::meta::size_of(t)};
76 const char code{ai_is_unsigned(t) ? 'u' : 'i'};
77 const char prefix{sz == 1 ? '|' : '<'};
78 return std::string{prefix} + code + ai_uint_string(sz);
79}
80
83template <class E>
84consteval const char* ai_typestr() {
85 return std::define_static_string("|V" + ai_uint_string(sizeof(E)));
86}
87
90consteval bool ai_all_fields_numpy(std::meta::info E) {
91 const auto members{std::meta::nonstatic_data_members_of(
92 E, std::meta::access_context::unchecked())};
93 if (members.empty())
94 return false;
95 for (std::meta::info m : members)
96 if (numpy_typestr(std::meta::type_of(m)).empty())
97 return false;
98 return true;
99}
100
105template <class E>
106consteval bool pod_array_eligible() {
107 return std::is_class_v<E> && std::is_trivially_copyable_v<E> &&
108 std::is_standard_layout_v<E> && ai_all_fields_numpy(^^E);
109}
110
113consteval std::size_t ai_entry_count(std::meta::info E) {
114 std::size_t n{0}, running{0};
115 for (std::meta::info m : std::meta::nonstatic_data_members_of(
116 E, std::meta::access_context::unchecked())) {
117 // offset_of(m).bytes is std::ptrdiff_t (signed); a field offset is
118 // non-negative, so cast to size_t (avoids -Werror=narrowing/sign-conversion).
119 const std::size_t off{
120 static_cast<std::size_t>(std::meta::offset_of(m).bytes)};
121 if (off > running)
122 ++n; // interior padding
123 ++n; // the field
124 running = off + std::meta::size_of(std::meta::type_of(m));
125 }
126 if (running < std::meta::size_of(E))
127 ++n; // trailing padding
128 return n;
129}
130
136template <std::meta::info E>
137consteval std::array<std::pair<const char*, const char*>, ai_entry_count(E)>
139 std::array<std::pair<const char*, const char*>, ai_entry_count(E)> out{};
140 std::size_t i{0}, running{0};
141 for (std::meta::info m : std::meta::nonstatic_data_members_of(
142 E, std::meta::access_context::unchecked())) {
143 // offset_of(m).bytes is std::ptrdiff_t (signed); a field offset is
144 // non-negative, so cast to size_t (avoids -Werror=narrowing/sign-conversion).
145 const std::size_t off{
146 static_cast<std::size_t>(std::meta::offset_of(m).bytes)};
147 if (off > running)
148 out[i++] = {"", std::define_static_string("|V" +
149 ai_uint_string(off - running))};
150 out[i++] = {std::define_static_string(std::meta::identifier_of(m)),
151 std::define_static_string(numpy_typestr(std::meta::type_of(m)))};
152 running = off + std::meta::size_of(std::meta::type_of(m));
153 }
154 if (running < std::meta::size_of(E))
155 out[i++] = {"", std::define_static_string(
156 "|V" + ai_uint_string(std::meta::size_of(E) - running))};
157 return out;
158}
159
160} // namespace welder::inline v0::rods::python
consteval bool pod_array_eligible()
Is std::vector<E> viewable as a NumPy structured array — i.e.
consteval const char * ai_typestr()
The array-interface typestr for the whole element E — an opaque |V<sizeof> void field; the per-field ...
consteval std::size_t ai_entry_count(std::meta::info E)
The number of array-interface descr entries for E — one per field, plus one per padding gap (interior...
consteval bool ai_all_fields_numpy(std::meta::info E)
Whether every one of a POD struct's fields maps to a NumPy scalar (so the descr is complete).
consteval std::string numpy_typestr(std::meta::info t)
The NumPy typestr for arithmetic type t ('<' little-endian, '|' for a single byte),...
consteval bool ai_is_unsigned(std::meta::info t)
Is t one of the unsigned integer fundamental types?
consteval std::array< std::pair< const char *, const char * >, ai_entry_count(E)> ai_descr()
The NumPy array-interface descr for E: a splice-ready std::array of (name, typestr) pairs — each fiel...
consteval std::string ai_uint_string(std::size_t n)
Decimal render of n (constexpr std::to_string is unavailable on gcc-16).