docs
Loading...
Searching...
No Matches
ValueTraits.h
Go to the documentation of this file.
1///
2/// Copyright (C) 2026 Ultralight, Inc. All rights reserved.
3/// A license is required for commercial use. https://ultralig.ht
4///
5#pragma once
6#include <Ultralight/Color.h>
7#include <Ultralight/String.h>
9#include <Ultralight/dom/data/detail/EnumReflection.h>
10
11#include <cstdint>
12#include <cstdio>
13#include <string>
14#include <string_view>
15#include <type_traits>
16
17namespace ultralight {
18namespace dom {
19namespace data {
20
21///
22/// The kinds of value a schema entry holds.
23///
24/// A leaf field (one that holds a value, not a nested object or a list) has one of the first six
25/// kinds (see ValueTraits). Object, List, and Action entries hold no value of their own.
26///
27enum class ValueKind : uint8_t {
28 Bool = 0, ///< A boolean.
29 Int64, ///< A signed 64-bit integer (from any integer type).
30 Double, ///< A double-precision float (from any floating-point type).
31 String, ///< UTF-8 text (from strings and enums).
32 StyleValue, ///< A CSS number with a unit (see dom::StyleValue).
33 Color, ///< A color (see ultralight::Color).
34 Object, ///< A nested object (no value of its own).
35 List, ///< A list of rows (no value of its own).
36 Action, ///< An action (no value of its own).
37};
38
39///
40/// The range of values searched for an enum's enumerator names.
41///
42/// Only enumerators whose values fall in this range (0 to 63 by default) bind by name. Specialize
43/// it for enums with values outside that range:
44///
45/// ```
46/// template <> struct ultralight::dom::data::EnumRange<MyFlags> {
47/// static constexpr long long min = 0, max = 255;
48/// };
49/// ```
50///
51/// The range can span at most 1024 values. An enum with no enumerator in its range fails to
52/// compile.
53///
54/// @note This is separate from js::EnumRange. An enum you use with both APIs needs a
55/// specialization of each.
56///
57template <typename E>
58struct EnumRange {
59 static constexpr long long min = 0; ///< The lowest value searched (inclusive).
60 static constexpr long long max = 63; ///< The highest value searched (inclusive).
61};
62
63/// \cond INTERNAL
64namespace detail {
65
66// The library's built-in leaf conversions. ValueTraits inherits this tier, so a user
67// specialization of ValueTraits always takes priority, and a release can add built-ins here
68// without colliding with specializations users already wrote.
69template <typename T, typename = void>
70struct BuiltinValueTraits {};
71
72template <>
73struct BuiltinValueTraits<bool> {
74 static constexpr ValueKind kind = ValueKind::Bool;
75 static constexpr bool ToSlot(bool value) { return value; }
76};
77
78// Integral types (bool excluded) lower to the signed 64-bit slot.
79template <typename T>
80struct BuiltinValueTraits<T,
81 std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool>>> {
82 static constexpr ValueKind kind = ValueKind::Int64;
83 static constexpr int64_t ToSlot(T value) { return static_cast<int64_t>(value); }
84};
85
86// Floating-point types lower to the double slot.
87template <typename T>
88struct BuiltinValueTraits<T, std::enable_if_t<std::is_floating_point_v<T>>> {
89 static constexpr ValueKind kind = ValueKind::Double;
90 static constexpr double ToSlot(T value) { return static_cast<double>(value); }
91};
92
93template <>
94struct BuiltinValueTraits<String> {
95 static constexpr ValueKind kind = ValueKind::String;
96 static const String& ToSlot(const String& value) { return value; }
97};
98
99template <>
100struct BuiltinValueTraits<std::string> {
101 static constexpr ValueKind kind = ValueKind::String;
102 static String ToSlot(const std::string& value) {
103 return String(value.data(), value.size());
104 }
105};
106
107template <>
108struct BuiltinValueTraits<std::string_view> {
109 static constexpr ValueKind kind = ValueKind::String;
110 static String ToSlot(std::string_view value) { return String(value.data(), value.size()); }
111};
112
113template <>
114struct BuiltinValueTraits<const char*> {
115 static constexpr ValueKind kind = ValueKind::String;
116 static String ToSlot(const char* value) { return String(value ? value : ""); }
117};
118
119// Non-const char* fields are strings too; without this they would classify as nullable
120// pointers to an undescribed char and fail with a misleading diagnostic.
121template <>
122struct BuiltinValueTraits<char*> : BuiltinValueTraits<const char*> {};
123
124template <>
125struct BuiltinValueTraits<StyleValue> {
126 static constexpr ValueKind kind = ValueKind::StyleValue;
127 static const StyleValue& ToSlot(const StyleValue& value) { return value; }
128};
129
130template <>
131struct BuiltinValueTraits<Color> {
132 static constexpr ValueKind kind = ValueKind::Color;
133 static const Color& ToSlot(const Color& value) { return value; }
134};
135
136#if ULTRALIGHT_REFLECTION
137
138// Enums lower to the string slot as their enumerator name ("Small"), reflected from the
139// type. A value with no reflected name lowers as its decimal number rather than an
140// invented name (a native-side bug made visible, matching the js family's behavior).
141template <typename E>
142struct BuiltinValueTraits<E, std::enable_if_t<std::is_enum_v<E>>> {
143 static constexpr ValueKind kind = ValueKind::String;
144 static String ToSlot(E value) {
145 const long long raw = static_cast<long long>(value);
146 const std::string_view name = detail::EnumInfo<E>::NameOf(raw);
147 if (!name.empty())
148 return String(name.data(), name.size());
149 char buffer[24];
150 const int length = snprintf(buffer, sizeof(buffer), "%lld", raw);
151 return String(buffer, length > 0 ? static_cast<size_t>(length) : 0);
152 }
153};
154
155#endif // ULTRALIGHT_REFLECTION
156
157} // namespace detail
158/// \endcond
159
160///
161/// Conversion rules that expose a C++ type as a primitive value in data bindings.
162///
163/// ValueTraits defines how data bindings convert a custom C++ scalar type into a primitive value on
164/// the page. Specializing it lets your application bind custom scalar types, such as a fixed-point
165/// coordinate, directly as values without schemas.
166///
167/// This specialization binds a fixed-point engine type as a floating-point number:
168///
169/// ```
170/// template <> struct dd::ValueTraits<Fixed> {
171/// static constexpr dd::ValueKind kind = dd::ValueKind::Double;
172/// static double ToSlot(const Fixed& value) { return value.ToDouble(); }
173/// };
174///
175/// struct Ship {
176/// Fixed speed;
177/// void Boost(Fixed amount);
178///
179/// static constexpr auto schema = dd::Schema(
180/// dd::Field("speed", &Ship::speed, dd::Editable), // ship.speed
181/// dd::Action<Fixed>("boost"));
182/// };
183/// ```
184///
185/// ## Built-In Value Types
186///
187/// The library provides built-in traits for primitive C++ types:
188///
189/// | Type | Kind |
190/// |----------------------|-----------------------|
191/// | `bool` | ValueKind::Bool |
192/// | Integer types | ValueKind::Int64 |
193/// | Floating-point types | ValueKind::Double |
194/// | String types | ValueKind::String |
195/// | Enums | ValueKind::String |
196/// | dom::StyleValue | ValueKind::StyleValue |
197/// | Color | ValueKind::Color |
198///
199/// An enum converts to text using its enumerator name. If an enum value doesn't have a matching
200/// name in the reflected range (0 to 63 by default), it displays as its decimal number instead (see
201/// dom::data::EnumRange).
202///
203/// ## Custom Value Types
204///
205/// To bind a custom scalar type, specialize ValueTraits in the `ultralight::dom::data` namespace
206/// with two members:
207///
208/// - **Set `kind` to the matching ValueKind constant.** A specialization can use any of the six
209/// value kinds to represent the type on the page.
210/// - **Define a static ToSlot() function.** It accepts your custom type by value or const reference
211/// and returns the corresponding C++ representation.
212///
213/// Specializations follow two priority rules:
214///
215/// - **Your specialization takes priority over built-in traits.**
216/// - **A ValueTraits specialization takes priority over a schema.** The library binds the type as a
217/// leaf value even if it defines a schema or qualifies for aggregate reflection.
218///
219/// ## Receiving Values from the Page
220///
221/// Conversion works in one direction only, from native code to the page. When receiving values
222/// back, handlers accept primitive representations:
223///
224/// - **Change handlers take the primitive C++ type or a dom::data::Value.** A handler registered
225/// with Binding::OnChange() receives the type corresponding to your `kind` (such as `double` for
226/// `ValueKind::Double`).
227/// - **Action payloads arrive as a dom::data::Value.**
228///
229/// Convert the primitive value back to your custom type inside the handler:
230///
231/// ```
232/// binding.OnChange<"speed">([&](double speed) {
233/// ship.speed = Fixed::FromDouble(speed);
234/// });
235/// binding.OnAction<"boost">([&](dd::Value amount) {
236/// ship.Boost(Fixed::FromDouble(amount.Or(0.0)));
237/// });
238/// ```
239///
240/// @note Don't register a change handler that takes your own type. If your type converts from a
241/// number, it can compile and receive the wrong value (see the warning on
242/// dom::data::Binding::OnChange()).
243///
244/// @see dom::data::ValueKind, dom::data::EnumRange, dom::data::TypeTraits,
245/// dom::data::Binding::OnChange()
246///
247template <typename T, typename = void>
248struct ValueTraits : detail::BuiltinValueTraits<T> {};
249
250///
251/// Whether or not T binds as a leaf value (ignoring const, volatile, and references), ie.
252/// ValueTraits is specialized for it.
253///
254template <typename T>
255concept Bindable = requires { ValueTraits<std::remove_cvref_t<T>>::kind; };
256
257///
258/// Whether or not P can be an action's payload without a payload struct (ignoring const,
259/// volatile, and references).
260///
261/// It can when it binds as a Bool, Int64, Double, or String value (eg, `Action<double>` or an
262/// enum). dom::StyleValue and Color can't be payloads.
263///
264/// @note A handler for a payload of your own ValueTraits type must take a dd::Value (see
265/// ValueTraits).
266///
267template <typename P>
273
274} // namespace data
275} // namespace dom
276} // namespace ultralight
An RGBA color value in a certain color space (with CSS parsing helpers).
Definition Color.h:69
Unicode string container with conversions for UTF-8, UTF-16, and UTF-32.
Definition String.h:31
Whether or not T binds as a leaf value (ignoring const, volatile, and references),...
Definition ValueTraits.h:255
Whether or not P can be an action's payload without a payload struct (ignoring const,...
Definition ValueTraits.h:268
Definition StringSTL.h:166
Data-binding API that connects native C++ data to HTML and CSS markup.
Definition ActionInfo.h:13
ValueKind
The kinds of value a schema entry holds.
Definition ValueTraits.h:27
@ String
UTF-8 text (from strings and enums).
Definition ValueTraits.h:31
@ Object
A nested object (no value of its own).
Definition ValueTraits.h:34
@ StyleValue
A CSS number with a unit (see dom::StyleValue).
Definition ValueTraits.h:32
@ Bool
A boolean.
Definition ValueTraits.h:28
@ Color
A color (see ultralight::Color).
Definition ValueTraits.h:33
@ Double
A double-precision float (from any floating-point type).
Definition ValueTraits.h:30
@ Int64
A signed 64-bit integer (from any integer type).
Definition ValueTraits.h:29
consteval ActionToken< Payload > Action(const char *name, Tags... tags)
Declare an action (a request the page sends to your code).
Definition Schema.h:688
consteval auto List(const char *name, A accessor, KeyPtr key, Tags... tags)
Declare a keyed list.
Definition Schema.h:615
Direct C++ access to modify page elements and handle events.
Root namespace for every public Ultralight type, function, and enumeration.
A numeric CSS value and its unit.
Definition StyleValue.h:147
The range of values searched for an enum's enumerator names.
Definition ValueTraits.h:58
static constexpr long long max
The highest value searched (inclusive).
Definition ValueTraits.h:60
static constexpr long long min
The lowest value searched (inclusive).
Definition ValueTraits.h:59
Conversion rules that expose a C++ type as a primitive value in data bindings.
Definition ValueTraits.h:248