Program Listing for File curve_rtc.cpp

Return to documentation for file (src/flamegpu/runtime/detail/curve/curve_rtc.cpp)

#include <sstream>

#include "flamegpu/runtime/detail/curve/curve_rtc.cuh"
#include "flamegpu/exception/FLAMEGPUException.h"
#include "flamegpu/simulation/detail/CUDAErrorChecking.cuh"
#include "flamegpu/simulation/detail/EnvironmentManager.cuh"
#include "flamegpu/detail/cuda.cuh"

// jitify include for demangle
#ifdef _MSC_VER
#pragma warning(push, 2)
#include "jitify/jitify.hpp"
#pragma warning(pop)
#else
#include "jitify/jitify.hpp"
#endif

namespace flamegpu {
namespace detail {
namespace curve {


const char* CurveRTCHost::curve_rtc_dynamic_h_template = R"###(dynamic/curve_rtc_dynamic.h
#line 1 "$FILENAME"
#ifndef CURVE_RTC_DYNAMIC_H_
#define CURVE_RTC_DYNAMIC_H_

#include "flamegpu/exception/FLAMEGPUDeviceException.cuh"
#include "flamegpu/detail/type_decode.h"
#include "flamegpu/runtime/detail/curve/Curve.cuh"
#include "flamegpu/util/dstring.h"

namespace flamegpu {

template <unsigned int N, unsigned int I> struct StringCompare {
    __device__ inline static bool strings_equal_loop(const char(&a)[N], const char(&b)[N]) {
        return a[N - I] == b[N - I] && StringCompare<N, I - 1>::strings_equal_loop(a, b);
    }
};

template <unsigned int N> struct StringCompare<N, 1> {
    __device__ inline static bool strings_equal_loop(const char(&a)[N], const char(&b)[N]) {
        return a[0] == b[0];
    }
};

template <unsigned int N>
__device__ bool strings_equal(const char(&a)[N], const char(&b)[N]) {
    return StringCompare<N, N>::strings_equal_loop(a, b);
}

template <unsigned int N, unsigned int M>
__device__ bool strings_equal(const char(&a)[N], const char(&b)[M]) {
    return false;
}

namespace detail {
namespace curve {

$DYNAMIC_VARIABLES

class DeviceCurve {
    public:
    static const int UNKNOWN_VARIABLE = -1;
    static const unsigned int UNKNOWN_GRAPH = 0xFFFFFFFF;  // UINT_MAX

    typedef int                      Variable;
    typedef unsigned int             VariableHash;
    typedef unsigned int             NamespaceHash;

    template <typename T, unsigned int N>
    __device__ __forceinline__ static T getAgentVariable(const char(&name)[N], unsigned int index);
    template <typename T, unsigned int N>
    __device__ __forceinline__ static T getMessageVariable(const char(&name)[N], unsigned int index);

    template <typename T, unsigned int N>
    __device__ __forceinline__ static T getAgentVariable_ldg(const char(&name)[N], unsigned int index);
    template <typename T, unsigned int N>
    __device__ __forceinline__ static T getMessageVariable_ldg(const char(&name)[N], unsigned int index);

    template <typename T, unsigned int N, unsigned int M>
    __device__ __forceinline__ static T getAgentArrayVariable(const char(&name)[M], unsigned int variable_index, unsigned int array_index);
    template <typename T, unsigned int N, unsigned int M>
    __device__ __forceinline__ static T getMessageArrayVariable(const char(&name)[M], unsigned int variable_index, unsigned int array_index);

    template <typename T, unsigned int N, unsigned int M>
    __device__ __forceinline__ static T getAgentArrayVariable_ldg(const char(&name)[M], unsigned int variable_index, unsigned int array_index);
    template <typename T, unsigned int N, unsigned int M>
    __device__ __forceinline__ static T getMessageArrayVariable_ldg(const char(&name)[M], unsigned int variable_index, unsigned int array_index);

    template <typename T, unsigned int N>
    __device__ __forceinline__ static void setAgentVariable(const char(&name)[N], T variable, unsigned int index);
    template <typename T, unsigned int N>
    __device__ __forceinline__ static void setMessageVariable(const char(&name)[N], T variable, unsigned int index);
    template <typename T, unsigned int N>
    __device__ __forceinline__ static void setNewAgentVariable(const char(&name)[N], T variable, unsigned int index);

    template <typename T, unsigned int N, unsigned int M>
    __device__ __forceinline__ static void setAgentArrayVariable(const char(&name)[M], T variable, unsigned int variable_index, unsigned int array_index);
    template <typename T, unsigned int N, unsigned int M>
    __device__ __forceinline__ static void setMessageArrayVariable(const char(&name)[M], T variable, unsigned int variable_index, unsigned int array_index);
    template <typename T, unsigned int N, unsigned int M>
    __device__ __forceinline__ static void setNewAgentArrayVariable(const char(&name)[M], T variable, unsigned int variable_index, unsigned int array_index);

    __device__ __forceinline__ static unsigned int *getEnvironmentDirectedGraphPBM(VariableHash graphHash);
    __device__ __forceinline__ static unsigned int *getEnvironmentDirectedGraphIPBM(VariableHash graphHash);
    __device__ __forceinline__ static unsigned int *getEnvironmentDirectedGraphIPBMEdges(VariableHash graphHash);

    template <typename T, unsigned int M>
    __device__ __forceinline__ static T getEnvironmentDirectedGraphVertexProperty(VariableHash graphHash, const char(&propertyName)[M], unsigned int vertex_index);
    template <typename T, unsigned int N, unsigned int M>
    __device__ __forceinline__ static T getEnvironmentDirectedGraphVertexArrayProperty(VariableHash graphHash, const char(&propertyName)[M], unsigned int vertex_index, unsigned int array_index);

    template <typename T, unsigned int M>
    __device__ __forceinline__ static T getEnvironmentDirectedGraphEdgeProperty(VariableHash graphHash, const char(&propertyName)[M], unsigned int edge_index);
    template <typename T, unsigned int N, unsigned int M>
    __device__ __forceinline__ static T getEnvironmentDirectedGraphEdgeArrayProperty(VariableHash graphHash, const char(&propertyName)[M], unsigned int edge_index, unsigned int array_index);

    template <unsigned int M>
    __device__ __forceinline__ static VariableHash getGraphHash(const char(&graphName)[M]);

    template <unsigned int M>
    __device__ __forceinline__ static unsigned int getVariableCount(const char(&variableName)[M], const VariableHash namespace_hash);

