docs
Docs
C++ API
C API
Search API
Ctrl K
2.0
2.0
latest
1.4
Ultralight C++ API
2.0.0
Toggle main menu visibility
Loading...
Searching...
No Matches
Holder.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/HolderTraits.h
>
7
#include <
Ultralight/RefPtr.h
>
8
9
#include <concepts>
10
#include <memory>
11
#include <type_traits>
12
13
namespace
ultralight
{
14
namespace
js
{
15
16
///
17
/// Non-owning holder for bound-class singletons and program-lifetime objects.
18
///
19
/// An Eternal wraps a pointer to a native object that lives for the entire run of the application
20
/// and is never destroyed, such as an engine subsystem or a global singleton. It lets page scripts
21
/// interact with the native instance through a standard JavaScript wrapper without reference
22
/// counting or ownership transfers.
23
///
24
/// You'll typically specialize js::ClassHolder with Eternal beside the class declaration so every
25
/// file that binds or converts the class includes it. This makes Eternal the class's canonical
26
/// holder, allowing you to assign the singleton directly to an API path.
27
///
28
/// This example exposes a game's world singleton to the page as `app.world`:
29
///
30
/// ```
31
/// class GameWorld {
32
/// public:
33
/// static GameWorld& Current();
34
/// };
35
///
36
/// template <>
37
/// struct ultralight::js::ClassHolder<GameWorld> {
38
/// using type = js::Eternal<GameWorld>;
39
/// };
40
///
41
/// void RegisterWorld(js::API& api) {
42
/// api.DefineClass<GameWorld>("GameWorld"); // no Constructor: `new` throws
43
/// api["world"] = js::Eternal(&GameWorld::Current());
44
/// }
45
/// ```
46
///
47
/// ## Differences from Raw Pointers
48
///
49
/// Both a `T*` and an Eternal leave ownership with you, but they differ in how they can cross the
50
/// bridge:
51
///
52
/// - **Raw pointers cannot be assigned to an API path directly.** A bound function or property
53
/// getter can return a `T*` to give the page a wrapper that lasts, but raw pointers can't be
54
/// passed to API::Emit() or Resolver::Resolve().
55
/// - **Eternal holders support direct assignment and deferred deliveries.** An Eternal can be
56
/// assigned directly to an API path (as shown above) or passed to API::Emit() and
57
/// Resolver::Resolve(), which deliver the instance on a later frame during Renderer::Update().
58
///
59
/// @note ClassBuilder::Constructor() doesn't support classes whose canonical holder is an Eternal.
60
/// Calling `new` on the page throws a TypeError with code `ULJS_NO_CTOR` (use a property
61
/// binding or a static method returning the Eternal instead).
62
///
63
/// @see js::ClassHolder, js::ClassBuilder, ultralight::HolderTraits
64
///
65
template
<
typename
T>
66
class
Eternal
{
67
public
:
68
///
69
/// Create an empty Eternal.
70
///
71
constexpr
Eternal
() =
default
;
72
73
///
74
/// Create an Eternal for an instance.
75
///
76
/// @param instance The instance, which must never be destroyed.
77
///
78
constexpr
explicit
Eternal
(T* instance) : ptr_(instance) {}
79
80
///
81
/// Get the instance (NULL when empty).
82
///
83
T*
get
()
const
{
return
ptr_; }
84
85
///
86
/// Whether or not this holds an instance.
87
///
88
explicit
operator
bool()
const
{
return
ptr_ !=
nullptr
; }
89
90
private
:
91
T* ptr_ =
nullptr
;
92
};
93
94
template
<
typename
T>
95
Eternal
(T*) ->
Eternal<T>
;
96
97
///
98
/// Whether or not H (ignoring const and references) is an owning holder: its
99
/// ultralight::HolderTraits declares element_type, kind, and Get().
100
///
101
template
<
typename
H>
102
concept
Holder
=
requires
(
const
std::remove_cvref_t<H>& h) {
103
typename
HolderTraits<std::remove_cvref_t<H>
>::element_type;
104
{
HolderTraits<std::remove_cvref_t<H>
>::kind } -> std::convertible_to<HolderKind>;
105
HolderTraits<std::remove_cvref_t<H>
>::Get(h);
106
};
107
108
///
109
/// Whether or not H (ignoring const and references) is a weak holder: its
110
/// ultralight::HolderTraits declares element_type and a Lock() that returns an owning holder.
111
///
112
template
<
typename
H>
113
concept
LockableHolder
=
requires
(
const
std::remove_cvref_t<H>& h) {
114
typename
HolderTraits<std::remove_cvref_t<H>
>::element_type;
115
{
HolderTraits<std::remove_cvref_t<H>
>
::Lock
(h) } ->
Holder
;
116
};
117
118
///
119
/// Type trait that selects the smart pointer type for owned instances of a bound class.
120
///
121
/// When an owned instance of a bound C++ class crosses the bridge to JavaScript, it travels in its
122
/// canonical holder-- the smart pointer that manages native ownership for that class.
123
///
124
/// This specialization sets `std::shared_ptr` as the holder for a bound `Database` class:
125
///
126
/// ```
127
/// template <>
128
/// struct ultralight::js::ClassHolder<Database> {
129
/// using type = std::shared_ptr<Database>;
130
/// };
131
/// ```
132
///
133
/// ## Where the Holder Is Used
134
///
135
/// The library requires the canonical holder when transferring ownership across the bridge:
136
///
137
/// - **Bound functions must return owned instances in the canonical holder.** Returning an owned
138
/// instance in any other smart pointer is a compile error.
139
/// - **API::Emit() and Resolver::Resolve() require the canonical holder.** Delivering an instance
140
/// later during Renderer::Update() requires an owning smart pointer rather than a borrowed raw
141
/// pointer.
142
///
143
/// ## Supported Holder Types
144
///
145
/// By default, `type` is RefPtr for a class that derives from RefCounted, or std::unique_ptr for
146
/// any other class. You can specialize ClassHolder to select any smart pointer that matches your
147
/// ownership model:
148
///
149
/// - **Use std::shared_ptr for shared ownership.** Native code and the JavaScript wrapper share
150
/// ownership of non-RefCounted objects.
151
/// - **Use js::Eternal for program-lifetime objects.** Singletons and subsystems wrapped in
152
/// js::Eternal are never destroyed by the library or the page.
153
/// - **Use a custom smart pointer with HolderTraits.** Specialize ultralight::HolderTraits for your
154
/// smart pointer type before specializing ClassHolder.
155
///
156
/// \parblock
157
/// @note Declare the specialization in the header that defines the class, so every file that binds
158
/// or converts the class uses the same holder type.
159
/// \endparblock
160
///
161
/// \parblock
162
/// @note ClassBuilder::Constructor() works only when the canonical holder is RefPtr,
163
/// std::unique_ptr, or std::shared_ptr. For another holder (such as js::Eternal), bind a
164
/// static factory method with ClassBuilder::StaticMethod() that returns the holder instead.
165
/// \endparblock
166
///
167
/// @see js::ClassBuilder, ultralight::HolderTraits, js::Eternal, js::Detach()
168
///
169
template
<
typename
T,
typename
=
void
>
170
struct
ClassHolder
{
171
using
type
= std::conditional_t<std::is_base_of_v<RefCounted, T>,
RefPtr<T>
,
172
std::unique_ptr<T>>;
173
};
174
175
}
// namespace js
176
177
/// \cond INTERNAL
178
namespace
detail {
179
180
template
<
typename
T>
181
struct
BuiltinHolderTraits<
js
::Eternal<T>> {
182
using
element_type = T;
183
static
constexpr
HolderKind
kind =
HolderKind::Shared
;
184
static
T* Get(
const
js::Eternal<T>
& holder) {
return
holder.
get
(); }
185
};
186
187
}
// namespace detail
188
/// \endcond
189
190
}
// namespace ultralight
HolderTraits.h
RefPtr.h
ultralight::Lock
Lightweight mutex optimized for short locking periods.
Definition
Lock.h:23
ultralight::RefPtr
A nullable smart pointer.
Definition
RefPtr.h:126
ultralight::js::Eternal
Non-owning holder for bound-class singletons and program-lifetime objects.
Definition
Holder.h:66
ultralight::js::Eternal::Eternal
constexpr Eternal(T *instance)
Create an Eternal for an instance.
Definition
Holder.h:78
ultralight::js::Eternal::Eternal
constexpr Eternal()=default
Create an empty Eternal.
ultralight::js::Eternal::get
T * get() const
Get the instance (NULL when empty).
Definition
Holder.h:83
ultralight::js::Holder
Whether or not H (ignoring const and references) is an owning holder: its ultralight::HolderTraits de...
Definition
Holder.h:102
ultralight::js::LockableHolder
Whether or not H (ignoring const and references) is a weak holder: its ultralight::HolderTraits decla...
Definition
Holder.h:113
ultralight::js
Type-checked JavaScript bridge between C++ and web pages.
Definition
JSInterop.h:141
ultralight
Root namespace for every public Ultralight type, function, and enumeration.
ultralight::HolderKind
HolderKind
Whether a holder shares ownership of a bound-class instance or owns it alone.
Definition
HolderTraits.h:16
ultralight::HolderKind::Shared
@ Shared
Copies share ownership (eg, RefPtr), or the instance is never destroyed (js::Eternal).
Definition
HolderTraits.h:20
ultralight::HolderTraits
Traits template that lets the library hold objects through custom smart pointers.
Definition
HolderTraits.h:156
ultralight::js::ClassHolder
Type trait that selects the smart pointer type for owned instances of a bound class.
Definition
Holder.h:170
ultralight::js::ClassHolder::type
std::conditional_t< std::is_base_of_v< RefCounted, T >, RefPtr< T >, std::unique_ptr< T > > type
Definition
Holder.h:171
Ultralight
js
Holder.h
Docs
C++ API
C API
Version
2.0
2.0
latest
1.4