7. Class operators

7. Class operators #

Operators can be added to your custom types, this is done via the wren::ForeignMethodOperator enumeration.

This is a list of all supported operators by WrenBind17:

Operator Enum Value
Add (+) OPERATOR_ADD
Subtract (-) OPERATOR_SUB
Multiply (*) OPERATOR_MUL
Divide (/) OPERATOR_DIV
Unary negative (-) OPERATOR_NEG
Modulo (%) OPERATOR_MOD
Equal to (==) OPERATOR_EQUAL
Not equal to (!=) OPERATOR_NOT_EQUAL
Greater than (>) OPERATOR_GT
Less than (<) OPERATOR_LT
Greather than or equal (>=) OPERATOR_GT_EQUAL
Less than or equal (<=) OPERATOR_LT_EQUAL
Shift left («) OPERATOR_SHIFT_LEFT
Shift right (») OPERATOR_SHIFT_RIGHT
Binary and (&) OPERATOR_AND
Binary xor (^) OPERATOR_XOR
Binary or (|) OPERATOR_OR
Get by index [] OPERATOR_GET_INDEX
Set by index [] OPERATOR_SET_INDEX

7.1. Basic usage #

Adding arithemtic or comparison operators can be done in the following way.

class Vec3 {
public:
    Vec3(float x, float y, float z) : x(x), y(y), z(z) {
    }

    Vec3 operator - () const { // Unary negation operator
        ...
    }

    Vec3 operator + (const Vec3& other) const {
        ...
    }

    Vec3 operator - (const Vec3& other) const {
        ...
    }

    Vec3 operator * (const Vec3& other) const {
        ...
    }

    Vec3 operator / (const Vec3& other) const {
        ...
    }

    bool operator == (const Vec3& other) const {
        ...
    }

    bool operator != (const Vec3& other) const {
        ...
    }

    float x;
    float y;
    float z;
};

int main(...) {
    wren::VM vm;
    ...

    // If you don't do this, the compiler will have no idea
    // which operator to use when binding OPERATOR_SUB and OPERATOR_NEG.
    // With this and static_cast you will explicitly tell the compiler
    // which exact function to use.
    typedef Vec3 (Vec3::*Vec3Sub)(const Vec3&) const;
    typedef Vec3 (Vec3::*Vec3Neg)() const;

    // Bind the class and some basic functions/vars
    auto& cls = m.klass<Vec3>("Vec3");
    cls.ctor<float, float, float>(); // Constructor
    cls.var<&Vec3::x>("x");
    cls.var<&Vec3::y>("y");
    cls.var<&Vec3::z>("z");

    // Bind the operators
    cls.func<&Vec3::operator+ >(wren::OPERATOR_ADD);
    cls.func<static_cast<Vec3Sub>(&Vec3::operator-)>(wren::OPERATOR_SUB);
    cls.func<static_cast<Vec3Neg>(&Vec3::operator-)>(wren::OPERATOR_NEG);
    cls.func<&Vec3::operator* >(wren::OPERATOR_MUL);
    cls.func<&Vec3::operator/ >(wren::OPERATOR_DIV);
    cls.func<&Vec3::operator== >(wren::OPERATOR_EQUAL);
    cls.func<&Vec3::operator!= >(wren::OPERATOR_NOT_EQUAL);
}

Afterwards you can use it in the following way:

import "test" for Vec3
var a = Vec3.new(1.0, 2.0, 3.0)
var b = Vec3.new(4.0, 5.0, 6.0)
var c = a + b

Note

If you are using Visual Studio and trying to bind operator < then you might get an error: error C2833: 'operator >' is not a recognized operator or type. This happens because the compiler is unable to understand cls.func<&Vec3::operator>>(wren::OPERATOR_GT). Simply, put ( ) around the operator like this: cls.func<(&Vec3::operator>)>(...).

Note

Using *=, -=, +=, or /= is not allowed. Wren does not support these assignment operators and results in Wren compilation error.

7.2. Operator with multiple types #

Consider the following C++ class:

class Vec3 {
public:
    Vec3 operator * (const Vec3& other) const {
        ...
    }

    Vec3 operator * (const float value) const {
        ...
    }
};

You have two operators but the second one only accepts a single value. This can be useful when you want to, for example, multiply a 3D vector with a constant value. This can be a problem when binding these two operators to Wren. You can’t bind them both, but you can use std::variant<> instead.

Create a new function in the following way:

Vec3 operator * (const std::variant<Vec3, float>& var) const {
    if (var.index() == 0) {
        Vec3 other = std::get<Vec3>(var);
        // Multiply by other vector
    } else {
        float other = std::get<float>(var);
        // Multiply by constant value
    }
}

int main(...) {
    wren::VM vm;
    ...

    auto& cls = m.klass<Vec3>("Vec3");
    cls.ctor<float, float, float>();
    cls.var<&Vec3::x>("x");
    cls.var<&Vec3::y>("y");
    cls.var<&Vec3::z>("z");

    // Optional typedef to explicitly select the correct 
    // operator with the std::variant
    typedef Vec3 (Vec3::*Vec3Mul)(const std::variant<Vec3, float>&) const;
    // Bind the function
    cls.func<static_cast<Vec3Mul>(&Vec3::operator*)>(wren::OPERATOR_MUL);
}

Then, insie of Wren, you can do the following:

import "test" for Vec3
var a = Vec3.new(1.0, 2.0, 3.0)
var b = Vec3.new(4.0, 5.0, 6.0)

// Multiply by the other vector
var c = a + b

// Or multiply by a constant value
var c = a * 1.5