    __device__ __forceinline__ static bool isAgent(const char* agent_name);
    __device__ __forceinline__ static bool isState(const char* agent_state);
};

template <typename T, unsigned int N>
__device__ __forceinline__ T DeviceCurve::getAgentVariable(const char (&name)[N], unsigned int index) {
$DYNAMIC_GETAGENTVARIABLE_IMPL
}
template <typename T, unsigned int N>
__device__ __forceinline__ T DeviceCurve::getMessageVariable(const char (&name)[N], unsigned int index) {
$DYNAMIC_GETMESSAGEVARIABLE_IMPL
}

template <typename T, unsigned int N>
__device__ __forceinline__ T DeviceCurve::getEnvironmentDirectedGraphVertexProperty(VariableHash graphHash, const char(&name)[N], unsigned int index) {
$DYNAMIC_GETDIRECTEDGRAPHVERTEXPROPERTY_IMPL
}
template <typename T, unsigned int N>
__device__ __forceinline__ T DeviceCurve::getEnvironmentDirectedGraphEdgeProperty(VariableHash graphHash, const char(&name)[N], unsigned int index) {
$DYNAMIC_GETDIRECTEDGRAPHEDGEPROPERTY_IMPL
}

template <typename T, unsigned int N>
__device__ __forceinline__ T DeviceCurve::getAgentVariable_ldg(const char (&name)[N], unsigned int index) {
$DYNAMIC_GETAGENTVARIABLE_LDG_IMPL
}
template <typename T, unsigned int N>
__device__ __forceinline__ T DeviceCurve::getMessageVariable_ldg(const char (&name)[N], unsigned int index) {
$DYNAMIC_GETMESSAGEVARIABLE_LDG_IMPL
}

template <typename T, unsigned int N, unsigned int M>
__device__ __forceinline__ T DeviceCurve::getAgentArrayVariable(const char(&name)[M], unsigned int index, unsigned int array_index) {
$DYNAMIC_GETAGENTARRAYVARIABLE_IMPL
}
template <typename T, unsigned int N, unsigned int M>
__device__ __forceinline__ T DeviceCurve::getMessageArrayVariable(const char(&name)[M], unsigned int index, unsigned int array_index) {
$DYNAMIC_GETMESSAGEARRAYVARIABLE_IMPL
}


__device__ __forceinline__ unsigned int *DeviceCurve::getEnvironmentDirectedGraphPBM(VariableHash graphHash) {
$DYNAMIC_GETDIRECTEDGRAPHPBM_IMPL
}
__device__ __forceinline__ unsigned int *DeviceCurve::getEnvironmentDirectedGraphIPBM(VariableHash graphHash) {
$DYNAMIC_GETDIRECTEDGRAPHIPBM_IMPL
}
__device__ __forceinline__ unsigned int *DeviceCurve::getEnvironmentDirectedGraphIPBMEdges(VariableHash graphHash) {
$DYNAMIC_GETDIRECTEDGRAPHIPBMEDGES_IMPL
}
template <typename T, unsigned int N, unsigned int M>
__device__ __forceinline__ T DeviceCurve::getEnvironmentDirectedGraphVertexArrayProperty(VariableHash graphHash, const char(&name)[M], unsigned int index, unsigned int array_index) {
$DYNAMIC_GETDIRECTEDGRAPHVERTEXARRAYPROPERTY_IMPL
}
template <typename T, unsigned int N, unsigned int M>
__device__ __forceinline__ T DeviceCurve::getEnvironmentDirectedGraphEdgeArrayProperty(VariableHash graphHash, const char(&name)[M], unsigned int index, unsigned int array_index) {
$DYNAMIC_GETDIRECTEDGRAPHEDGEARRAYPROPERTY_IMPL
}

template <typename T, unsigned int N, unsigned int M>
__device__ __forceinline__ T DeviceCurve::getAgentArrayVariable_ldg(const char(&name)[M], unsigned int index, unsigned int array_index) {
$DYNAMIC_GETAGENTARRAYVARIABLE_LDG_IMPL
}
template <typename T, unsigned int N, unsigned int M>
__device__ __forceinline__ T DeviceCurve::getMessageArrayVariable_ldg(const char(&name)[M], unsigned int index, unsigned int array_index) {
$DYNAMIC_GETMESSAGEARRAYVARIABLE_LDG_IMPL
}

template <typename T, unsigned int N>
__device__ __forceinline__ void DeviceCurve::setAgentVariable(const char(&name)[N], T variable, unsigned int index) {
$DYNAMIC_SETAGENTVARIABLE_IMPL
}
template <typename T, unsigned int N>
__device__ __forceinline__ void DeviceCurve::setMessageVariable(const char(&name)[N], T variable, unsigned int index) {
$DYNAMIC_SETMESSAGEVARIABLE_IMPL
}
template <typename T, unsigned int N>
__device__ __forceinline__ void DeviceCurve::setNewAgentVariable(const char(&name)[N], T variable, unsigned int index) {
$DYNAMIC_SETNEWAGENTVARIABLE_IMPL
}

template <typename T, unsigned int N, unsigned int M>
__device__ __forceinline__ void DeviceCurve::setAgentArrayVariable(const char(&name)[M], T variable, unsigned int index, unsigned int array_index) {
$DYNAMIC_SETAGENTARRAYVARIABLE_IMPL
}
template <typename T, unsigned int N, unsigned int M>
__device__ __forceinline__ void DeviceCurve::setMessageArrayVariable(const char(&name)[M], T variable, unsigned int index, unsigned int array_index) {
$DYNAMIC_SETMESSAGEARRAYVARIABLE_IMPL
}
template <typename T, unsigned int N, unsigned int M>
__device__ __forceinline__ void DeviceCurve::setNewAgentArrayVariable(const char(&name)[M], T variable, unsigned int index, unsigned int array_index) {
$DYNAMIC_SETNEWAGENTARRAYVARIABLE_IMPL
}

template <unsigned int M>
__device__ __forceinline__ DeviceCurve::VariableHash DeviceCurve::getGraphHash(const char(&graphName)[M]) {
$DYNAMIC_GETGRAPHHASH_IMPL
}

template <unsigned int M>
__device__ __forceinline__ unsigned int DeviceCurve::getVariableCount(const char(&variableName)[M], const VariableHash namespace_hash) {
$DYNAMIC_GETVARIABLECOUNT_IMPL
}

__device__ __forceinline__ bool DeviceCurve::isAgent(const char* agent_name) {
    return util::dstrcmp(agent_name, "$DYNAMIC_AGENT_NAME") == 0;
}
__device__ __forceinline__ bool DeviceCurve::isState(const char* agent_state) {
    return util::dstrcmp(agent_state, "$DYNAMIC_AGENT_STATE") == 0;
}

}  // namespace curve
}  // namespace detail
}  // namespace flamegpu

// has to be included after definition of curve namespace
#include "flamegpu/runtime/environment/DeviceEnvironment.cuh"
//#include "flamegpu/runtime/environment/DeviceMacroProperty.cuh"

namespace flamegpu {

template<typename T, unsigned int M>
__device__ __forceinline__ T ReadOnlyDeviceEnvironment::getProperty(const char(&name)[M]) const {
$DYNAMIC_ENV_GETVARIABLE_IMPL
}

template<typename T, unsigned int N, unsigned int M>
__device__ __forceinline__ T ReadOnlyDeviceEnvironment::getProperty(const char(&name)[M], const unsigned int index) const {
$DYNAMIC_ENV_GETARRAYVARIABLE_IMPL
}


template<typename T, unsigned int I, unsigned int J, unsigned int K, unsigned int W, unsigned int N>
__device__ __forceinline__ ReadOnlyDeviceMacroProperty<T, I, J, K, W> ReadOnlyDeviceEnvironment::getMacroProperty(const char(&name)[N]) const {
$DYNAMIC_ENV_GETREADONLYMACROPROPERTY_IMPL
}
template<typename T, unsigned int I, unsigned int J, unsigned int K, unsigned int W, unsigned int N>
__device__ __forceinline__ DeviceMacroProperty<T, I, J, K, W> DeviceEnvironment::getMacroProperty(const char(&name)[N]) const {
$DYNAMIC_ENV_GETMACROPROPERTY_IMPL
}

}  // namespace flamegpu

#endif  // CURVE_RTC_DYNAMIC_H_
)###";


CurveRTCHost::CurveRTCHost() : header(CurveRTCHost::curve_rtc_dynamic_h_template) {
}

CurveRTCHost::~CurveRTCHost() {
    gpuErrchk(flamegpu::detail::cuda::cudaFreeHost(h_data_buffer));
}

void CurveRTCHost::registerAgentVariable(const char* variableName, const char* type, size_t type_size, unsigned int elements, bool read, bool write) {
    RTCVariableProperties props;
    props.type = CurveRTCHost::demangle(type);
    props.read = read;
    props.write = write;
    props.elements = elements;
    props.type_size = type_size;
    props.count_index = static_cast<unsigned int>(count_buffer.size()); count_buffer.push_back(0);
    if (!agent_variables.emplace(variableName, props).second) {
        THROW exception::UnknownInternalError("Variable '%s' is already registered, in CurveRTCHost::registerAgentVariable()", variableName);
    }
}
void CurveRTCHost::registerMessageInVariable(const char* variableName, const char* type, size_t type_size, unsigned int elements, bool read, bool write) {
    RTCVariableProperties props;
    props.type = CurveRTCHost::demangle(type);
    props.read = read;
    props.write = write;
    props.elements = elements;
    props.type_size = type_size;
    props.count_index = static_cast<unsigned int>(count_buffer.size()); count_buffer.push_back(0);
    if (!messageIn_variables.emplace(variableName, props).second) {
        THROW exception::UnknownInternalError("Variable '%s' is already registered, in CurveRTCHost::registerMessageInVariable()", variableName);
    }
}
void CurveRTCHost::registerMessageOutVariable(const char* variableName, const char* type, size_t type_size, unsigned int elements, bool read, bool write) {
    RTCVariableProperties props;
    props.type = CurveRTCHost::demangle(type);
    props.read = read;
    props.write = write;
    props.elements = elements;
    props.type_size = type_size;
    props.count_index = static_cast<unsigned int>(count_buffer.size()); count_buffer.push_back(0);
    if (!messageOut_variables.emplace(variableName, props).second) {
        THROW exception::UnknownInternalError("Variable '%s' is already registered, in CurveRTCHost::registerMessageOutVariable()", variableName);
    }
}
void CurveRTCHost::registerNewAgentVariable(const char* variableName, const char* type, size_t type_size, unsigned int elements, bool read, bool write) {
    RTCVariableProperties props;
    props.type = CurveRTCHost::demangle(type);
    props.read = read;
    props.write = write;
    props.elements = elements;
    props.type_size = type_size;
    props.count_index = static_cast<unsigned int>(count_buffer.size()); count_buffer.push_back(0);
    if (!newAgent_variables.emplace(variableName, props).second) {
        THROW exception::UnknownInternalError("Variable '%s' is already registered, in CurveRTCHost::registerNewAgentVariable()", variableName);
    }
}
void CurveRTCHost::registerEnvironmentDirectedGraphVertexProperty(const std::string& graphName, const std::string& propertyName, const char* type, size_t type_size, unsigned int elements, bool read, bool write) {
    RTCVariableProperties props;
    props.type = CurveRTCHost::demangle(type);
    props.read = read;
    props.write = write;
    props.elements = elements;
    props.type_size = type_size;
    props.count_index = static_cast<unsigned int>(count_buffer.size()); count_buffer.push_back(0);
    if (!directedGraph_vertexProperties.emplace(std::pair{ graphName, propertyName}, props).second) {
        THROW exception::UnknownInternalError("Property '%s' is already registered, in CurveRTCHost::registerEnvironmentDirectedGraphVertexProperty()", propertyName.c_str());
    }
}
void CurveRTCHost::registerEnvironmentDirectedGraphEdgeProperty(const std::string& graphName, const std::string& propertyName, const char* type, size_t type_size, unsigned int elements, bool read, bool write) {
    RTCVariableProperties props;
    props.type = CurveRTCHost::demangle(type);
    props.read = read;
    props.write = write;
    props.elements = elements;
    props.type_size = type_size;
    props.count_index = static_cast<unsigned int>(count_buffer.size()); count_buffer.push_back(0);
    if (!directedGraph_edgeProperties.emplace(std::pair{ graphName, propertyName }, props).second) {
        THROW exception::UnknownInternalError("Property '%s' is already registered, in CurveRTCHost::registerEnvironmentDirectedGraphEdgeProperty()", propertyName.c_str());
    }
}

void CurveRTCHost::unregisterAgentVariable(const char* variableName) {
    auto i = agent_variables.find(variableName);
    if (i != agent_variables.end()) {
        agent_variables.erase(variableName);
    } else {
        THROW exception::UnknownInternalError("Variable '%s' not found when removing variable, in CurveRTCHost::unregisterAgentVariable()", variableName);
    }
}
void CurveRTCHost::unregisterMessageOutVariable(const char* variableName) {
    auto i = messageOut_variables.find(variableName);
    if (i != messageOut_variables.end()) {
        messageOut_variables.erase(variableName);
    } else {
        THROW exception::UnknownInternalError("Variable '%s' not found when removing variable, in CurveRTCHost::unregisterMessageOutVariable()", variableName);
    }
}
void CurveRTCHost::unregisterMessageInVariable(const char* variableName) {
    auto i = messageIn_variables.find(variableName);
    if (i != messageIn_variables.end()) {
        messageIn_variables.erase(variableName);
    } else {
        THROW exception::UnknownInternalError("Variable '%s' not found when removing variable, in CurveRTCHost::unregisterMessageInVariable()", variableName);
    }
}
void CurveRTCHost::unregisterNewAgentVariable(const char* variableName) {
    auto i = newAgent_variables.find(variableName);
    if (i != newAgent_variables.end()) {
        newAgent_variables.erase(variableName);
    } else {
        THROW exception::UnknownInternalError("Variable '%s' not found when removing variable, in CurveRTCHost::unregisterNewAgentVariable()", variableName);
    }
}
void CurveRTCHost::unregisterEnvironmentDirectedGraphVertexProperty(const std::string& graphName, const std::string& propertyName) {
    auto i = directedGraph_vertexProperties.find({graphName, propertyName});
    if (i != directedGraph_vertexProperties.end()) {
        directedGraph_vertexProperties.erase({graphName, propertyName});
    } else {
        THROW exception::UnknownInternalError("Vertex property '%s' from graph '%s' not found when removing property, "
          "in CurveRTCHost::unregisterEnvironmentDirectedGraphVertexProperty()", propertyName.c_str(), graphName.c_str());
    }
}
void CurveRTCHost::unregisterEnvironmentDirectedGraphEdgeProperty(const std::string& graphName, const std::string& propertyName) {
    auto i = directedGraph_edgeProperties.find({graphName, propertyName});
    if (i != directedGraph_edgeProperties.end()) {
        directedGraph_edgeProperties.erase({graphName, propertyName});
    } else {
        THROW exception::UnknownInternalError("Edge property '%s' from graph '%s' not found when removing property, "
            "in CurveRTCHost::unregisterEnvironmentDirectedGraphEdgeProperty()", propertyName.c_str(), graphName.c_str());
    }
}


void* CurveRTCHost::getAgentVariableCachePtr(const char* variableName) {
    const auto i = agent_variables.find(variableName);
    if (i != agent_variables.end()) {
        if (i->second.h_data_ptr)
            return i->second.h_data_ptr;
        THROW exception::UnknownInternalError("Variable '%s' has not yet been allocated within the cache, in CurveRTCHost::getAgentVariableCachePtr()", variableName);
    }
    THROW exception::UnknownInternalError("Variable '%s' not found when accessing variable, in CurveRTCHost::getAgentVariableCachePtr()", variableName);
}
void* CurveRTCHost::getMessageOutVariableCachePtr(const char* variableName) {
    const auto i = messageOut_variables.find(variableName);
    if (i != messageOut_variables.end()) {
        if (i->second.h_data_ptr)
            return i->second.h_data_ptr;
        THROW exception::UnknownInternalError("Variable '%s' has not yet been allocated within the cache, in CurveRTCHost::getMessageOutVariableCachePtr()", variableName);
    }
    THROW exception::UnknownInternalError("Variable '%s' not found when accessing variable, in CurveRTCHost::getMessageOutVariableCachePtr()", variableName);
}
void* CurveRTCHost::getMessageInVariableCachePtr(const char* variableName) {
    const auto i = messageIn_variables.find(variableName);
    if (i != messageIn_variables.end()) {
        if (i->second.h_data_ptr)
            return i->second.h_data_ptr;
        THROW exception::UnknownInternalError("Variable '%s' has not yet been allocated within the cache, in CurveRTCHost::getMessageInVariableCachePtr()", variableName);
    }
    THROW exception::UnknownInternalError("Variable '%s' not found when accessing variable, in CurveRTCHost::getMessageInVariableCachePtr()", variableName);
}
void* CurveRTCHost::getNewAgentVariableCachePtr(const char* variableName) {
    const auto i = newAgent_variables.find(variableName);
    if (i != newAgent_variables.end()) {
        if (i->second.h_data_ptr)
            return i->second.h_data_ptr;
        THROW exception::UnknownInternalError("Variable '%s' has not yet been allocated within the cache, in CurveRTCHost::getNewAgentVariableCachePtr()", variableName);
    }
    THROW exception::UnknownInternalError("Variable '%s' not found when accessing variable, in CurveRTCHost::getNewAgentVariableCachePtr()", variableName);
}
void* CurveRTCHost::getEnvironmentDirectedGraphVertexPropertyCachePtr(const std::string& graphName, const std::string& propertyName) {
    const auto i = directedGraph_vertexProperties.find(std::pair{graphName, propertyName});
    if (i != directedGraph_vertexProperties.end()) {
        if (i->second.h_data_ptr)
            return i->second.h_data_ptr;
        THROW exception::UnknownInternalError("Vertex property '%s' from graph '%s' has not yet been allocated within the cache, in CurveRTCHost::getEnvironmentDirectedGraphVertexPropertyCachePtr()", propertyName.c_str(), graphName.c_str());
    }
    THROW exception::UnknownInternalError("Vertex property '%s' from graph '%s' not found when retrieving property, "
        "in CurveRTCHost::getEnvironmentDirectedGraphVertexPropertyCachePtr()", propertyName.c_str(), graphName.c_str());
}
void* CurveRTCHost::getEnvironmentDirectedGraphEdgePropertyCachePtr(const std::string& graphName, const std::string& propertyName) {
    const auto i = directedGraph_edgeProperties.find(std::pair{graphName, propertyName});
    if (i != directedGraph_edgeProperties.end()) {
        if (i->second.h_data_ptr)
            return i->second.h_data_ptr;
        THROW exception::UnknownInternalError("Edge property '%s' from graph '%s' has not yet been allocated within the cache, in CurveRTCHost::getEnvironmentDirectedGraphEdgePropertyCachePtr()", propertyName.c_str(), graphName.c_str());
    }
    THROW exception::UnknownInternalError("Edge property '%s' from graph '%s' not found when retrieving property, "
        "in CurveRTCHost::getEnvironmentDirectedGraphEdgePropertyCachePtr()", propertyName.c_str(), graphName.c_str());
}
void CurveRTCHost::setAgentVariableCount(const std::string& variableName, const unsigned int count) {
    const auto i = agent_variables.find(variableName);
    if (i != agent_variables.end()) {
        count_buffer[i->second.count_index] = count;
        return;
    }
    THROW exception::UnknownInternalError("Agent variable '%s' not found when retrieving property, "
        "in CurveRTCHost::setAgentVariablePropertyCount()", variableName.c_str());
}
void CurveRTCHost::setMessageOutVariableCount(const std::string& variableName, const unsigned int count) {
    const auto i = messageOut_variables.find(variableName);
    if (i != messageOut_variables.end()) {
        count_buffer[i->second.count_index] = count;
        return;
    }
    THROW exception::UnknownInternalError("Message variable '%s' not found when retrieving property, "
        "in CurveRTCHost::setMessageOutVariablePropertyCount()", variableName.c_str());
}
void CurveRTCHost::setMessageInVariableCount(const std::string& variableName, const unsigned int count) {
    const auto i = messageIn_variables.find(variableName);
    if (i != messageIn_variables.end()) {
        count_buffer[i->second.count_index] = count;
        return;
    }
    THROW exception::UnknownInternalError("Message variable '%s' not found when retrieving property, "
        "in CurveRTCHost::setMessageInVariableCount()", variableName.c_str());
}
void CurveRTCHost::setNewAgentVariableCount(const std::string& variableName, const unsigned int count) {
    const auto i = newAgent_variables.find(variableName);
    if (i != newAgent_variables.end()) {
        count_buffer[i->second.count_index] = count;
        return;
    }
    THROW exception::UnknownInternalError("Agent variable '%s' not found when retrieving property, "
        "in CurveRTCHost::setNewAgentVariableCount()", variableName.c_str());
}
void CurveRTCHost::setEnvironmentDirectedGraphVertexPropertyCount(const std::string& graphName, const std::string& propertyName, const unsigned int count) {
    const auto i = directedGraph_vertexProperties.find(std::pair{ graphName, propertyName });
    if (i != directedGraph_vertexProperties.end()) {
        count_buffer[i->second.count_index] = count;
        return;
    }
    THROW exception::UnknownInternalError("Vertex property '%s' from graph '%s' not found when retrieving property, "
        "in CurveRTCHost::setEnvironmentDirectedGraphVertexPropertyCount()", propertyName.c_str(), graphName.c_str());
}
void CurveRTCHost::setEnvironmentDirectedGraphEdgePropertyCount(const std::string& graphName, const std::string& propertyName, const unsigned int count) {
    const auto i = directedGraph_edgeProperties.find(std::pair{ graphName, propertyName });
    if (i != directedGraph_edgeProperties.end()) {
        count_buffer[i->second.count_index] = count;
        return;
    }
    THROW exception::UnknownInternalError("Edge property '%s' from graph '%s' not found when retrieving property, "
        "in CurveRTCHost::setEnvironmentDirectedGraphEdgePropertyCount()", propertyName.c_str(), graphName.c_str());
}

void CurveRTCHost::registerEnvVariable(const char* propertyName, ptrdiff_t offset, const char* type, size_t type_size, unsigned int elements) {
    RTCEnvVariableProperties props;
    props.type = CurveRTCHost::demangle(type);
    props.elements = elements;
    props.offset = offset;
    props.type_size = type_size;
    if (!RTCEnvVariables.emplace(propertyName, props).second) {
        THROW exception::UnknownInternalError("Environment property with name '%s' is already registered, in CurveRTCHost::registerEnvVariable()", propertyName);
    }
}
void CurveRTCHost::registerAgent(const std::string &_agentName, const std::string &_agentState) {
    if (this->agentName.empty()) {
        this->agentName = _agentName;
        this->agentState = _agentState;
    } else {
        THROW exception::UnknownInternalError("Agent is already registered with name '%s' and state '%s', in CurveRTCHost::registerAgent()", this->agentName.c_str(), this->agentState.c_str());
    }
}

void CurveRTCHost::unregisterEnvVariable(const char* propertyName) {
    auto i = RTCEnvVariables.find(propertyName);
    if (i != RTCEnvVariables.end()) {
        RTCEnvVariables.erase(propertyName);
    } else {
        THROW exception::UnknownInternalError("Environment property '%s' not found when removing environment property, in CurveRTCHost::unregisterEnvVariable()", propertyName);
    }
}
void CurveRTCHost::registerEnvMacroProperty(const char* propertyName, void *d_ptr, const char* type, size_t type_size, const std::array<unsigned int, 4> &dimensions) {
    RTCEnvMacroPropertyProperties props;
    props.type = CurveRTCHost::demangle(type);
    props.dimensions = dimensions;
    props.d_ptr = d_ptr;
    props.h_data_ptr = nullptr;
    props.type_size = type_size;
    if (!RTCEnvMacroProperties.emplace(propertyName, props).second) {
        THROW exception::UnknownInternalError("Environment property with name '%s' is already registered, in CurveRTCHost::registerEnvMacroProperty()", propertyName);
    }
}

void CurveRTCHost::unregisterEnvMacroProperty(const char* propertyName) {
    auto i = RTCEnvMacroProperties.find(propertyName);
    if (i != RTCEnvMacroProperties.end()) {
        RTCEnvMacroProperties.erase(propertyName);
    } else {
        THROW exception::UnknownInternalError("Environment macro property '%s' not found when removing environment property, in CurveRTCHost::unregisterEnvMacroProperty()", propertyName);
    }
}


void CurveRTCHost::initHeaderEnvironment(const size_t env_buffer_len) {
    // Calculate size of, and generate dynamic variables buffer
    std::stringstream variables;
    data_buffer_size = env_buffer_len;
    // Fix alignment
    data_buffer_size += (data_buffer_size % sizeof(void*) != 0) ? sizeof(void*) - (data_buffer_size % sizeof(void*)) : 0;

    agent_data_offset = data_buffer_size;     data_buffer_size += agent_variables.size() * sizeof(void*);
    messageOut_data_offset = data_buffer_size;    data_buffer_size += messageOut_variables.size() * sizeof(void*);
    messageIn_data_offset = data_buffer_size;     data_buffer_size += messageIn_variables.size() * sizeof(void*);
    newAgent_data_offset = data_buffer_size;  data_buffer_size += newAgent_variables.size() * sizeof(void*);
    directedGraphVertex_data_offset = data_buffer_size;  data_buffer_size += directedGraph_vertexProperties.size() * sizeof(void*);
    directedGraphEdge_data_offset = data_buffer_size;  data_buffer_size += directedGraph_edgeProperties.size() * sizeof(void*);
    envMacro_data_offset = data_buffer_size;  data_buffer_size += RTCEnvMacroProperties.size() * sizeof(void*);
    count_data_offset = data_buffer_size;  data_buffer_size += count_buffer.size() * sizeof(unsigned int);
    variables << "__constant__  char " << getVariableSymbolName() << "[" << data_buffer_size << "];\n";
    setHeaderPlaceholder("$DYNAMIC_VARIABLES", variables.str());
    // generate Environment::get func implementation ($DYNAMIC_ENV_GETVARIABLE_IMPL)
    {
        std::stringstream getEnvVariableImpl;
        for (std::pair<std::string, RTCEnvVariableProperties> element : RTCEnvVariables) {
            RTCEnvVariableProperties props = element.second;
            {
                getEnvVariableImpl <<   "    if (strings_equal(name, \"" << element.first << "\")) {\n";
                getEnvVariableImpl <<   "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
                getEnvVariableImpl <<   "        if(sizeof(detail::type_decode<T>::type_t) != " << element.second.type_size << ") {\n";
                getEnvVariableImpl <<   "            DTHROW(\"Environment property '%s' type mismatch.\\n\", name);\n";
                getEnvVariableImpl <<   "            return {};\n";
                getEnvVariableImpl <<   "        } else if(detail::type_decode<T>::len_t != " << element.second.elements << ") {\n";
                getEnvVariableImpl <<   "            DTHROW(\"Environment property '%s' length mismatch.\\n\", name);\n";
                getEnvVariableImpl <<   "            return {};\n";
                getEnvVariableImpl <<   "        }\n";
                getEnvVariableImpl <<   "#endif\n";
                getEnvVariableImpl <<   "        return *reinterpret_cast<T*>(reinterpret_cast<void*>(flamegpu::detail::curve::" << getVariableSymbolName() <<" + " << props.offset << "));\n";
                getEnvVariableImpl <<   "    };\n";
            }
        }
        getEnvVariableImpl <<           "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
        getEnvVariableImpl <<           "    DTHROW(\"Environment property '%s' was not found.\\n\", name);\n";
        getEnvVariableImpl <<           "#endif\n";
        getEnvVariableImpl <<           "    return  {};\n";
        setHeaderPlaceholder("$DYNAMIC_ENV_GETVARIABLE_IMPL", getEnvVariableImpl.str());
    }
    // generate Environment::get func implementation for array variables ($DYNAMIC_ENV_GETARRAYVARIABLE_IMPL)
    {
        std::stringstream getEnvArrayVariableImpl;
        for (std::pair<std::string, RTCEnvVariableProperties> element : RTCEnvVariables) {
            RTCEnvVariableProperties props = element.second;
            if (props.elements > 1) {
                getEnvArrayVariableImpl << "    if (strings_equal(name, \"" << element.first << "\")) {\n";
                getEnvArrayVariableImpl << "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
                getEnvArrayVariableImpl << "        const unsigned int t_index = detail::type_decode<T>::len_t * index + detail::type_decode<T>::len_t;\n";
                getEnvArrayVariableImpl << "        if(sizeof(detail::type_decode<T>::type_t) != " << element.second.type_size << ") {\n";
                getEnvArrayVariableImpl << "            DTHROW(\"Environment array property '%s' type mismatch.\\n\", name);\n";
                getEnvArrayVariableImpl << "            return {};\n";
                getEnvArrayVariableImpl << "        } else if (detail::type_decode<T>::len_t * N != " << element.second.elements << " && N != 0) {\n";  // Special case, env array specifying length is optional as it's not actually required
                getEnvArrayVariableImpl << "            DTHROW(\"Environment array property '%s' length mismatch.\\n\", name);\n";
                getEnvArrayVariableImpl << "            return {};\n";
                getEnvArrayVariableImpl << "        } else if (t_index > " << element.second.elements << " || t_index < index) {\n";
                getEnvArrayVariableImpl << "            DTHROW(\"Environment array property '%s', index %d is out of bounds.\\n\", name, index);\n";
                getEnvArrayVariableImpl << "            return {};\n";
                getEnvArrayVariableImpl << "        }\n";
                getEnvArrayVariableImpl << "#endif\n";
                getEnvArrayVariableImpl << "        return reinterpret_cast<T*>(reinterpret_cast<void*>(flamegpu::detail::curve::" << getVariableSymbolName() <<" + " << props.offset << "))[index];\n";
                getEnvArrayVariableImpl << "    };\n";
            }
        }
        getEnvArrayVariableImpl <<         "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
        getEnvArrayVariableImpl <<         "    DTHROW(\"Environment array property '%s' was not found.\\n\", name);\n";
        getEnvArrayVariableImpl <<         "#endif\n";
        getEnvArrayVariableImpl <<         "    return {};\n";
        setHeaderPlaceholder("$DYNAMIC_ENV_GETARRAYVARIABLE_IMPL", getEnvArrayVariableImpl.str());
    }
    // generate Environment::getMacroProperty func implementation ($DYNAMIC_ENV_GETREADONLYMACROPROPERTY_IMPL)
    {
        size_t ct = 0;
        std::stringstream getMacroPropertyImpl;
        for (std::pair<std::string, RTCEnvMacroPropertyProperties> element : RTCEnvMacroProperties) {
            RTCEnvMacroPropertyProperties props = element.second;
            getMacroPropertyImpl << "    if (strings_equal(name, \"" << element.first << "\")) {\n";
            getMacroPropertyImpl << "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
            getMacroPropertyImpl << "        if(sizeof(T) != " << element.second.type_size << ") {\n";
            getMacroPropertyImpl << "            DTHROW(\"Environment macro property '%s' type mismatch.\\n\", name);\n";
            getMacroPropertyImpl << "        } else if (I != " << element.second.dimensions[0] << " ||\n";
            getMacroPropertyImpl << "            J != " << element.second.dimensions[1] << " |\n";
            getMacroPropertyImpl << "            K != " << element.second.dimensions[2] << " |\n";
            getMacroPropertyImpl << "            W != " << element.second.dimensions[3] << ") {\n";
            getMacroPropertyImpl << "            DTHROW(\"Environment macro property '%s' dimensions do not match (%u, %u, %u, %u) != (%u, %u, %u, %u).\\n\", name,\n";
            getMacroPropertyImpl << "                I, J, K, W, " << element.second.dimensions[0] << ", " << element.second.dimensions[1] << ", " << element.second.dimensions[2] << ", " << element.second.dimensions[3] << ");\n";
            getMacroPropertyImpl << "        } else {\n";
            getMacroPropertyImpl << "            return ReadOnlyDeviceMacroProperty<T, I, J, K, W>(*reinterpret_cast<T**>(flamegpu::detail::curve::" << getVariableSymbolName() << " + " << envMacro_data_offset + (ct * sizeof(void*)) << "),\n";
            // Read-write flag resides in 8 bits at the end of the buffer
            getMacroPropertyImpl << "                reinterpret_cast<unsigned int*>(*reinterpret_cast<char**>(flamegpu::detail::curve::" << getVariableSymbolName() << " + " << envMacro_data_offset + (ct * sizeof(void*)) << ") + (I * J * K * W * sizeof(T))));\n";
            getMacroPropertyImpl << "        }\n";
            getMacroPropertyImpl << "#else\n";
            getMacroPropertyImpl << "        return ReadOnlyDeviceMacroProperty<T, I, J, K, W>(*reinterpret_cast<T**>(flamegpu::detail::curve::" << getVariableSymbolName() << " + " << envMacro_data_offset + (ct * sizeof(void*)) << "));\n";
            getMacroPropertyImpl << "#endif\n";
            getMacroPropertyImpl << "    };\n";
            ++ct;
        }
        getMacroPropertyImpl << "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
        getMacroPropertyImpl << "    DTHROW(\"Environment macro property '%s' was not found.\\n\", name);\n";
        getMacroPropertyImpl << "    return ReadOnlyDeviceMacroProperty<T, I, J, K, W>(nullptr, nullptr);\n";
        getMacroPropertyImpl << "#else\n";
        getMacroPropertyImpl << "    return ReadOnlyDeviceMacroProperty<T, I, J, K, W>(nullptr);\n";
        getMacroPropertyImpl << "#endif\n";
        setHeaderPlaceholder("$DYNAMIC_ENV_GETREADONLYMACROPROPERTY_IMPL", getMacroPropertyImpl.str());
    }
    // generate Environment::getMacroProperty func implementation ($DYNAMIC_ENV_GETMACROPROPERTY_IMPL)
    {
        size_t ct = 0;
        std::stringstream getMacroPropertyImpl;
        for (std::pair<std::string, RTCEnvMacroPropertyProperties> element : RTCEnvMacroProperties) {
            RTCEnvMacroPropertyProperties props = element.second;
            getMacroPropertyImpl << "    if (strings_equal(name, \"" << element.first << "\")) {\n";
            getMacroPropertyImpl << "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
            getMacroPropertyImpl << "        if(sizeof(T) != " << element.second.type_size << ") {\n";
            getMacroPropertyImpl << "            DTHROW(\"Environment macro property '%s' type mismatch.\\n\", name);\n";
            getMacroPropertyImpl << "        } else if (I != " << element.second.dimensions[0] << " ||\n";
            getMacroPropertyImpl << "            J != " << element.second.dimensions[1] << " |\n";
            getMacroPropertyImpl << "            K != " << element.second.dimensions[2] << " |\n";
            getMacroPropertyImpl << "            W != " << element.second.dimensions[3] << ") {\n";
            getMacroPropertyImpl << "            DTHROW(\"Environment macro property '%s' dimensions do not match (%u, %u, %u, %u) != (%u, %u, %u, %u).\\n\", name,\n";
            getMacroPropertyImpl << "                I, J, K, W, " << element.second.dimensions[0] << ", " << element.second.dimensions[1] << ", " << element.second.dimensions[2] << ", " << element.second.dimensions[3] << ");\n";
            getMacroPropertyImpl << "        } else {\n";
            getMacroPropertyImpl << "            return DeviceMacroProperty<T, I, J, K, W>(*reinterpret_cast<T**>(flamegpu::detail::curve::" << getVariableSymbolName() << " + " << envMacro_data_offset + (ct * sizeof(void*)) << "),\n";
            // Read-write flag resides in 8 bits at the end of the buffer
            getMacroPropertyImpl << "                reinterpret_cast<unsigned int*>(*reinterpret_cast<char**>(flamegpu::detail::curve::" << getVariableSymbolName() << " + " << envMacro_data_offset + (ct * sizeof(void*)) << ") + (I * J * K * W * sizeof(T))));\n";
            getMacroPropertyImpl << "        }\n";
            getMacroPropertyImpl << "#else\n";
            getMacroPropertyImpl << "        return DeviceMacroProperty<T, I, J, K, W>(*reinterpret_cast<T**>(flamegpu::detail::curve::" << getVariableSymbolName() << " + " << envMacro_data_offset + (ct * sizeof(void*)) << "));\n";
            getMacroPropertyImpl << "#endif\n";
            getMacroPropertyImpl << "    };\n";
            ++ct;
        }
        getMacroPropertyImpl << "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
        getMacroPropertyImpl << "    DTHROW(\"Environment macro property '%s' was not found.\\n\", name);\n";
        getMacroPropertyImpl << "    return DeviceMacroProperty<T, I, J, K, W>(nullptr, nullptr);\n";
        getMacroPropertyImpl << "#else\n";
        getMacroPropertyImpl << "    return DeviceMacroProperty<T, I, J, K, W>(nullptr);\n";
        getMacroPropertyImpl << "#endif\n";
        setHeaderPlaceholder("$DYNAMIC_ENV_GETMACROPROPERTY_IMPL", getMacroPropertyImpl.str());
    }
}
void CurveRTCHost::initHeaderSetters() {
    // generate setAgentVariable func implementation ($DYNAMIC_SETAGENTVARIABLE_IMPL)
    {
        size_t ct = 0;
        std::stringstream setAgentVariableImpl;
        for (const auto &element : agent_variables) {
            RTCVariableProperties props = element.second;
            if (props.write) {
                setAgentVariableImpl << "          if (strings_equal(name, \"" << element.first << "\")) {\n";
                setAgentVariableImpl << "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
                setAgentVariableImpl << "              if(sizeof(detail::type_decode<T>::type_t) != " << element.second.type_size << ") {\n";
                setAgentVariableImpl << "                  DTHROW(\"Agent variable '%s' type mismatch during setVariable().\\n\", name);\n";
                setAgentVariableImpl << "                  return;\n";
                setAgentVariableImpl << "              } else if(detail::type_decode<T>::len_t != " << element.second.elements << ") {\n";
                setAgentVariableImpl << "                  DTHROW(\"Agent variable '%s' length mismatch during setVariable().\\n\", name);\n";
                setAgentVariableImpl << "                  return;\n";
                setAgentVariableImpl << "              }\n";
                setAgentVariableImpl << "#endif\n";
                setAgentVariableImpl << "              (*static_cast<T**>(static_cast<void*>(flamegpu::detail::curve::" << getVariableSymbolName() << " + " << agent_data_offset + (ct++ * sizeof(void*)) << ")))[index] = (T) variable;\n";
                setAgentVariableImpl << "              return;\n";
                setAgentVariableImpl << "          }\n";
            } else { ++ct; }
        }
        setAgentVariableImpl <<         "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
        setAgentVariableImpl <<         "          DTHROW(\"Agent variable '%s' was not found during setVariable().\\n\", name);\n";
        setAgentVariableImpl <<         "#endif\n";
        setHeaderPlaceholder("$DYNAMIC_SETAGENTVARIABLE_IMPL", setAgentVariableImpl.str());
    }
    // generate setMessageVariable func implementation ($DYNAMIC_SETMESSAGEVARIABLE_IMPL)
    {
        size_t ct = 0;
        std::stringstream setMessageVariableImpl;
        for (const auto &element : messageOut_variables) {
            RTCVariableProperties props = element.second;
            if (props.write) {
                setMessageVariableImpl << "          if (strings_equal(name, \"" << element.first << "\")) {\n";
                setMessageVariableImpl << "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
                setMessageVariableImpl << "              if(sizeof(detail::type_decode<T>::type_t) != " << element.second.type_size << ") {\n";
                setMessageVariableImpl << "                  DTHROW(\"Message variable '%s' type mismatch during setVariable().\\n\", name);\n";
                setMessageVariableImpl << "                  return;\n";
                setMessageVariableImpl << "              } else if(detail::type_decode<T>::len_t != " << element.second.elements << ") {\n";
                setMessageVariableImpl << "                  DTHROW(\"Message variable '%s' length mismatch during setVariable().\\n\", name);\n";
                setMessageVariableImpl << "                  return;\n";
                setMessageVariableImpl << "              }\n";
                setMessageVariableImpl << "#endif\n";
                setMessageVariableImpl << "              (*static_cast<T**>(static_cast<void*>(flamegpu::detail::curve::" << getVariableSymbolName() << " + " << messageOut_data_offset + (ct++ * sizeof(void*)) << ")))[index] = (T) variable;\n";
                setMessageVariableImpl << "              return;\n";
                setMessageVariableImpl << "          }\n";
            } else { ++ct; }
        }
        setMessageVariableImpl <<         "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
        setMessageVariableImpl <<         "          DTHROW(\"Message variable '%s' was not found during setVariable().\\n\", name);\n";
        setMessageVariableImpl <<         "#endif\n";
        setHeaderPlaceholder("$DYNAMIC_SETMESSAGEVARIABLE_IMPL", setMessageVariableImpl.str());
    }
    // generate setNewAgentVariable func implementation ($DYNAMIC_SETNEWAGENTVARIABLE_IMPL)
    {
        size_t ct = 0;
        std::stringstream setNewAgentVariableImpl;
        for (const auto &element : newAgent_variables) {
            RTCVariableProperties props = element.second;
            if (props.write) {
                setNewAgentVariableImpl << "          if (strings_equal(name, \"" << element.first << "\")) {\n";
                setNewAgentVariableImpl << "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
                setNewAgentVariableImpl << "              if(sizeof(detail::type_decode<T>::type_t) != " << element.second.type_size << ") {\n";
                setNewAgentVariableImpl << "                  DTHROW(\"New agent variable '%s' type mismatch during setVariable().\\n\", name);\n";
                setNewAgentVariableImpl << "                  return;\n";
                setNewAgentVariableImpl << "              } else if(detail::type_decode<T>::len_t != " << element.second.elements << ") {\n";
                setNewAgentVariableImpl << "                  DTHROW(\"New agent variable '%s' length mismatch during setVariable().\\n\", name);\n";
                setNewAgentVariableImpl << "                  return;\n";
                setNewAgentVariableImpl << "              }\n";
                setNewAgentVariableImpl << "#endif\n";
                setNewAgentVariableImpl << "              (*static_cast<T**>(static_cast<void*>(flamegpu::detail::curve::" << getVariableSymbolName() << " + " << newAgent_data_offset + (ct++ * sizeof(void*)) << ")))[index] = (T) variable;\n";
                setNewAgentVariableImpl << "              return;\n";
                setNewAgentVariableImpl << "          }\n";
            } else { ++ct; }
        }
        setNewAgentVariableImpl <<         "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
        setNewAgentVariableImpl <<         "          DTHROW(\"New agent variable '%s' was not found during setVariable().\\n\", name);\n";
        setNewAgentVariableImpl <<         "#endif\n";
        setHeaderPlaceholder("$DYNAMIC_SETNEWAGENTVARIABLE_IMPL", setNewAgentVariableImpl.str());
    }
    // generate setAgentArrayVariable func implementation ($DYNAMIC_SETAGENTARRAYVARIABLE_IMPL)
    {
        size_t ct = 0;
        std::stringstream setAgentArrayVariableImpl;
        if (!agent_variables.empty())
            setAgentArrayVariableImpl <<             "    const size_t i = (index * detail::type_decode<T>::len_t * N) + detail::type_decode<T>::len_t * array_index;\n";
        for (const auto &element : agent_variables) {
            RTCVariableProperties props = element.second;
            if (props.write && props.elements > 1) {
                setAgentArrayVariableImpl << "          if (strings_equal(name, \"" << element.first << "\")) {\n";
                setAgentArrayVariableImpl << "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
                setAgentArrayVariableImpl << "              const unsigned int t_index = detail::type_decode<T>::len_t * array_index + detail::type_decode<T>::len_t;\n";
                setAgentArrayVariableImpl << "              if(sizeof(detail::type_decode<T>::type_t) != " << element.second.type_size << ") {\n";
                setAgentArrayVariableImpl << "                  DTHROW(\"Agent array variable '%s' type mismatch during setVariable().\\n\", name);\n";
                setAgentArrayVariableImpl << "                  return;\n";
                setAgentArrayVariableImpl << "              } else if (detail::type_decode<T>::len_t * N != " << element.second.elements << ") {\n";
                setAgentArrayVariableImpl << "                  DTHROW(\"Agent array variable '%s' length mismatch during setVariable().\\n\", name);\n";
                setAgentArrayVariableImpl << "                  return;\n";
                setAgentArrayVariableImpl << "              } else if (t_index > " << element.second.elements << " || t_index < array_index) {\n";
                setAgentArrayVariableImpl << "                  DTHROW(\"Agent array variable '%s', index %d is out of bounds during setVariable().\\n\", name, array_index);\n";
                setAgentArrayVariableImpl << "                  return;\n";
                setAgentArrayVariableImpl << "              }\n";
                setAgentArrayVariableImpl << "#endif\n";
                setAgentArrayVariableImpl << "              (*static_cast<T**>(static_cast<void*>(flamegpu::detail::curve::" << getVariableSymbolName() << " + " << agent_data_offset + (ct++ * sizeof(void*)) << ")))[i] = (T) variable;\n";
                setAgentArrayVariableImpl << "              return;\n";
                setAgentArrayVariableImpl << "          }\n";
            } else { ++ct; }
        }
        setAgentArrayVariableImpl <<         "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
        setAgentArrayVariableImpl <<         "          DTHROW(\"Agent array variable '%s' was not found during setVariable().\\n\", name);\n";
        setAgentArrayVariableImpl <<         "#endif\n";
        setHeaderPlaceholder("$DYNAMIC_SETAGENTARRAYVARIABLE_IMPL", setAgentArrayVariableImpl.str());
    }
    // generate setMessageArrayVariable func implementation ($DYNAMIC_SETMESSAGEARRAYVARIABLE_IMPL)
    {
        size_t ct = 0;
        std::stringstream setMessageArrayVariableImpl;
        if (!messageOut_variables.empty())
            setMessageArrayVariableImpl << "    const size_t i = (index * detail::type_decode<T>::len_t * N) + detail::type_decode<T>::len_t * array_index;\n";
        for (const auto& element : messageOut_variables) {
            RTCVariableProperties props = element.second;
            if (props.write && props.elements > 1) {
                setMessageArrayVariableImpl << "          if (strings_equal(name, \"" << element.first << "\")) {\n";
                setMessageArrayVariableImpl << "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
                setMessageArrayVariableImpl << "              const unsigned int t_index = detail::type_decode<T>::len_t * array_index + detail::type_decode<T>::len_t;\n";
                setMessageArrayVariableImpl << "              if(sizeof(detail::type_decode<T>::type_t) != " << element.second.type_size << ") {\n";
                setMessageArrayVariableImpl << "                  DTHROW(\"Message array variable '%s' type mismatch during setVariable().\\n\", name);\n";
                setMessageArrayVariableImpl << "                  return;\n";
                setMessageArrayVariableImpl << "              } else if (detail::type_decode<T>::len_t * N != " << element.second.elements << ") {\n";
                setMessageArrayVariableImpl << "                  DTHROW(\"Message array variable '%s' length mismatch during setVariable().\\n\", name);\n";
                setMessageArrayVariableImpl << "                  return;\n";
                setMessageArrayVariableImpl << "              } else if (t_index > " << element.second.elements << " || t_index < array_index) {\n";
                setMessageArrayVariableImpl << "                  DTHROW(\"Message array variable '%s', index %d is out of bounds during setVariable().\\n\", name, array_index);\n";
                setMessageArrayVariableImpl << "                  return;\n";
                setMessageArrayVariableImpl << "              }\n";
                setMessageArrayVariableImpl << "#endif\n";
                setMessageArrayVariableImpl << "              (*static_cast<T**>(static_cast<void*>(flamegpu::detail::curve::" << getVariableSymbolName() << " + " << messageOut_data_offset + (ct++ * sizeof(void*)) << ")))[i] = (T) variable;\n";
                setMessageArrayVariableImpl << "              return;\n";
                setMessageArrayVariableImpl << "          }\n";
            } else { ++ct; }
        }
        setMessageArrayVariableImpl << "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
        setMessageArrayVariableImpl << "          DTHROW(\"Message array variable '%s' was not found during setVariable().\\n\", name);\n";
        setMessageArrayVariableImpl << "#endif\n";
        setHeaderPlaceholder("$DYNAMIC_SETMESSAGEARRAYVARIABLE_IMPL", setMessageArrayVariableImpl.str());
    }
    // generate setNewAgentArrayVariable func implementation ($DYNAMIC_SETNEWAGENTARRAYVARIABLE_IMPL)
    {
        size_t ct = 0;
        std::stringstream setNewAgentArrayVariableImpl;
        if (!newAgent_variables.empty())
            setNewAgentArrayVariableImpl << "    const size_t i = (index * detail::type_decode<T>::len_t * N) + detail::type_decode<T>::len_t * array_index;\n";
        for (const auto &element : newAgent_variables) {
            RTCVariableProperties props = element.second;
            if (props.write && props.elements > 1) {
                setNewAgentArrayVariableImpl << "          if (strings_equal(name, \"" << element.first << "\")) {\n";
                setNewAgentArrayVariableImpl << "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
                setNewAgentArrayVariableImpl << "              const unsigned int t_index = detail::type_decode<T>::len_t * array_index + detail::type_decode<T>::len_t;\n";
                setNewAgentArrayVariableImpl << "              if(sizeof(detail::type_decode<T>::type_t) != " << element.second.type_size << ") {\n";
                setNewAgentArrayVariableImpl << "                  DTHROW(\"New agent array variable '%s' type mismatch during setVariable().\\n\", name);\n";
                setNewAgentArrayVariableImpl << "                  return;\n";
                setNewAgentArrayVariableImpl << "              } else if (detail::type_decode<T>::len_t * N != " << element.second.elements << ") {\n";
                setNewAgentArrayVariableImpl << "                  DTHROW(\"New agent array variable '%s' length mismatch during setVariable().\\n\", name);\n";
                setNewAgentArrayVariableImpl << "                  return;\n";
                setNewAgentArrayVariableImpl << "              } else if (t_index > " << element.second.elements << " || t_index < array_index) {\n";
                setNewAgentArrayVariableImpl << "                  DTHROW(\"New agent array variable '%s', index %d is out of bounds during setVariable().\\n\", name, array_index);\n";
                setNewAgentArrayVariableImpl << "                  return;\n";
                setNewAgentArrayVariableImpl << "              }\n";
                setNewAgentArrayVariableImpl << "#endif\n";
                setNewAgentArrayVariableImpl << "              (*static_cast<T**>(static_cast<void*>(flamegpu::detail::curve::" << getVariableSymbolName() << " + " << newAgent_data_offset + (ct++ * sizeof(void*)) << ")))[i] = (T) variable;\n";
                setNewAgentArrayVariableImpl << "              return;\n";
                setNewAgentArrayVariableImpl << "          }\n";
            } else { ++ct; }
        }
        setNewAgentArrayVariableImpl <<         "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
        setNewAgentArrayVariableImpl <<         "          DTHROW(\"New agent array variable '%s' was not found during setVariable().\\n\", name);\n";
        setNewAgentArrayVariableImpl <<         "#endif\n";
        setHeaderPlaceholder("$DYNAMIC_SETNEWAGENTARRAYVARIABLE_IMPL", setNewAgentArrayVariableImpl.str());
    }
}
void CurveRTCHost::initHeaderGetters() {
    // generate getAgentVariable func implementation ($DYNAMIC_GETAGENTVARIABLE_IMPL)
    {
        size_t ct = 0;
        std::stringstream getAgentVariableImpl;
        for (const auto &element : agent_variables) {
            RTCVariableProperties props = element.second;
            if (props.read) {
                getAgentVariableImpl << "            if (strings_equal(name, \"" << element.first << "\")) {\n";
                getAgentVariableImpl << "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
                getAgentVariableImpl << "                if(sizeof(detail::type_decode<T>::type_t) != " << element.second.type_size << ") {\n";
                getAgentVariableImpl << "                    DTHROW(\"Agent variable '%s' type mismatch during getVariable().\\n\", name);\n";
                getAgentVariableImpl << "                    return {};\n";
                getAgentVariableImpl << "                } else if(detail::type_decode<T>::len_t != " << element.second.elements << ") {\n";
                getAgentVariableImpl << "                    DTHROW(\"Agent variable '%s' length mismatch during getVariable().\\n\", name);\n";
                getAgentVariableImpl << "                    return {};\n";
                getAgentVariableImpl << "                }\n";
                getAgentVariableImpl << "#endif\n";
                getAgentVariableImpl << "                return (*static_cast<T**>(static_cast<void*>(flamegpu::detail::curve::" << getVariableSymbolName() << " + " << agent_data_offset + (ct++ * sizeof(void*)) << ")))[index];\n";
                getAgentVariableImpl << "            }\n";
            } else { ++ct; }
        }
        getAgentVariableImpl <<         "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
        getAgentVariableImpl <<         "            DTHROW(\"Agent variable '%s' was not found during getVariable().\\n\", name);\n";
        getAgentVariableImpl <<         "#endif\n";
        getAgentVariableImpl <<         "            return {};\n";
        setHeaderPlaceholder("$DYNAMIC_GETAGENTVARIABLE_IMPL", getAgentVariableImpl.str());
    }
    // generate getMessageVariable func implementation ($DYNAMIC_GETMESSAGEVARIABLE_IMPL)
    {
        size_t ct = 0;
        std::stringstream getMessageVariableImpl;
        for (const auto &element : messageIn_variables) {
            RTCVariableProperties props = element.second;
            if (props.read) {
                getMessageVariableImpl << "            if (strings_equal(name, \"" << element.first << "\")) {\n";
                getMessageVariableImpl << "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
                getMessageVariableImpl << "                if(sizeof(detail::type_decode<T>::type_t) != " << element.second.type_size << ") {\n";
                getMessageVariableImpl << "                    DTHROW(\"Message variable '%s' type mismatch during getVariable().\\n\", name);\n";
                getMessageVariableImpl << "                    return {};\n";
                getMessageVariableImpl << "                } else if(detail::type_decode<T>::len_t != " << element.second.elements << ") {\n";
                getMessageVariableImpl << "                    DTHROW(\"Message variable '%s' length mismatch during getVariable().\\n\", name);\n";
                getMessageVariableImpl << "                    return {};\n";
                getMessageVariableImpl << "                }\n";
                getMessageVariableImpl << "#endif\n";
                getMessageVariableImpl << "                return (*static_cast<T**>(static_cast<void*>(flamegpu::detail::curve::" << getVariableSymbolName() << " + " << messageIn_data_offset + (ct++ * sizeof(void*)) << ")))[index];\n";
                getMessageVariableImpl << "            }\n";
            } else { ++ct; }
        }
        getMessageVariableImpl <<         "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
        getMessageVariableImpl <<         "            DTHROW(\"Message variable '%s' was not found during getVariable().\\n\", name);\n";
        getMessageVariableImpl <<         "#endif\n";
        getMessageVariableImpl <<         "            return {};\n";
        setHeaderPlaceholder("$DYNAMIC_GETMESSAGEVARIABLE_IMPL", getMessageVariableImpl.str());
    }
    // getEnvironmentDirectedGraphPBM
    {
        size_t ct = 0;
        std::stringstream getDirectedGraphPBMImpl;
        for (const auto& element : directedGraph_vertexProperties) {
            RTCVariableProperties props = element.second;
            if (props.read  && element.first.second == GRAPH_VERTEX_PBM_VARIABLE_NAME) {
                getDirectedGraphPBMImpl << "            if (graphHash == " << Curve::variableRuntimeHash(element.first.first) << ") {\n";
                getDirectedGraphPBMImpl << "                return (*static_cast<unsigned int**>(static_cast<void*>(flamegpu::detail::curve::" << getVariableSymbolName() << " + " << directedGraphVertex_data_offset + (ct++ * sizeof(void*)) << ")));\n";
                getDirectedGraphPBMImpl << "            }\n";
            } else { ++ct; }
        }
        getDirectedGraphPBMImpl << "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
        getDirectedGraphPBMImpl << "            DTHROW(\"Directed graph PBM was not found during getEnvironmentDirectedGraphPBM().\\n\");\n";
        getDirectedGraphPBMImpl << "#endif\n";
        getDirectedGraphPBMImpl << "            return {};\n";
        setHeaderPlaceholder("$DYNAMIC_GETDIRECTEDGRAPHPBM_IMPL", getDirectedGraphPBMImpl.str());
    }
    // getEnvironmentDirectedGraphIPBM
    {
        size_t ct = 0;
        std::stringstream getDirectedGraphIPBMImpl;
        for (const auto& element : directedGraph_vertexProperties) {
            RTCVariableProperties props = element.second;
            if (props.read && element.first.second == GRAPH_VERTEX_IPBM_VARIABLE_NAME) {
                getDirectedGraphIPBMImpl << "            if (graphHash == " << Curve::variableRuntimeHash(element.first.first) << ") {\n";
                getDirectedGraphIPBMImpl << "                return (*static_cast<unsigned int**>(static_cast<void*>(flamegpu::detail::curve::" << getVariableSymbolName() << " + " << directedGraphVertex_data_offset + (ct++ * sizeof(void*)) << ")));\n";
                getDirectedGraphIPBMImpl << "            }\n";
            } else { ++ct; }
        }
        getDirectedGraphIPBMImpl << "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
        getDirectedGraphIPBMImpl << "            DTHROW(\"Directed graph IPBM was not found during getEnvironmentDirectedGraphIPBM().\\n\");\n";
        getDirectedGraphIPBMImpl << "#endif\n";
        getDirectedGraphIPBMImpl << "            return {};\n";
        setHeaderPlaceholder("$DYNAMIC_GETDIRECTEDGRAPHIPBM_IMPL", getDirectedGraphIPBMImpl.str());
    }
    // getEnvironmentDirectedGraphIPBMEdges
    {
        size_t ct = 0;
        std::stringstream getDirectedGraphIPBMEdgesImpl;
        for (const auto& element : directedGraph_vertexProperties) {
            RTCVariableProperties props = element.second;
            if (props.read && element.first.second == GRAPH_VERTEX_IPBM_EDGES_VARIABLE_NAME) {
                getDirectedGraphIPBMEdgesImpl << "            if (graphHash == " << Curve::variableRuntimeHash(element.first.first) << ") {\n";
                getDirectedGraphIPBMEdgesImpl << "                return (*static_cast<unsigned int**>(static_cast<void*>(flamegpu::detail::curve::" << getVariableSymbolName() << " + " << directedGraphVertex_data_offset + (ct++ * sizeof(void*)) << ")));\n";
                getDirectedGraphIPBMEdgesImpl << "            }\n";
            } else { ++ct; }
        }
        getDirectedGraphIPBMEdgesImpl << "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
        getDirectedGraphIPBMEdgesImpl << "            DTHROW(\"Directed graph IPBM edge list was not found during getEnvironmentDirectedGraphIPBMEdges().\\n\");\n";
        getDirectedGraphIPBMEdgesImpl << "#endif\n";
        getDirectedGraphIPBMEdgesImpl << "            return {};\n";
        setHeaderPlaceholder("$DYNAMIC_GETDIRECTEDGRAPHIPBMEDGES_IMPL", getDirectedGraphIPBMEdgesImpl.str());
    }
    // generate getEnvironmentDirectedGraphVertexProperty func implementation ($DYNAMIC_GETDIRECTEDGRAPHVERTEXPROPERTY_IMPL)
    {
        size_t ct = 0;
        std::stringstream getGraphVertexPropertyImpl;
        for (const auto& element : directedGraph_vertexProperties) {
            RTCVariableProperties props = element.second;
            if (props.read) {
                getGraphVertexPropertyImpl << "            if (strings_equal(name, \"" << element.first.second << "\") && graphHash == " << Curve::variableRuntimeHash(element.first.first) << ") {\n";
                getGraphVertexPropertyImpl << "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
                getGraphVertexPropertyImpl << "                if(sizeof(type_decode<T>::type_t) != " << element.second.type_size << ") {\n";
                getGraphVertexPropertyImpl << "                    DTHROW(\"Directed graph vertex property '%s' type mismatch during getProperty().\\n\", name);\n";
                getGraphVertexPropertyImpl << "                    return {};\n";
                getGraphVertexPropertyImpl << "                } else if(type_decode<T>::len_t != " << element.second.elements << ") {\n";
                getGraphVertexPropertyImpl << "                    DTHROW(\"MDirected graph vertex property '%s' length mismatch during getProperty().\\n\", name);\n";
                getGraphVertexPropertyImpl << "                    return {};\n";
                getGraphVertexPropertyImpl << "                }\n";
                getGraphVertexPropertyImpl << "#endif\n";
                getGraphVertexPropertyImpl << "                return (*static_cast<T**>(static_cast<void*>(flamegpu::detail::curve::" << getVariableSymbolName() << " + " << directedGraphVertex_data_offset + (ct++ * sizeof(void*)) << ")))[index];\n";
                getGraphVertexPropertyImpl << "            }\n";
            } else { ++ct; }
        }
        getGraphVertexPropertyImpl << "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
        getGraphVertexPropertyImpl << "            DTHROW(\"Directed graph vertex property '%s' was not found during getProperty().\\n\", name);\n";
        getGraphVertexPropertyImpl << "#endif\n";
        getGraphVertexPropertyImpl << "            return {};\n";
        setHeaderPlaceholder("$DYNAMIC_GETDIRECTEDGRAPHVERTEXPROPERTY_IMPL", getGraphVertexPropertyImpl.str());
    }
    // generate getEnvironmentDirectedGraphEdgeProperty func implementation ($DYNAMIC_GETDIRECTEDGRAPHEDGEPROPERTY_IMPL)
    {
        size_t ct = 0;
        std::stringstream getGraphEdgePropertyImpl;
        for (const auto& element : directedGraph_edgeProperties) {
            RTCVariableProperties props = element.second;
            if (props.read) {
                getGraphEdgePropertyImpl << "            if (strings_equal(name, \"" << element.first.second << "\") && graphHash == " << Curve::variableRuntimeHash(element.first.first) << ") {\n";
                getGraphEdgePropertyImpl << "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
                getGraphEdgePropertyImpl << "                if(sizeof(type_decode<T>::type_t) != " << element.second.type_size << ") {\n";
                getGraphEdgePropertyImpl << "                    DTHROW(\"Directed graph edge property '%s' type mismatch during getProperty().\\n\", name);\n";
                getGraphEdgePropertyImpl << "                    return {};\n";
                getGraphEdgePropertyImpl << "                } else if(type_decode<T>::len_t != " << element.second.elements << ") {\n";
                getGraphEdgePropertyImpl << "                    DTHROW(\"MDirected graph edge property '%s' length mismatch during getProperty().\\n\", name);\n";
                getGraphEdgePropertyImpl << "                    return {};\n";
                getGraphEdgePropertyImpl << "                }\n";
                getGraphEdgePropertyImpl << "#endif\n";
                getGraphEdgePropertyImpl << "                return (*static_cast<T**>(static_cast<void*>(flamegpu::detail::curve::" << getVariableSymbolName() << " + " << directedGraphEdge_data_offset + (ct++ * sizeof(void*)) << ")))[index];\n";
                getGraphEdgePropertyImpl << "            }\n";
            } else { ++ct; }
        }
        getGraphEdgePropertyImpl << "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
        getGraphEdgePropertyImpl << "            DTHROW(\"Directed graph edge property '%s' was not found during getProperty().\\n\", name);\n";
        getGraphEdgePropertyImpl << "#endif\n";
        getGraphEdgePropertyImpl << "            return {};\n";
        setHeaderPlaceholder("$DYNAMIC_GETDIRECTEDGRAPHEDGEPROPERTY_IMPL", getGraphEdgePropertyImpl.str());
    }
    // generate getAgentVariable_ldg func implementation ($DYNAMIC_GETAGENTVARIABLE_LDG_IMPL)
    {
        size_t ct = 0;
        std::stringstream getAgentVariableLDGImpl;
        for (const auto &element : agent_variables) {
            RTCVariableProperties props = element.second;
            if (props.read && props.elements == 1) {  // GLM does not support __ldg() so should not use this
                getAgentVariableLDGImpl << "            if (strings_equal(name, \"" << element.first << "\")) {\n";
                getAgentVariableLDGImpl << "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
                getAgentVariableLDGImpl << "                if(sizeof(T) != " << element.second.type_size * element.second.elements << ") {\n";
                getAgentVariableLDGImpl << "                    DTHROW(\"Agent variable '%s' type mismatch during getVariable().\\n\", name);\n";
                getAgentVariableLDGImpl << "                    return {};\n";
                getAgentVariableLDGImpl << "                }\n";
                getAgentVariableLDGImpl << "#endif\n";
                getAgentVariableLDGImpl << "                return (T) __ldg((*static_cast<T**>(static_cast<void*>(flamegpu::detail::curve::" << getVariableSymbolName() << " + " << agent_data_offset + (ct * sizeof(void*)) << "))) + index);\n";
                getAgentVariableLDGImpl << "            }\n";
                ++ct;  // Prev was part of the return line, but don't want confusion
            } else { ++ct; }
        }
        getAgentVariableLDGImpl <<         "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
        getAgentVariableLDGImpl <<         "            DTHROW(\"Agent variable '%s' was not found during getVariable().\\n\", name);\n";
        getAgentVariableLDGImpl <<         "#endif\n";
        getAgentVariableLDGImpl <<         "            return {};\n";
        setHeaderPlaceholder("$DYNAMIC_GETAGENTVARIABLE_LDG_IMPL", getAgentVariableLDGImpl.str());
    }
    // generate getMessageVariable_ldg func implementation ($DYNAMIC_GETMESSAGEVARIABLE_LDG_IMPL)
    {
        size_t ct = 0;
        std::stringstream getMessageVariableLDGImpl;
        for (const auto &element : messageIn_variables) {
            RTCVariableProperties props = element.second;
            if (props.read && props.elements == 1) {  // GLM does not support __ldg() so should not use this
                getMessageVariableLDGImpl << "            if (strings_equal(name, \"" << element.first << "\")) {\n";
                getMessageVariableLDGImpl << "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
                getMessageVariableLDGImpl << "                if(sizeof(T) != " << element.second.type_size << ") {\n";
                getMessageVariableLDGImpl << "                    DTHROW(\"Message variable '%s' type mismatch during getVariable().\\n\", name);\n";
                getMessageVariableLDGImpl << "                    return {};\n";
                getMessageVariableLDGImpl << "                }\n";
                getMessageVariableLDGImpl << "#endif\n";
                getMessageVariableLDGImpl << "                return (T) __ldg((*static_cast<T**>(static_cast<void*>(flamegpu::detail::curve::" << getVariableSymbolName() << " + " << messageIn_data_offset + (ct * sizeof(void*)) << "))) + index);\n";
                getMessageVariableLDGImpl << "            }\n";
                ++ct;  // Prev was part of the return line, but don't want confusion
            } else { ++ct; }
        }
        getMessageVariableLDGImpl <<         "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
        getMessageVariableLDGImpl <<         "            DTHROW(\"Message variable '%s' was not found during getVariable().\\n\", name);\n";
        getMessageVariableLDGImpl <<         "#endif\n";
        getMessageVariableLDGImpl <<         "            return {};\n";
        setHeaderPlaceholder("$DYNAMIC_GETMESSAGEVARIABLE_LDG_IMPL", getMessageVariableLDGImpl.str());
    }
    // generate getAgentArrayVariable func implementation ($DYNAMIC_GETAGENTARRAYVARIABLE_IMPL)
    {
        size_t ct = 0;
        std::stringstream getAgentArrayVariableImpl;
        if (!agent_variables.empty())
            getAgentArrayVariableImpl << "    const size_t i = (index * detail::type_decode<T>::len_t * N) + detail::type_decode<T>::len_t * array_index;\n";
        for (const auto &element : agent_variables) {
            RTCVariableProperties props = element.second;
            if (props.read && props.elements > 1) {
                getAgentArrayVariableImpl << "          if (strings_equal(name, \"" << element.first << "\")) {\n";
                getAgentArrayVariableImpl << "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
                getAgentArrayVariableImpl << "              const unsigned int t_index = detail::type_decode<T>::len_t * array_index + detail::type_decode<T>::len_t;\n";
                getAgentArrayVariableImpl << "              if(sizeof(detail::type_decode<T>::type_t) != " << element.second.type_size << ") {\n";
                getAgentArrayVariableImpl << "                  DTHROW(\"Agent array variable '%s' type mismatch during getVariable().\\n\", name);\n";
                getAgentArrayVariableImpl << "                  return {};\n";
                getAgentArrayVariableImpl << "              } else if (detail::type_decode<T>::len_t * N != " << element.second.elements << ") {\n";
                getAgentArrayVariableImpl << "                  DTHROW(\"Agent array variable '%s' length mismatch during getVariable().\\n\", name);\n";
                getAgentArrayVariableImpl << "                  return {};\n";
                getAgentArrayVariableImpl << "              } else if (t_index > " << element.second.elements << " || t_index < array_index) {\n";
                getAgentArrayVariableImpl << "                  DTHROW(\"Agent array variable '%s', index %d is out of bounds during getVariable().\\n\", name, array_index);\n";
                getAgentArrayVariableImpl << "                  return {};\n";
                getAgentArrayVariableImpl << "              }\n";
                getAgentArrayVariableImpl << "#endif\n";
                getAgentArrayVariableImpl << "              return (*static_cast<T**>(static_cast<void*>(flamegpu::detail::curve::" << getVariableSymbolName() << " + " << agent_data_offset + (ct++ * sizeof(void*)) << ")))[i];\n";
                getAgentArrayVariableImpl << "           };\n";
            } else { ++ct; }
        }
        getAgentArrayVariableImpl <<         "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
        getAgentArrayVariableImpl <<         "           DTHROW(\"Agent array variable '%s' was not found during getVariable().\\n\", name);\n";
        getAgentArrayVariableImpl <<         "#endif\n";
        getAgentArrayVariableImpl <<         "           return {};\n";
        setHeaderPlaceholder("$DYNAMIC_GETAGENTARRAYVARIABLE_IMPL", getAgentArrayVariableImpl.str());
    }
    // generate getMessageArrayVariable func implementation ($DYNAMIC_GETMESSAGEARRAYVARIABLE_IMPL)
    {
        size_t ct = 0;
        std::stringstream getMessageArrayVariableImpl;
        if (!messageIn_variables.empty())
            getMessageArrayVariableImpl << "    const size_t i = (index * detail::type_decode<T>::len_t * N) + detail::type_decode<T>::len_t * array_index;\n";
        for (const auto& element : messageIn_variables) {
            RTCVariableProperties props = element.second;
            if (props.read && props.elements > 1) {
                getMessageArrayVariableImpl << "          if (strings_equal(name, \"" << element.first << "\")) {\n";
                getMessageArrayVariableImpl << "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
                getMessageArrayVariableImpl << "              const unsigned int t_index = detail::type_decode<T>::len_t * array_index + detail::type_decode<T>::len_t;\n";
                getMessageArrayVariableImpl << "              if(sizeof(detail::type_decode<T>::type_t) != " << element.second.type_size << ") {\n";
                getMessageArrayVariableImpl << "                  DTHROW(\"Message array variable '%s' type mismatch during getVariable().\\n\", name);\n";
                getMessageArrayVariableImpl << "                  return {};\n";
                getMessageArrayVariableImpl << "              } else if (detail::type_decode<T>::len_t * N != " << element.second.elements << ") {\n";
                getMessageArrayVariableImpl << "                  DTHROW(\"Message array variable '%s' length mismatch during getVariable().\\n\", name);\n";
                getMessageArrayVariableImpl << "                  return {};\n";
                getMessageArrayVariableImpl << "              } else if (t_index > " << element.second.elements << " || t_index < array_index) {\n";
                getMessageArrayVariableImpl << "                  DTHROW(\"Message array variable '%s', index %d is out of bounds during getVariable().\\n\", name, array_index);\n";
                getMessageArrayVariableImpl << "                  return {};\n";
                getMessageArrayVariableImpl << "              }\n";
                getMessageArrayVariableImpl << "#endif\n";
                getMessageArrayVariableImpl << "              return (*static_cast<T**>(static_cast<void*>(flamegpu::detail::curve::" << getVariableSymbolName() << " + " << messageIn_data_offset + (ct++ * sizeof(void*)) << ")))[i];\n";
                getMessageArrayVariableImpl << "           };\n";
            } else { ++ct; }
        }
        getMessageArrayVariableImpl << "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
        getMessageArrayVariableImpl << "           DTHROW(\"Message array variable '%s' was not found during getVariable().\\n\", name);\n";
        getMessageArrayVariableImpl << "#endif\n";
        getMessageArrayVariableImpl << "           return {};\n";
        setHeaderPlaceholder("$DYNAMIC_GETMESSAGEARRAYVARIABLE_IMPL", getMessageArrayVariableImpl.str());
    }
    // generate getEnvironmentDirectedGraphVertexArrayProperty func implementation ($DYNAMIC_GETDIRECTEDGRAPHVERTEXARRAYPROPERTY_IMPL)
    {
        size_t ct = 0;
        std::stringstream getGraphVertexArrayPropertyImpl;
        if (!directedGraph_vertexProperties.empty())
            getGraphVertexArrayPropertyImpl << "    const size_t i = (index * type_decode<T>::len_t * N) + type_decode<T>::len_t * array_index;\n";
        for (const auto& element : directedGraph_vertexProperties) {
            RTCVariableProperties props = element.second;
            if (props.read && props.elements > 1) {
                getGraphVertexArrayPropertyImpl << "          if (strings_equal(name, \"" << element.first.second << "\") && graphHash == " << Curve::variableRuntimeHash(element.first.first) << ") {\n";
                getGraphVertexArrayPropertyImpl << "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
                getGraphVertexArrayPropertyImpl << "              const unsigned int t_index = type_decode<T>::len_t * array_index + type_decode<T>::len_t;\n";
                getGraphVertexArrayPropertyImpl << "              if(sizeof(type_decode<T>::type_t) != " << element.second.type_size << ") {\n";
                getGraphVertexArrayPropertyImpl << "                  DTHROW(\"Directed graph vertex array property '%s' type mismatch during getProperty().\\n\", name);\n";
                getGraphVertexArrayPropertyImpl << "                  return {};\n";
                getGraphVertexArrayPropertyImpl << "              } else if (type_decode<T>::len_t * N != " << element.second.elements << ") {\n";
                getGraphVertexArrayPropertyImpl << "                  DTHROW(\"Directed graph vertex array property '%s' length mismatch during getProperty().\\n\", name);\n";
                getGraphVertexArrayPropertyImpl << "                  return {};\n";
                getGraphVertexArrayPropertyImpl << "              } else if (t_index > " << element.second.elements << " || t_index < array_index) {\n";
                getGraphVertexArrayPropertyImpl << "                  DTHROW(\"Directed graph vertex array property '%s', index %d is out of bounds during getProperty().\\n\", name, array_index);\n";
                getGraphVertexArrayPropertyImpl << "                  return {};\n";
                getGraphVertexArrayPropertyImpl << "              }\n";
                getGraphVertexArrayPropertyImpl << "#endif\n";
                getGraphVertexArrayPropertyImpl << "              return (*static_cast<T**>(static_cast<void*>(flamegpu::detail::curve::" << getVariableSymbolName() << " + " << directedGraphVertex_data_offset + (ct++ * sizeof(void*)) << ")))[i];\n";
                getGraphVertexArrayPropertyImpl << "           };\n";
            } else { ++ct; }
        }
        getGraphVertexArrayPropertyImpl << "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
        getGraphVertexArrayPropertyImpl << "           DTHROW(\"Directed graph vertex array property '%s' was not found during getProperty().\\n\", name);\n";
        getGraphVertexArrayPropertyImpl << "#endif\n";
        getGraphVertexArrayPropertyImpl << "           return {};\n";
        setHeaderPlaceholder("$DYNAMIC_GETDIRECTEDGRAPHVERTEXARRAYPROPERTY_IMPL", getGraphVertexArrayPropertyImpl.str());
    }
    // generate getEnvironmentDirectedGraphEdgeArrayProperty func implementation ($DYNAMIC_GETDIRECTEDGRAPHEDGEARRAYPROPERTY_IMPL)
    {
        size_t ct = 0;
        std::stringstream getGraphEdgeArrayPropertyImpl;
        if (!directedGraph_edgeProperties.empty())
            getGraphEdgeArrayPropertyImpl << "    const size_t i = (index * type_decode<T>::len_t * N) + type_decode<T>::len_t * array_index;\n";
        for (const auto& element : directedGraph_edgeProperties) {
            RTCVariableProperties props = element.second;
            if (props.read && props.elements > 1) {
                getGraphEdgeArrayPropertyImpl << "          if (strings_equal(name, \"" << element.first.second << "\") && graphHash == " << Curve::variableRuntimeHash(element.first.first) << ") {\n";
                getGraphEdgeArrayPropertyImpl << "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
                getGraphEdgeArrayPropertyImpl << "              const unsigned int t_index = type_decode<T>::len_t * array_index + type_decode<T>::len_t;\n";
                getGraphEdgeArrayPropertyImpl << "              if(sizeof(type_decode<T>::type_t) != " << element.second.type_size << ") {\n";
                getGraphEdgeArrayPropertyImpl << "                  DTHROW(\"Directed graph edge array property '%s' type mismatch during getProperty().\\n\", name);\n";
                getGraphEdgeArrayPropertyImpl << "                  return {};\n";
                getGraphEdgeArrayPropertyImpl << "              } else if (type_decode<T>::len_t * N != " << element.second.elements << ") {\n";
                getGraphEdgeArrayPropertyImpl << "                  DTHROW(\"Directed graph edge array property '%s' length mismatch during getProperty().\\n\", name);\n";
                getGraphEdgeArrayPropertyImpl << "                  return {};\n";
                getGraphEdgeArrayPropertyImpl << "              } else if (t_index > " << element.second.elements << " || t_index < array_index) {\n";
                getGraphEdgeArrayPropertyImpl << "                  DTHROW(\"Directed graph edge array property '%s', index %d is out of bounds during getProperty().\\n\", name, array_index);\n";
                getGraphEdgeArrayPropertyImpl << "                  return {};\n";
                getGraphEdgeArrayPropertyImpl << "              }\n";
                getGraphEdgeArrayPropertyImpl << "#endif\n";
                getGraphEdgeArrayPropertyImpl << "              return (*static_cast<T**>(static_cast<void*>(flamegpu::detail::curve::" << getVariableSymbolName() << " + " << directedGraphEdge_data_offset + (ct++ * sizeof(void*)) << ")))[i];\n";
                getGraphEdgeArrayPropertyImpl << "           };\n";
            } else { ++ct; }
        }
        getGraphEdgeArrayPropertyImpl << "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
        getGraphEdgeArrayPropertyImpl << "           DTHROW(\"Directed graph edge array property '%s' was not found during getProperty().\\n\", name);\n";
        getGraphEdgeArrayPropertyImpl << "#endif\n";
        getGraphEdgeArrayPropertyImpl << "           return {};\n";
        setHeaderPlaceholder("$DYNAMIC_GETDIRECTEDGRAPHEDGEARRAYPROPERTY_IMPL", getGraphEdgeArrayPropertyImpl.str());
    }
    // generate getAgentArrayVariable_ldg func implementation ($DYNAMIC_GETAGENTARRAYVARIABLE_LDG_IMPL)
    {
        size_t ct = 0;
        std::stringstream getAgentArrayVariableLDGImpl;
        if (!agent_variables.empty())
            getAgentArrayVariableLDGImpl <<             "    const size_t i = (index * N) + array_index;\n";
        for (std::pair<std::string, RTCVariableProperties> element : agent_variables) {
            RTCVariableProperties props = element.second;
            if (props.read && props.elements > 1) {  // GLM does not support __ldg() so should not use this
                getAgentArrayVariableLDGImpl << "          if (strings_equal(name, \"" << element.first << "\")) {\n";
                getAgentArrayVariableLDGImpl << "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
                getAgentArrayVariableLDGImpl << "              if(sizeof(T) != " << element.second.type_size << ") {\n";
                getAgentArrayVariableLDGImpl << "                  DTHROW(\"Agent array variable '%s' type mismatch during getVariable().\\n\", name);\n";
                getAgentArrayVariableLDGImpl << "                  return {};\n";
                getAgentArrayVariableLDGImpl << "              } else if (N != " << element.second.elements << ") {\n";
                getAgentArrayVariableLDGImpl << "                  DTHROW(\"Agent array variable '%s' length mismatch during getVariable().\\n\", name);\n";
                getAgentArrayVariableLDGImpl << "                  return {};\n";
                getAgentArrayVariableLDGImpl << "              } else if (array_index >= " << element.second.elements << ") {\n";
                getAgentArrayVariableLDGImpl << "                  DTHROW(\"Agent array variable '%s', index %d is out of bounds during getVariable().\\n\", name, array_index);\n";
                getAgentArrayVariableLDGImpl << "                  return {};\n";
                getAgentArrayVariableLDGImpl << "              }\n";
                getAgentArrayVariableLDGImpl << "#endif\n";
                getAgentArrayVariableLDGImpl << "                return (T) __ldg((*static_cast<T**>(static_cast<void*>(flamegpu::detail::curve::" << getVariableSymbolName() << " + " << agent_data_offset + (ct * sizeof(void*)) << "))) + i);\n";
                getAgentArrayVariableLDGImpl << "           };\n";
                ++ct;  // Prev was part of the return line, but don't want confusion
            } else { ++ct; }
        }
        getAgentArrayVariableLDGImpl <<         "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
        getAgentArrayVariableLDGImpl <<         "           DTHROW(\"Agent array variable '%s' was not found during getVariable().\\n\", name);\n";
        getAgentArrayVariableLDGImpl <<         "#endif\n";
        getAgentArrayVariableLDGImpl <<         "           return {};\n";
        setHeaderPlaceholder("$DYNAMIC_GETAGENTARRAYVARIABLE_LDG_IMPL", getAgentArrayVariableLDGImpl.str());
    }
    // generate getMessageArrayVariable func implementation ($DYNAMIC_GETMESSAGEARRAYVARIABLE_LDG_IMPL)
    {
        size_t ct = 0;
        std::stringstream getMessageArrayVariableLDGImpl;
        if (!messageIn_variables.empty())
            getMessageArrayVariableLDGImpl << "    const size_t i = (index * N) + array_index;\n";
        for (const auto& element : messageIn_variables) {
            RTCVariableProperties props = element.second;
            if (props.read && props.elements > 1) {  // GLM does not support __ldg() so should not use this
                getMessageArrayVariableLDGImpl << "          if (strings_equal(name, \"" << element.first << "\")) {\n";
                getMessageArrayVariableLDGImpl << "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
                getMessageArrayVariableLDGImpl << "              if(sizeof(T) != " << element.second.type_size << ") {\n";
                getMessageArrayVariableLDGImpl << "                  DTHROW(\"Message array variable '%s' type mismatch during getVariable().\\n\", name);\n";
                getMessageArrayVariableLDGImpl << "                  return {};\n";
                getMessageArrayVariableLDGImpl << "              } else if (N != " << element.second.elements << ") {\n";
                getMessageArrayVariableLDGImpl << "                  DTHROW(\"Message array variable '%s' length mismatch during getVariable().\\n\", name);\n";
                getMessageArrayVariableLDGImpl << "                  return {};\n";
                getMessageArrayVariableLDGImpl << "              } else if (array_index >= " << element.second.elements << ") {\n";
                getMessageArrayVariableLDGImpl << "                  DTHROW(\"Message array variable '%s', index %d is out of bounds during getVariable().\\n\", name, array_index);\n";
                getMessageArrayVariableLDGImpl << "                  return {};\n";
                getMessageArrayVariableLDGImpl << "              }\n";
                getMessageArrayVariableLDGImpl << "#endif\n";
                getMessageArrayVariableLDGImpl << "                return (T) __ldg((*static_cast<T**>(static_cast<void*>(flamegpu::detail::curve::" << getVariableSymbolName() << " + " << messageIn_data_offset + (ct * sizeof(void*)) << "))) + i);\n";
                getMessageArrayVariableLDGImpl << "           };\n";
                ++ct;  // Prev was part of the return line, but don't want confusion
            } else { ++ct; }
        }
        getMessageArrayVariableLDGImpl << "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
        getMessageArrayVariableLDGImpl << "           DTHROW(\"Message array variable '%s' was not found during getVariable().\\n\", name);\n";
        getMessageArrayVariableLDGImpl << "#endif\n";
        getMessageArrayVariableLDGImpl << "           return {};\n";
        setHeaderPlaceholder("$DYNAMIC_GETMESSAGEARRAYVARIABLE_LDG_IMPL", getMessageArrayVariableLDGImpl.str());
    }

    // generate getGraphHash func implementation ($DYNAMIC_GETGRAPHHASH_IMPL)
    // This is bespoke to RTC Curve, as in place of getVariableIndex()
    {
        std::set<std::string> graphs_added;
        std::stringstream getGraphHashImpl;
        for (const auto& element : directedGraph_vertexProperties) {
            if (graphs_added.find(element.first.first) == graphs_added.end()) {
                getGraphHashImpl << "          if (strings_equal(graphName, \"" << element.first.first << "\")) {\n";
                getGraphHashImpl << "                return " << Curve::variableRuntimeHash(element.first.first) << ";\n";
                getGraphHashImpl << "          }\n";
                graphs_added.insert(element.first.first);
            }
        }
        getGraphHashImpl << "          return UNKNOWN_GRAPH;\n";
        setHeaderPlaceholder("$DYNAMIC_GETGRAPHHASH_IMPL", getGraphHashImpl.str());
    }

    // generate getVariableCount func implementation ($DYNAMIC_GETVARIABLECOUNT_IMPL)
    {
        std::stringstream getVariableCountImpl;
        for (const auto& element : directedGraph_vertexProperties) {
            getVariableCountImpl << "    if (namespace_hash == " << (Curve::variableRuntimeHash(element.first.first) ^ Curve::variableRuntimeHash("_environment_directed_graph_vertex")) << " && strings_equal(variableName, \"" << element.first.second << "\"))\n";
            getVariableCountImpl << "        return reinterpret_cast<unsigned int*>(reinterpret_cast<void*>(flamegpu::detail::curve::" << getVariableSymbolName() << " + " << count_data_offset << "))["<< element.second.count_index <<"];";
        }
        for (const auto& element : directedGraph_edgeProperties) {
            getVariableCountImpl << "    if (namespace_hash == " << (Curve::variableRuntimeHash(element.first.first) ^ Curve::variableRuntimeHash("_environment_directed_graph_edge")) << " && strings_equal(variableName, \"" << element.first.second << "\"))\n";
            getVariableCountImpl << "        return reinterpret_cast<unsigned int*>(reinterpret_cast<void*>(flamegpu::detail::curve::" << getVariableSymbolName() << " + " << count_data_offset << "))[" << element.second.count_index << "];";
        }
        // Note, the below are currently unused so untested (but support should be functional)
        for (const auto& element : agent_variables) {
            getVariableCountImpl << "    if (namespace_hash == 0 && strings_equal(variableName, \"" << element.first << "\"))\n";
            getVariableCountImpl << "        return reinterpret_cast<unsigned int*>(reinterpret_cast<void*>(flamegpu::detail::curve::" << getVariableSymbolName() << " + " << count_data_offset << "))[" << element.second.count_index << "];\n";
        }
        for (const auto& element : messageOut_variables) {
            getVariableCountImpl << "    if (namespace_hash == " << Curve::variableRuntimeHash("_message_out") << " && strings_equal(variableName, \"" << element.first << "\"))\n";
            getVariableCountImpl << "        return reinterpret_cast<unsigned int*>(reinterpret_cast<void*>(flamegpu::detail::curve::" << getVariableSymbolName() << " + " << count_data_offset << "))[" << element.second.count_index << "];\n";
        }
        for (const auto& element : messageIn_variables) {
            getVariableCountImpl << "    if (namespace_hash == " << Curve::variableRuntimeHash("_message_in") << " && strings_equal(variableName, \"" << element.first << "\"))\n";
            getVariableCountImpl << "        return reinterpret_cast<unsigned int*>(reinterpret_cast<void*>(flamegpu::detail::curve::" << getVariableSymbolName() << " + " << count_data_offset << "))[" << element.second.count_index << "];\n";
        }
        for (const auto& element : newAgent_variables) {
            getVariableCountImpl << "    if (namespace_hash == " << Curve::variableRuntimeHash("_agent_birth") << " && strings_equal(variableName, \"" << element.first << "\"))\n";
            getVariableCountImpl << "        return reinterpret_cast<unsigned int*>(reinterpret_cast<void*>(flamegpu::detail::curve::" << getVariableSymbolName() << " + " << count_data_offset << "))[" << element.second.count_index << "];\n";
        }
        getVariableCountImpl << "#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS\n";
        getVariableCountImpl << "    DTHROW(\"Curve variable with name '%s' was not found.\\n\", variableName);\n";
        getVariableCountImpl << "#endif\n";
        getVariableCountImpl << "          return 0;\n";
        setHeaderPlaceholder("$DYNAMIC_GETVARIABLECOUNT_IMPL", getVariableCountImpl.str());
    }
    setHeaderPlaceholder("$DYNAMIC_AGENT_NAME", this->agentName);
    setHeaderPlaceholder("$DYNAMIC_AGENT_STATE", this->agentState);
}
void CurveRTCHost::initDataBuffer() {
    if (data_buffer_size == 0 || h_data_buffer) {
        THROW exception::InvalidOperation("CurveRTCHost::initDataBuffer() should only be called once, during the init chain.\n");
    }
    // Alloc buffer
    gpuErrchk(cudaMallocHost(&h_data_buffer, data_buffer_size));
    // Notify all variables of their ptr to store data in cache
    size_t ct = 0;
    for (auto &element : agent_variables) {
        element.second.h_data_ptr = h_data_buffer + agent_data_offset + (ct++ * sizeof(void*));
    }
    ct = 0;
    for (auto &element : messageOut_variables) {
        element.second.h_data_ptr = h_data_buffer + messageOut_data_offset + (ct++ * sizeof(void*));
    }
    ct = 0;
    for (auto &element : messageIn_variables) {
        element.second.h_data_ptr = h_data_buffer + messageIn_data_offset + (ct++ * sizeof(void*));
    }
    ct = 0;
    for (auto &element : newAgent_variables) {
        element.second.h_data_ptr = h_data_buffer + newAgent_data_offset + (ct++ * sizeof(void*));
    }
    ct = 0;
    for (auto& element : directedGraph_vertexProperties) {
        element.second.h_data_ptr = h_data_buffer + directedGraphVertex_data_offset + (ct++ * sizeof(void*));
    }
    ct = 0;
    for (auto& element : directedGraph_edgeProperties) {
        element.second.h_data_ptr = h_data_buffer + directedGraphEdge_data_offset + (ct++ * sizeof(void*));
    }
    ct = 0;
    for (auto& element : RTCEnvMacroProperties) {
        element.second.h_data_ptr = h_data_buffer + envMacro_data_offset + (ct++ * sizeof(void*));
        // Env macro properties don't update, so fill them as we go
        memcpy(element.second.h_data_ptr, &element.second.d_ptr, sizeof(void*));
    }
}

void CurveRTCHost::setFileName(const std::string &filename) {
    setHeaderPlaceholder("$FILENAME", filename);
}

std::string CurveRTCHost::getDynamicHeader(const size_t env_buffer_len) {
    initHeaderEnvironment(env_buffer_len);
    initHeaderSetters();
    initHeaderGetters();
    initDataBuffer();
    return header;
}

void CurveRTCHost::setHeaderPlaceholder(std::string placeholder, std::string dst) {
    // replace placeholder with dynamically generated variables string
    size_t pos = header.find(placeholder);
    if (pos != std::string::npos) {
        header.replace(pos, placeholder.length(), dst);
    } else {
        THROW exception::UnknownInternalError("String (%s) not found when creating dynamic version of curve for RTC: in CurveRTCHost::setHeaderPlaceholder", placeholder.c_str());
    }
}

std::string CurveRTCHost::getVariableSymbolName() {
    std::stringstream name;
    name << "rtc_env_data_curve";
    return name.str();
}

std::string CurveRTCHost::demangle(const char* verbose_name) {
#ifndef _MSC_VER
    std::string s = jitify::reflection::detail::demangle_cuda_symbol(verbose_name);
#else
    // Jitify removed the required demangle function, this is a basic clone of what was being done in earlier version
    // It's possible jitify::reflection::detail::demangle_native_type() would work, however that requires type_info, not type_index
    size_t index = 0;
    std::string s = verbose_name;
    while (true) {
        /* Locate the substring to replace. */
        index = s.find("class", index);
        if (index == std::string::npos) break;

        /* Make the replacement. */
        s.replace(index, 5, "     ");

        /* Advance index forward so the next iteration doesn't pick it up as well. */
        index += 5;
    }
#endif
    // Lambda function for trimming whitesapce as jitify demangle does not remove this
    s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) {
        return !std::isspace(ch);
        }));
