7.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 NEED_MODULE fails inside a namespace

  • DECLARE_MODULE / PULL_MODULE — the namespace-safe alternative

  • DECLARE_ALL_DEFAULT_MODULES / PULL_ALL_DEFAULT_MODULES

7.4.22.1. Prerequisites

  • Tutorial 1 completed (tutorial_integration_cpp_hello_world) — basic compile → simulate → eval cycle.

7.4.22.2. The problem

The NEED_MODULE macro expands to an extern declaration followed by a call:

extern DAS_API das::Module * register_Module_BuiltIn();
*das::ModuleKarma += unsigned(intptr_t(register_Module_BuiltIn()));

When this macro is used inside a C++ namespace, the extern declaration is placed in that namespace’s scope. The linker then looks for MyApp::register_Module_BuiltIn() instead of the global ::register_Module_BuiltIn() defined in the library:

namespace MyApp {
    void init() {
        NEED_ALL_DEFAULT_MODULES;   // FAILS: linker error!
        das::Module::Initialize();
    }
}

The solution is to split the declaration and the call into two separate macros.

7.4.22.3. DECLARE_MODULE / PULL_MODULE

DECLARE_MODULE(ClassName) — forward-declares the global-scope register_* function. Must be placed at file or global 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.

Convenience wrappers DECLARE_ALL_DEFAULT_MODULES and PULL_ALL_DEFAULT_MODULES cover every built-in module.

7.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.
//
// The standard NEED_MODULE / NEED_ALL_DEFAULT_MODULES macros place an
// `extern` declaration at the current scope.  When that scope is a C++
// namespace, the linker looks for MyNamespace::register_Module_BuiltIn()
// instead of the global ::register_Module_BuiltIn(), causing link errors.
//
// The solution is 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)
//
// Convenience wrappers DECLARE_ALL_DEFAULT_MODULES and PULL_ALL_DEFAULT_MODULES
// cover all built-in modules at once.

#include "daScript/daScript.h"

using namespace das;

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

// Step 1: Forward-declare module functions at global/file scope.
// This ensures the extern declarations refer to the global-scope symbols
// defined by REGISTER_MODULE / REGISTER_MODULE_IN_NAMESPACE in the library.
DECLARE_ALL_DEFAULT_MODULES;

// For external (plugin) modules, include the generated file:
//   #include "modules/external_declare.inc"
// (mirrors external_need.inc but uses DECLARE_MODULE instead of NEED_MODULE)

// ------------------------------------------------------------------
// 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() {
    // Step 2: Pull (register) the modules.  PULL_ALL_DEFAULT_MODULES uses
    // ::register_Module_*() — the :: prefix ensures the call resolves to
    // the global-scope function regardless of the enclosing namespace.
    PULL_ALL_DEFAULT_MODULES;

    // For external (plugin) modules, include the generated file:
    //   #include "modules/external_pull.inc"
    // (mirrors external_need.inc but uses PULL_MODULE instead of NEED_MODULE)

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

7.4.22.5. How it works

  1. DECLARE_ALL_DEFAULT_MODULES at file scope forward-declares every default module’s register_* function as a global-scope symbol.

  2. Inside MyApp::initialize(), PULL_ALL_DEFAULT_MODULES calls each ::register_Module_*() function — the :: prefix bypasses the enclosing namespace.

  3. Module::Initialize() and the rest of the daslang API continue to work normally inside the namespace — only module registration has the namespace restriction.

7.4.22.6. Custom modules

For custom modules, use DECLARE_MODULE at file scope alongside the convenience macros:

DECLARE_ALL_DEFAULT_MODULES;
DECLARE_MODULE(Module_MyMod);

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

7.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(...) — the traditional all-in-one macro (works only at global scope).

external_declare.inc

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

external_pull.inc

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

For namespace-safe code, replace:

// old (global scope only)
#include "modules/external_need.inc"

with:

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

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

7.4.22.8. Build and run

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