5.3.5. C++ Integration: Binding Enumerations

This tutorial shows how to expose C++ enum and enum class types to daslang. Topics covered:

  • DAS_BASE_BIND_ENUM — the macro approach to enum binding

  • DAS_BIND_ENUM_CAST — cast specialization (when needed)

  • addEnumeration — registering enums in a module

  • Manual Enumeration construction — an alternative to macros

  • Using bound enums in addExtern functions

5.3.5.1. Prerequisites

  • Tutorial 04 completed (tutorial_integration_cpp_binding_types).

  • Comfort with MAKE_TYPE_FACTORY and addExtern.

5.3.5.2. Defining the C++ enums

Both scoped (enum class) and unscoped (enum) enums can be bound. This tutorial uses scoped enums — the modern C++ style:

enum class Direction : int {
    North = 0,
    East  = 1,
    South = 2,
    West  = 3
};

enum class Severity : int {
    Debug   = 0,
    Info    = 1,
    Warning = 2,
    Error   = 3
};

5.3.5.3. DAS_BASE_BIND_ENUM

The primary macro for binding enums. It creates:

  • A class Enumeration<DasName> that describes the enum’s values

  • A typeFactory<CppEnum> so that addExtern can resolve the type

DAS_BASE_BIND_ENUM(Direction, Direction,
    North,
    East,
    South,
    West
)

DAS_BASE_BIND_ENUM(Severity, Severity,
    Debug,
    Info,
    Warning,
    Error
)

The first argument is the C++ enum type, the second is the daslang name, and the remaining arguments are the enum values. The generated class names follow the pattern Enumeration<DasName> — e.g. EnumerationDirection.

Note

Place DAS_BASE_BIND_ENUM macros before using namespace das. The macros define names inside namespace das that can collide with the global enum names if both namespaces are active.

For unscoped (C-style) enums, use DAS_BASE_BIND_ENUM_98 instead.

5.3.5.4. DAS_BIND_ENUM_CAST

This macro creates a cast<> specialization that lets enum values cross the C++ / daslang boundary. In many cases the SFINAE default in the engine already handles this, but you may need it for more complex enum types:

DAS_BIND_ENUM_CAST(Direction)

5.3.5.5. Registering enums in the module

In the module constructor, call addEnumeration with the generated class:

addEnumeration(make_smart<EnumerationDirection>());
addEnumeration(make_smart<EnumerationSeverity>());

5.3.5.6. Manual enum construction

When the macros don’t fit (e.g. you need to rename values, skip some, or the enum lives in a deeply nested namespace), you can construct an Enumeration object by hand:

auto pEnum = make_smart<Enumeration>("Severity");
pEnum->cppName  = "Severity";
pEnum->external = true;
pEnum->baseType = Type::tInt;
pEnum->addIEx("Debug",   "Severity::Debug",   0, LineInfo());
pEnum->addIEx("Info",    "Severity::Info",     1, LineInfo());
pEnum->addIEx("Warning", "Severity::Warning",  2, LineInfo());
pEnum->addIEx("Error",   "Severity::Error",    3, LineInfo());
addEnumeration(pEnum);

You still need DAS_BASE_BIND_ENUM (or at least DAS_BIND_ENUM_CAST) for the typeFactory<> so that addExtern can match the type.

5.3.5.7. Binding functions that use enums

Once the enum type is registered, addExtern handles enum parameters and return values automatically — no special treatment is required:

Direction opposite_direction(Direction d) {
    switch (d) {
        case Direction::North: return Direction::South;
        case Direction::South: return Direction::North;
        case Direction::East:  return Direction::West;
        case Direction::West:  return Direction::East;
        default:               return d;
    }
}

addExtern<DAS_BIND_FUN(opposite_direction)>(*this, lib, "opposite_direction",
    SideEffects::none, "opposite_direction")
        ->args({"d"});

5.3.5.8. Using bound enums in daslang

Enum values are accessed with dot syntax — EnumName.Value:

require tutorial_05_cpp

[export]
def test() {
    let dir = Direction.North
    print("dir = {direction_name(dir)}\n")
    print("opposite = {direction_name(opposite_direction(dir))}\n")

    // Enum comparison
    let east = Direction.East
    let west = Direction.West
    print("opposite(East) == West? {opposite_direction(east) == west}\n")

    // Passing enums to C++ functions
    log_message(Severity.Warning, "disk space low")

    // Boolean result from enum logic
    print("Warning is severe? {is_severe(Severity.Warning)}\n")

5.3.5.9. Name collision warning

The daslang engine defines some enum names internally (e.g. das::LogLevel in string_writer.h). If your C++ enum has the same name as an engine-internal enum and you use using namespace das, you will get ambiguous symbol errors. Rename your enum to avoid the collision.

5.3.5.10. Building and running

cmake --build build --config Release --target integration_cpp_05
bin\Release\integration_cpp_05.exe

Expected output:

=== Direction enum ===
dir = North
opposite of North = South
Rotating CW: North East South West
East == West? false
opposite(East) == West? true

=== Severity enum ===
[DEBUG] system starting up
[INFO] ready to serve
[WARN] disk space low
[ERROR] connection lost
Debug is severe?   false
Warning is severe? true
Error is severe?   true

See also

Full source: 05_binding_enums.cpp, 05_binding_enums.das

Previous tutorial: tutorial_integration_cpp_binding_types

Next tutorial: tutorial_integration_cpp_interop