asbind20 is a C++20 AngelScript binding library powered by templates.
Although the interface of AngelScript is very friendly to C++ comparing to most binding APIs from other script languages, it still needs many additional proxy functions, e.g., when binding a constructor. However, most of these proxies have similar logic, so they can be generated by some template tricks.
Besides, this library provides tools for easily calling a script function, as well as RAII helper for managing lifetime of AngelScript objects.
In general, this library aims to automate everything that can be generated by template meta-programming.
The name and API design are inspired by the famous pybind11 library.
Full documentation is available on Read the Docs.
Declaration of the C++ class:
class my_value_class
{
public:
my_value_class() = default;
my_value_class(const my_value_class&) = default;
explicit my_value_class(int val);
~my_value_class() = default;
my_value_class& operator=(const my_value_class&) = default;
friend bool operator==(
const my_value_class& lhs, const my_value_class& rhs
);
friend std::strong_ordering operator<=>(
const my_value_class& lhs, const my_value_class& rhs
);
int get_val() const;
void set_val(int new_val);
// Interfaces which are designed to be found by ADL
friend void process(my_value_class& val);
// Operator overloads
friend my_value_class operator+(const my_value_class& lhs, int rhs);
friend my_value_class operator+(int lhs, const my_value_class& rhs);
int value = 0;
int another_value = 0;
};
// Wrapper functions extending the interface of C++ class,
// without actually changing its implementation.
void mul_obj_first(my_value_class* this_, int val);
void add_obj_last(int val, my_value_class* this_);
void my_op_gen(asIScriptGeneric* gen);
Register the C++ class to the AngelScript engine:
asIScriptEngine* engine = /* Get a script engine */;
asbind20::value_class<my_value_class>(
engine,
"my_value_class",
// Other flags will be automatically set using asGetTypeTraits<T>()
asOBJ_APP_CLASS_ALLINTS | asOBJ_APP_CLASS_MORE_CONSTRUCTORS
)
// Generate & register the default constructor, copy constructor, destructor,
// and assignment operator (operator=/opAssign) based on type traits
.behaviours_by_traits()
// The constructor `my_value_class::my_value_class(int val)`
// The tag `use_explicit` indicates an explicit constructor.
.constructor<int>("int val", asbind20::use_explicit)
// Generate opEquals for AngelScript using operator== in C++
.opEquals()
// Generate opCmp for AngelScript using operator<=> in C++,
// translating comparison result from the C++ enum to int value for AS.
.opCmp()
// Ordinary member functions
.method("int get_val() const", &my_value_class::get_val)
.method("void set_val(int new_val) const", &my_value_class::set_val)
// Automatically deducing calling conventions of wrapper functions
.method("void mul(int val)", &mul_obj_first) // asCALL_CDECL_OBJFIRST
.method("void add(int val)", &add_obj_last) // asCALL_CDECL_OBJLAST
.method("void my_op(int)", &set_val_gen) // asCALL_GENERIC
// Use lambda for a function which is designed to be found by ADL
.method(
"void process()",
[](my_value_class& val) -> void { process(val); }
)
// Operator overloads
.use(const_this + param<int>)
.use(param<int> + const_this)
// Register property by member pointer.
.property("int value", &my_value_class::value)
// Register property by offset.
.property("int another_value", offsetof(my_value_class, another_value));
Given some global functions:
float sin(float x);
float cos(float x);
void gfn(asIScriptGeneric* gen);
Register them to the AngelScript engine:
asIScriptEngine* engine = /* Get a script engine */;
asbind20::global(engine)
.function("float sin(float x)", &sin)
.function("float cos(float x)", &cos)
.function("int gen(int arg)", &gfn);
The binding generators also support registering a reference type, an interface, etc. to the AngelScript engine. In addition, it can generate generic wrappers for those things at compile-time, which is useful for platform without native calling convention support, e.g., Emscripten.
Please read the documentation for more information.
This library can automatically convert arguments in C++ for invoking an AngelScript function. Besides, the library provides RAII helpers for easily managing lifetime of AngelScript objects like asIScriptContext*
.
AngelScript function:
string test(int a, int&out b)
{
b = a + 1;
return "test";
}
C++ code:
asIScriptEngine* engine = /* Get a script engine */;
asIScriptModule* m = /* Build the above script */;
asIScriptFunction* func = m->GetFunctionByName("test");
if(!func)
/* Error handling */
// Manage script context using the RAII helper
asbind20::request_context ctx(engine);
int val = 0;
auto result = asbind20::script_invoke<std::string>(
ctx, func, 1, std::ref(val)
);
assert(result.value() == "test");
assert(val == 2);
The library provides tools for instantiating a script class. The script_invoke
also supports invoking a method, a.k.a., member function.
The script class defined in AngelScript:
class my_class
{
int m_val;
void set_val(int new_val) { m_val = new_val; }
int get_val() const { return m_val; }
int& get_val_ref() { return m_val; }
};
C++ code:
asIScriptEngine* engine = /* Get a script engine */;
asIScriptModule* m = /* Build the above script */;
asITypeInfo* my_class_t = m->GetTypeInfoByName("my_class");
asbind20::request_context ctx(engine);
auto my_class = asbind20::instantiate_class(ctx, my_class_t);
asIScriptFunction* set_val = my_class_t->GetMethodByDecl("void set_val(int)");
asbind20::script_invoke<void>(ctx, my_class, set_val, 182375);
asIScriptFunction* get_val = my_class_t->GetMethodByDecl("int get_val() const");
auto val = asbind20::script_invoke<int>(ctx, my_class, get_val);
assert(val.value() == 182375);
asIScriptFunction* get_val_ref = my_class_t->GetMethodByDecl("int& get_val_ref()");
auto val_ref = asbind20::script_invoke<int&>(ctx, my_class, get_val_ref);
assert(val_ref.value() == 182375);
*val_ref = 182376;
val = asbind20::script_invoke<int>(ctx, my_class, get_val);
assert(val.value() == 182376);