#ifdef _MSC_VER
    // int64_t is the only known problematic type in windows as it has a typeid().name() of __int64.
    // This can be manually replaced
    std::string int64_type = "__int64";
    std::string int64_type_fixed = "long long int";
    size_t start_pos = s.find(int64_type);
    if (!(start_pos == std::string::npos))
        s.replace(start_pos, int64_type.length(), int64_type_fixed);
#endif

    // map known basic types in
    return s;
}

std::string CurveRTCHost::demangle(const std::type_index& type) {
    return demangle(type.name());
}
void CurveRTCHost::updateEnvCache(const void *env_ptr, const size_t bufferLen) {
    if (bufferLen <= agent_data_offset) {
        memcpy(h_data_buffer, env_ptr, bufferLen);
    } else {
        THROW exception::OutOfBoundsException("Provided bufferlen exceeds initialised env buffer len! %llu > %llu, "
        "in CurveRTCHost::updateEnvCache().",
            bufferLen, agent_data_offset);
    }
}
void CurveRTCHost::updateDevice_async(const jitify::experimental::KernelInstantiation& instance, cudaStream_t stream) {
    // Move count buffer into h_data_buffer first
    memcpy(h_data_buffer + count_data_offset, count_buffer.data(), count_buffer.size() * sizeof(unsigned int));
    // The namespace is required here, but not in other uses of getVariableSymbolName.
    std::string cache_var_name = std::string("flamegpu::detail::curve::") + getVariableSymbolName();
    CUdeviceptr d_var_ptr = instance.get_global_ptr(cache_var_name.c_str());
    gpuErrchkDriverAPI(cuMemcpyHtoDAsync(d_var_ptr, h_data_buffer, data_buffer_size, stream));
}

}  // namespace curve
}  // namespace detail
}  // namespace flamegpu