Auxiliary Interfaces
Registering an Interface
Class interfaces can be registered if you want to guarantee that script classes implement a specific set of class methods. Interfaces can be easier to use when working with script classes from the application, but they are not necessary as the application can easily enumerate available methods and properties even without the interfaces.
asbind20::interface(engine, "my_interface")
// Declarations only
.method("int get() const")
.funcdef("int callback(int)")
.method("int invoke(callback@ cb) const");
Note
Unlike the raw AngelScript interface,
you don’t need to add the class name into the declaration of member funcdef for asbind20.
Type Aliases
Function definitions can be registered when you wish to allow the script to pass function pointers to the application, e.g. to implement callback routines.
Enumeration types and typedefs can also be registered to improve readability of the scripts.
-
inline global &funcdef(cstring_ref decl)
Register a funcdef.
- Parameters:
decl – Function declaration
-
inline global &typedef_(cstring_ref type_decl, cstring_ref new_name)
Register a typedef.
- Parameters:
type_decl – Type declaration
new_name – Aliased type name
-
inline global &using_(cstring_ref new_name, cstring_ref type_decl)
Register a typedef in C++11 style.
- Parameters:
new_name – Aliased type name
type_decl – Type declaration
Example code:
asbind20::global(engine)
.funcdef("bool callback(int, int)")
.typedef_("float", "real32")
// For those who feel more comfortable with the C++11 style
// "using alias = type;"
.using_("float32", "float");
Enumerations
enum class my_enum : int
{
A,
B
};
enum_<my_enum>(engine, "my_enum")
.value(my_enum::A, "A")
.value(my_enum::B, "B");
Note
Up until version 2.39, AngelScript used a 32-bit integer to store the underlying value of enums, before it supported customizable underlying types. Please make sure those values don’t overflow or underflow.
Besides, the library provides a convenient interface for generating string representation of enum value at compile-time.
The following code is equivalent to the above one:
asbind20::enum_<my_enum>(engine, "my_enum")
.value<my_enum::A>()
.value<my_enum::B>();
Note
However, as static reflection is still waiting for C++26, this feature relies on compiler extension. It’s guaranteed to work on mainstream compilers (MSVC, GCC, and Clang). It has some limitations. For example, it cannot generate string representation for enums with same value.
enum overlapped
{
A = 1,
B = 1 // Not supported for this kind of enum value
};
Since the version 2.39, AngelScript supports enumerations with custom underlying types.
You can register them by enum_underlying,
which is an alias of enum_<Enum, std::underlying_type_t<Enum>>.
enum class enum_uint64 : std::uint64_t
{
A,
B = std::uint64_t(-1) // Larger than UINT32_MAX
};
asbind20::enum_underlying<enum_uint64>(engine, "enum_uint64")
.value(enum_uint64::A, "A")
.value(enum_uint64::B, "B");
Note
If you need to interact these enums from C++ side, the enums with custom underlying type need to specify the conversion rules.
Listener API
Warning
The listener API described in this section is experimental. Its interface may change in future releases.
Every binding generator (value_class, ref_class, global, enum_, interface) accepts an optional
Listener template parameter. When an entity is registered, the generator invokes the corresponding callback
on the listener, passing itself and the returned value from AngelScript API.
This allows you to:
Record what was registered for logging or debugging
Validate registered entities
Collect metadata about the bound interface
Defining a listener
A listener is a class that implements any subset of the following callback methods. Each is optional — if omitted, the call is silently skipped (no-op).
struct my_listener
{
// Global scope callbacks
void on_function(auto& gen, int func_id);
void on_global_property(auto& gen, int prop_id);
void on_funcdef(auto& gen, int funcdef_id);
void on_typedef(auto& gen, int type_id);
// Class/interface scope callbacks
void on_class(auto& gen, int type_id);
void on_interface(auto& gen, int type_id);
void on_method(auto& gen, int method_id);
// Enum callbacks
void on_enum(auto& gen, int type_id);
void on_enum_value(auto& gen, int value_id);
};
The gen parameter is a reference to the binding generator instance (e.g. global, value_class<T>).
Use it to query the engine, type info, or the generator’s type ID.
The int parameter is the AngelScript ID returned by the registration call:
Non-negative values indicate success.
By default, negative values will throw a
std::system_error. DefineASBIND20_CONFIG_NO_THROW_ON_BAD_BINDINGto suppress this.
Using a listener
Pass the listener as a template parameter and retrieve it with get_listener():
value_class<my_class, false, class_listener> c(engine, "my_class");
c.method("void foo()", &my_class::foo);
c.method("void bar() const", &my_class::bar);
auto& listener = c.get_listener();
// listener.recorded_class == ["my_class"]
// listener.recorded_method == ["foo", "bar"]
Listeners are default-constructed unless you pass an instance to the generator’s constructor:
my_listener pre_conf;
// ... configure pre_conf ...
global<false, my_listener> g(engine, std::move(pre_conf));
Appending to existing interface
Sometimes you need to register parts of an interface, class, or enum across multiple translation units or at different points in your program. The appending mechanism lets you add members to an already-registered type without re-registering it from scratch.
Two modes are available via tag types passed as the first constructor argument:
asbind20::appending— pure append. The type must already exist; the constructor will not register a new type.asbind20::try_appending— register if missing, append if present. If the type is not found, it is registered as if you used the normal constructor.
Appending is supported by value_class, ref_class, enum_, and interface.
Note that value_class only supports appending, not try_appending.
Interface appending
asbind20::interface(engine, "intf")
.method("int a()");
// Append another method to the existing interface
asbind20::interface(appending, engine, "intf")
.method("int b(int)");
Enum appending
asbind20::enum_<my_enum>(engine, "e")
.value(my_enum::A, "A");
// Append extra values to the existing enum
asbind20::enum_<my_enum>(appending, engine, "e")
.value(my_enum::B, "B");
Ref class appending
asbind20::ref_class<my_ref, true>(engine, "rc")
.addref(fp<&my_ref::addref>)
.release(fp<&my_ref::release>)
.default_factory()
.method("int get_data() const", fp<&my_ref::get_data>);
// Append property to the existing ref class
asbind20::ref_class<my_ref>(appending, engine, "rc")
.property("int data", &my_ref::data);
Value class appending
asbind20::value_class<my_val, true>(engine, "val")
.behaviours_by_traits()
.method("int get_data() const", fp<&my_val::get_data>);
// Append property to the existing value class
asbind20::value_class<my_val, true>(appending, engine, "val")
.property("int data", &my_val::data);
try_appending
Use try_appending when the type may or may not exist yet:
// Register the type if it doesn't exist; append if it does
asbind20::ref_class<my_ref, true>(try_appending, engine, "rc")
.addref(fp<&my_ref::addref>)
.release(fp<&my_ref::release>)
.default_factory()
.method("int get_data() const", fp<&my_ref::get_data>);
// Now use pure appending for the rest
asbind20::ref_class<my_ref>(appending, engine, "rc")
.property("int data", &my_ref::data);
Type consistency check
When appending, the library verifies that the existing type is compatible with the
binding generator being used (e.g., appending to a value type with ref_class
is rejected). Define ASBIND20_CONFIG_NO_APPEND_CHECK to disable these checks.