8.4.22. C++ Integration: Namespace Integration

This tutorial shows how to initialize daslang modules when your code lives inside a C++ namespace.

Topics covered:

  • Why module registration entry points use C linkage

  • Using NEED_MODULE safely inside a namespace

  • DECLARE_MODULE / PULL_MODULE for separately compiled modules

8.4.22.1. Prerequisites

8.4.22.2. Global C-linkage entry points

REGISTER_MODULE exposes an unmangled global C entry point. For example, REGISTER_MODULE(Module_BuiltIn) provides:

extern "C" das::Module * register_Module_BuiltIn();

The built-in declarations are provided by daScriptModule.h. NEED_MODULE calls the global entry point explicitly, so it is safe inside a namespace:

namespace MyApp {
    void init() {
        NEED_ALL_DEFAULT_MODULES;
        das::Module::Initialize();
    }
}

8.4.22.3. DECLARE_MODULE / PULL_MODULE

Separately compiled custom modules are not known to daScriptModule.h. DECLARE_MODULE(ClassName) forward-declares their global C-linkage register_* function. It must be placed at file scope, outside any namespace.

PULL_MODULE(ClassName) — performs the registration call using the :: prefix to explicitly reference the global-scope function. Safe inside any namespace, class, or function body. Once a declaration is visible, NEED_MODULE and PULL_MODULE have the same pull behavior.

Convenience wrappers DECLARE_ALL_DEFAULT_MODULES and PULL_ALL_DEFAULT_MODULES remain available, although the built-in declarations are already supplied by the header.

8.4.22.4. The tutorial

The tutorial runs the same 01_hello_world.das script as Tutorial 1, but all daslang calls happen inside namespace MyApp.

tutorials/integration/cpp/22_namespace_integration.cpp
// Tutorial 22 — Namespace Integration
//
// Demonstrates how to initialize daslang modules inside a C++ namespace.
//
// Built-in registration entry points are globally declared with C linkage, so
// NEED_ALL_DEFAULT_MODULES is safe inside a namespace. Separately compiled
// custom modules use the DECLARE_MODULE / PULL_MODULE macro pair:
//   • DECLARE_MODULE  — forward-declares the register function (must be at
//                        file/global scope so it refers to the global symbol)
//   • PULL_MODULE     — calls the register function using ::qualified name
//                        (safe inside any namespace, class, or function body)
//
// NEED_MODULE and PULL_MODULE both call the global C-linkage entry point.

#include "daScript/daScript.h"

using namespace das;

#define SCRIPT_NAME "/tutorials/integration/cpp/01_hello_world.das"

// Separately compiled external modules are declared at global/file scope:
//   #include "modules/external_declare.inc"

// ------------------------------------------------------------------
// Everything below lives inside a namespace — PULL_MODULE works here.
// ------------------------------------------------------------------
namespace MyApp {

void runScript() {
    TextPrinter tout;
    ModuleGroup dummyLibGroup;
    auto fAccess = make_smart<FsFileAccess>();
    auto program = compileDaScript(getDasRoot() + SCRIPT_NAME,
                                   fAccess, tout, dummyLibGroup);
    if (program->failed()) {
        tout << "Compilation failed:\n";
        for (auto & err : program->errors) {
            tout << reportError(err.at, err.what, err.extra, err.fixme, err.cerr);
        }
        return;
    }
    Context ctx(program->getContextStackSize());
    if (!program->simulate(ctx, tout)) {
        tout << "Simulation failed:\n";
        for (auto & err : program->errors) {
            tout << reportError(err.at, err.what, err.extra, err.fixme, err.cerr);
        }
        return;
    }
    auto fnTest = ctx.findFunction("test");
    if (!fnTest) {
        tout << "Function 'test' not found\n";
        return;
    }
    ctx.evalWithCatch(fnTest, nullptr);
    if (auto ex = ctx.getException()) {
        tout << "Script exception: " << ex << "\n";
    }
}

void initialize() {
    // Built-ins are already globally declared by daScriptModule.h. The macro
    // calls their ::register_Module_* C entry points explicitly.
    NEED_ALL_DEFAULT_MODULES;

    // For external (plugin) modules, include the generated file:
    //   #include "modules/external_pull.inc"

    Module::Initialize();
}

void shutdown() {
    Module::Shutdown();
}

} // namespace MyApp

// main() delegates to the namespaced functions.
int main(int, char * []) {
    MyApp::initialize();
    MyApp::runScript();
    MyApp::shutdown();
    return 0;
}

8.4.22.5. How it works

  1. daScriptModule.h declares the default module entry points globally with C linkage.

  2. Inside MyApp::initialize(), NEED_ALL_DEFAULT_MODULES calls each ::register_Module_*() function explicitly.

  3. Module::Initialize() and the rest of the daslang API work normally inside the namespace.

8.4.22.6. Custom modules

For a separately compiled custom module, declare it at file scope before pulling it from a function or namespace:

DECLARE_MODULE(Module_MyMod);

namespace MyApp {
    void init() {
        NEED_ALL_DEFAULT_MODULES;
        PULL_MODULE(Module_MyMod);
        das::Module::Initialize();
    }
}

8.4.22.7. External (plugin) modules

CMake generates three .inc files for every module registered with ADD_MODULE_CPP:

external_need.inc

Contains NEED_MODULE(...) calls. The corresponding declarations must already be visible.

external_declare.inc

Contains DECLARE_MODULE(...) — include at file scope.

external_pull.inc

Contains PULL_MODULE(...) — include inside any namespace or function body.

The explicit declaration/pull pair is recommended:

// file scope
#include "modules/external_declare.inc"

namespace MyApp {
    void init() {
        // inside namespace
        #include "modules/external_pull.inc"
    }
}

8.4.22.8. Build and run

cmake --build build --config Release --target integration_cpp_22
$ bin/Release/integration_cpp_22
Hello, World!