C++20 AngelScript binding library powered by templates. It also provides tools for easy interoperability between the AngelScript and C++.
The name and API design are inspired by the famous pybind11 library.
class my_value_class
{
public:
my_value_class() = default;
my_value_class(const my_value_class&) = default;
my_value_class(int val)
: value(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)
{
return lhs.value == rhs.value;
}
friend std::strong_ordering operator<=>(const my_value_class& lhs, const my_value_class& rhs)
{
return lhs.value <=> rhs.value;
}
int get_val() const
{
return value;
}
void set_val(int new_val)
{
value = new_val;
}
int value = 0;
};
void add_obj_last(int val, my_value_class* this_)
{
this_->value += val;
}
void mul_obj_first(my_value_class* this_, int val)
{
this_->value *= val;
}
void set_val_gen(asIScriptGeneric* gen)
{
my_value_class* this_ = (my_value_class*)gen->GetObject();
int val = static_cast<int>(gen->GetArgDWord(0));
this_->set_val(val);
}
Binding code
asIScriptEngine* engine = /* Get a script engine */;
asbind20::value_class<my_value_class>(
engine, "my_value_class", asOBJ_APP_CLASS_CDAK | asOBJ_APP_CLASS_ALLINTS | asOBJ_APP_CLASS_MORE_CONSTRUCTORS
)
// Default & copy constructor, destructor,
// and assignment operator (operator=/opAssign)
.common_behaviours()
// The constructor my_value_class::my_value_class(int val)
.constructor<int>("void f(int val)")
// Generate opEquals for AngelScript using operator== in C++
.opEquals()
// Generate opCmp for AngelScript using operator<=> in C++
.opCmp()
// Ordinary member functions
.method("int get_val() const", &my_value_class::get_val)
// Automatically deducing calling conventions
.method("void add(int val)", &add_obj_last) // asCALL_CDECL_OBJLAST
.method("void mul(int val)", &mul_obj_first) // asCALL_CDECL_OBFIRST
.method("void set_val(int)", &set_val_gen) // asCALL_GENERIC
// Convert member pointer to property
.property("int value", &my_value_class::value);
The binding helpers also support registering a reference type, an interface, or global functions, etc. to the AngelScript engine.
You can find more examples in src/ext/array.cpp
and src/ext/stdstring.cpp
.
The library can automatically convert arguments in C++ for invoking an AngelScript function. Besides, the library provides RAII classes for easily managing lifetime of AngelScript object like asIScriptContext
.
- AngelScript side
string test(int a, int&out b)
{
b = a + 1;
return "test";
}
- C++ side
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 RAII
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);
You can find more examples in test/test_invoke.cpp
.
The library provides tools for instantiating a script class. The script_invoke
also supports invoking a method, a.k.a., member function.
Script class 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);
The asbind20 library also provides tools for advanced users. You can find detailed examples in extensions and unit tests.
Generating generic wrapper by macro-free utilities.
// generic_wrapper<MyFunction, OriginalCallConv>();
asGENFUNC_t f1 = asbind20::generic_wrapper<&global_func, asCALL_CDECL>();
asGENFUNC_T f2 = asbind20::generic_wrapper<&my_class::method, asCALL_THISCALL>();
// Use `use_generic` tag to force generated proxies to use asCALL_GENERIC
asbind20::value_class<my_value_class>(...)
.constructor<int>(asbind20::use_generic, "void f(int)")
.opEquals(asbind20::use_generic);
This feature is similar to how std::visit
and std::variant
works. It can be used for developing templated container for AngelScript.
asbind20::visit_primitive_type(
[](auto val)
{
using type = decltype(val);
if constexpr(std::is_same_v<type, int>)
{
// play with int
}
else if constexpr(std::is_same_v<type, float>)
{
// play with float
}
else
{
// ...
}
},
as_type_id, // asTYPEID_BOOL, asTYPEID_INT32, etc.
ptr_to_val // void* to value
);
You can find example usage in src/ext/array.cpp
.
- CMake >= 3.20
- AngelScript >= 2.37.0
- Any C++ compiler that supports C++20.
Currently, this library has been tested by MSVC 19.41 on Windows and GCC 12 on Linux.
Follow the tutorial of AngelScript to build and install it at first, or use a package manager like vcpkg.
Build and install the library.
cmake -GNinja -DCMAKE_BUILD_TYPE=Release -S. -B build
cmake --build build
cmake --install build
Use the library in a CMakeLists.txt
.
find_package(asbind20 REQUIRED)
target_link_libraries(main PRIVATE asbind20::asbind20)
You can find a detailed example in test/test_install/
.
Clone the library into your project.
git clone https://github.com/HenryAWE/asbind20.git
Use the library in a CMakeLists.txt
.
add_subdirectory(asbind20)
target_link_libraries(main PRIVATE asbind20::asbind20)
You can find a detailed example in test/test_subdir/
.