Skip to content

HenryAWE/asbind20

Repository files navigation

asbind20

Build

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.

Documentation

Full documentation is available on Read the Docs.

Brief Examples

1. Registering the Application Interface

Registering a Value Class

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));

Registering Global Functions

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.

2. Using AngelScript Objects from C++ Side

Invoking a Script Function

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);

Using a Script Class

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);

License

MIT License

About

C++20 AngelScript binding library

Topics

Resources

License

Stars

Watchers

Forks

Contributors 2

  •  
  •