Program Listing for File HostNewAgentAPI.h
↰ Return to documentation for file (include/flamegpu/runtime/agent/HostNewAgentAPI.h
)
#ifndef INCLUDE_FLAMEGPU_RUNTIME_AGENT_HOSTNEWAGENTAPI_H_
#define INCLUDE_FLAMEGPU_RUNTIME_AGENT_HOSTNEWAGENTAPI_H_
#include <unordered_map>
#include <string>
#include <vector>
#include "flamegpu/model/Variable.h"
#include "flamegpu/defines.h"
#include "flamegpu/detail/type_decode.h"
namespace flamegpu {
struct VarOffsetStruct {
struct OffsetLen {
const ptrdiff_t offset;
const size_t len;
const std::type_index type;
OffsetLen(const ptrdiff_t &_offset, const size_t _len, const std::type_index _type)
: offset(_offset)
, len(_len)
, type(_type) { }
bool operator==(const OffsetLen& other) const {
return offset == other.offset && len == other.len && type == other.type;
}
};
std::unordered_map<std::string, OffsetLen> vars;
const size_t totalSize;
char *const default_data;
explicit VarOffsetStruct(const VariableMap &vmap)
: totalSize(buildVars(vmap))
, default_data(reinterpret_cast<char*>(malloc(totalSize))) {
// Fill default_data with default struct
memset(default_data, 0, totalSize);
for (const auto &a : vmap) {
const auto &b = vars.at(a.first);
if (a.second.default_value)
memcpy(default_data + b.offset, a.second.default_value, b.len);
}
}
explicit VarOffsetStruct(const VarOffsetStruct &other)
: vars(other.vars)
, totalSize(other.totalSize)
, default_data(reinterpret_cast<char*>(malloc(other.totalSize))) {
memcpy(default_data, other.default_data, totalSize);
}
~VarOffsetStruct() {
free(default_data);
}
private:
size_t buildVars(const VariableMap &vmap) {
size_t _totalAgentSize = 0;
for (const auto &a : vmap) {
vars.emplace(a.first, OffsetLen(_totalAgentSize, a.second.type_size * a.second.elements, a.second.type));
_totalAgentSize += a.second.type_size * a.second.elements;
}
return _totalAgentSize;
}
};
struct NewAgentStorage {
explicit NewAgentStorage(const VarOffsetStruct &v, id_t id)
: data(reinterpret_cast<char*>(malloc(v.totalSize)))
, offsets(v) {
memcpy(data, offsets.default_data, offsets.totalSize);
// Overwrite _id value
const auto& var = offsets.vars.find(ID_VARIABLE_NAME);
if (var == offsets.vars.end()) {
THROW exception::InvalidOperation("Internal agent ID variable was not found, "
"in NewAgentStorage.NewAgentStorage().");
}
// Don't bother checking type/len
memcpy(data + var->second.offset, &id, sizeof(id_t));
}
NewAgentStorage(const NewAgentStorage &other) = delete;
NewAgentStorage(NewAgentStorage &&other) noexcept
: data(other.data)
, offsets(other.offsets) {
other.data = nullptr;
}
NewAgentStorage& operator=(const NewAgentStorage& hna) {
if (offsets.vars == hna.offsets.vars) {
// Iterate and copy all vars individually, skip those marked as internal
for (const auto &off : offsets.vars) {
if (off.first[0] != '_') {
memcpy(this->data + off.second.offset, hna.data + off.second.offset, off.second.len);
}
}
} else {
THROW exception::InvalidArgument("Attempting to assign data from agent of different type, in NewAgentStorage::operator=()\n");
}
return *this;
}
~NewAgentStorage() {
if (data)
free(data);
}
template<typename T>
void setVariable(const std::string &var_name, const T val) {
const auto &var = offsets.vars.find(var_name);
if (var == offsets.vars.end()) {
THROW exception::InvalidAgentVar("Variable '%s' not found, "
"in NewAgentStorage::setVariable().",
var_name.c_str());
}
const auto t_type = std::type_index(typeid(typename detail::type_decode<T>::type_t));
if (var->second.type != t_type) {
THROW exception::InvalidVarType("Variable '%s' has type '%s', incorrect type '%s' was requested, "
"in NewAgentStorage::setVariable().",
var_name.c_str(), var->second.type.name(), t_type.name());
}
if (var->second.len != sizeof(typename detail::type_decode<T>::type_t) * detail::type_decode<T>::len_t) {
THROW exception::InvalidAgentVar("This method is not suitable for agent array variables, "
" variable '%s' was passed, "
"in NewAgentStorage::setVariable().",
var_name.c_str());
}
memcpy(data + var->second.offset, &val, var->second.len);
}
template<typename T, unsigned int N = 0>
void setVariable(const std::string &var_name, const unsigned int index, const T val) {
const auto &var = offsets.vars.find(var_name);
if (var == offsets.vars.end()) {
THROW exception::InvalidAgentVar("Variable '%s' not found, "
"in NewAgentStorage::setVariable().",
var_name.c_str());
}
if (N && N != var->second.len / sizeof(typename detail::type_decode<T>::type_t)) {
THROW exception::InvalidAgentVar("Agent variable '%s' length mismatch %u != %u, "
"in NewAgentStorage::setVariable().",
var_name.c_str(), N, var->second.len / sizeof(typename detail::type_decode<T>::type_t));
}
const auto t_type = std::type_index(typeid(typename detail::type_decode<T>::type_t));
if (var->second.type != t_type) {
THROW exception::InvalidVarType("Variable '%s' has type '%s, incorrect type '%s' was requested, "
"in NewAgentStorage::setVariable().",
var_name.c_str(), var->second.type.name(), t_type.name());
}
if (var->second.len < (sizeof(typename detail::type_decode<T>::type_t) * detail::type_decode<T>::len_t) * (index + 1)) {
THROW exception::OutOfRangeVarArray("Variable '%s' is an array with %u elements, index %u is out of range, "
"in NewAgentStorage::setVariable().",
var_name.c_str(), var->second.len / (sizeof(typename detail::type_decode<T>::type_t) * detail::type_decode<T>::len_t), index);
}
memcpy(data + var->second.offset + (index * sizeof(typename detail::type_decode<T>::type_t) * detail::type_decode<T>::len_t), &val, sizeof(typename detail::type_decode<T>::type_t) * detail::type_decode<T>::len_t);
}
#ifndef SWIG
template<typename T, unsigned int N>
void setVariable(const std::string &var_name, const std::array<T, N> &val) {
const auto &var = offsets.vars.find(var_name);
if (var == offsets.vars.end()) {
THROW exception::InvalidAgentVar("Variable '%s' not found, "
"in NewAgentStorage::setVariable().",
var_name.c_str());
}
// if (var.second.len == 1 || N == 1) {
// THROW exception::InvalidAgentVar("Agent variable '%s' in not an array variable, "
// "in NewAgentStorage::setVariable().",
// var_name.c_str());
// }
const auto t_type = std::type_index(typeid(typename detail::type_decode<T>::type_t));
if (var->second.type != t_type) {
THROW exception::InvalidVarType("Variable '%s' has type '%s, incorrect type '%s' was requested, "
"in NewAgentStorage::setVariable().",
var_name.c_str(), var->second.type.name(), t_type.name());
}
if (var->second.len != sizeof(typename detail::type_decode<T>::type_t) * detail::type_decode<T>::len_t * N) {
THROW exception::InvalidVarArrayLen("Variable '%s' is an array with %u elements, incorrect array of length %u was provided, "
"in NewAgentStorage::setVariable().",
var_name.c_str(), var->second.len / (sizeof(typename detail::type_decode<T>::type_t) * detail::type_decode<T>::len_t), N);
}
memcpy(data + var->second.offset, val.data(), var->second.len);
}
#else
template<typename T>
void setVariableArray(const std::string &var_name, const std::vector<T> &val) {
const auto &var = offsets.vars.find(var_name);
if (var == offsets.vars.end()) {
THROW exception::InvalidAgentVar("Variable '%s' not found, "
"in NewAgentStorage::setVariableArray().",
var_name.c_str());
}
// if (var.second.len == 1 || N == 1) {
// THROW exception::InvalidAgentVar("Agent variable '%s' in not an array variable, "
// "in NewAgentStorage::setVariableArray().",
// var_name.c_str());
// }
const auto t_type = std::type_index(typeid(typename detail::type_decode<T>::type_t));
if (var->second.type != t_type) {
THROW exception::InvalidVarType("Variable '%s' has type '%s, incorrect type '%s' was requested, "
"in NewAgentStorage::setVariableArray().",
var_name.c_str(), var->second.type.name(), t_type.name());
}
if (var->second.len != sizeof(typename detail::type_decode<T>::type_t) * detail::type_decode<T>::len_t * val.size()) {
THROW exception::InvalidVarArrayLen("Variable '%s' is an array with %u elements, incorrect array of length %u was provided, "
"in NewAgentStorage::setVariableArray().",
var_name.c_str(), var->second.len / (sizeof(typename detail::type_decode<T>::type_t) * detail::type_decode<T>::len_t), val.size());
}
memcpy(data + var->second.offset, val.data(), var->second.len);
}
#endif
template<typename T>
T getVariable(const std::string &var_name) const {
const auto &var = offsets.vars.find(var_name);
if (var == offsets.vars.end()) {
THROW exception::InvalidAgentVar("Variable '%s' not found, "
"in NewAgentStorage::getVariable()",
var_name.c_str());
}
const auto t_type = std::type_index(typeid(typename detail::type_decode<T>::type_t));
if (var->second.type != t_type) {
THROW exception::InvalidVarType("Variable '%s' has type '%s, incorrect type '%s' was requested, "
"in NewAgentStorage::getVariable().",
var_name.c_str(), var->second.type.name(), t_type.name());
}
if (var->second.len != sizeof(typename detail::type_decode<T>::type_t) * detail::type_decode<T>::len_t) {
THROW exception::InvalidAgentVar("This method is not suitable for agent array variables, "
" variable '%s' was passed, "
"in NewAgentStorage::getVariable().",
var_name.c_str());
}
return *reinterpret_cast<T*>(data + var->second.offset);
}
template<typename T, unsigned int N = 0>
T getVariable(const std::string &var_name, const unsigned int index) {
const auto &var = offsets.vars.find(var_name);
if (var == offsets.vars.end()) {
THROW exception::InvalidAgentVar("Variable '%s' not found, "
"in NewAgentStorage::getVariable().",
var_name.c_str());
}
if (N && N != var->second.len / sizeof(typename detail::type_decode<T>::type_t)) {
THROW exception::InvalidAgentVar("Agent variable '%s' length mismatch %u != %u, "
"in NewAgentStorage::getVariable().",
var_name.c_str(), N, var->second.len / sizeof(typename detail::type_decode<T>::type_t));
}
const auto t_type = std::type_index(typeid(typename detail::type_decode<T>::type_t));
if (var->second.type != t_type) {
THROW exception::InvalidVarType("Variable '%s' has type '%s, incorrect type '%s' was requested, "
"in NewAgentStorage::getVariable().",
var_name.c_str(), var->second.type.name(), t_type.name());
}
if (var->second.len < sizeof(typename detail::type_decode<T>::type_t) * detail::type_decode<T>::len_t * (index + 1)) {
THROW exception::OutOfRangeVarArray("Variable '%s' is an array with %u elements, index %u is out of range, "
"in NewAgentStorage::getVariable().",
var_name.c_str(), var->second.len / (sizeof(typename detail::type_decode<T>::type_t) * detail::type_decode<T>::len_t), index);
}
return *reinterpret_cast<T*>(data + var->second.offset + (index * sizeof(typename detail::type_decode<T>::type_t) * detail::type_decode<T>::len_t));
}
#ifndef SWIG
template<typename T, unsigned int N>
std::array<T, N> getVariable(const std::string &var_name) {
const auto &var = offsets.vars.find(var_name);
if (var == offsets.vars.end()) {
THROW exception::InvalidAgentVar("Variable '%s' not found, "
"in NewAgentStorage::getVariable().",
var_name.c_str());
}
// if (var.second.len == 1 || N == 1) {
// THROW exception::InvalidAgentVar("Agent variable '%s' in not an array variable, "
// "in NewAgentStorage::getVariable().",
// var_name.c_str());
// }
const auto t_type = std::type_index(typeid(typename detail::type_decode<T>::type_t));
if (var->second.type != t_type) {
THROW exception::InvalidVarType("Variable '%s' has type '%s, incorrect type '%s' was requested, "
"in NewAgentStorage::getVariable().",
var_name.c_str(), var->second.type.name(), t_type.name());
}
if (var->second.len != sizeof(typename detail::type_decode<T>::type_t) * detail::type_decode<T>::len_t * N) {
THROW exception::InvalidVarArrayLen("Variable '%s' is an array with %u elements, incorrect array of length %u was specified, "
"in NewAgentStorage::getVariable().",
var_name.c_str(), var->second.len / (sizeof(typename detail::type_decode<T>::type_t) * detail::type_decode<T>::len_t), N);
}
std::array<T, N> rtn;
memcpy(rtn.data(), data + var->second.offset, var->second.len);
return rtn;
}
#else
template<typename T>
std::vector<T> getVariableArray(const std::string &var_name) {
const auto &var = offsets.vars.find(var_name);
if (var == offsets.vars.end()) {
THROW exception::InvalidAgentVar("Variable '%s' not found, "
"in NewAgentStorage::getVariableArray().",
var_name.c_str());
}
const auto t_type = std::type_index(typeid(typename detail::type_decode<T>::type_t));
if (var->second.type != t_type) {
THROW exception::InvalidVarType("Variable '%s' has type '%s, incorrect type '%s' was requested, "
"in NewAgentStorage::getVariableArray().",
var_name.c_str(), var->second.type.name(), t_type.name());
}
if (var->second.len % (sizeof(typename detail::type_decode<T>::type_t) * detail::type_decode<T>::len_t) != 0) {
THROW exception::InvalidVarType("Variable '%s' has length (%llu) is not divisible by vector length (%u), "
"in NewAgentStorage::getVariableArray().",
var_name.c_str(), var->second.len / sizeof(typename detail::type_decode<T>::type_t), detail::type_decode<T>::len_t);
}
const size_t elements = var->second.len / (sizeof(typename detail::type_decode<T>::type_t) * detail::type_decode<T>::len_t);
std::vector<T> rtn(elements);
memcpy(rtn.data(), data + var->second.offset, var->second.len);
return rtn;
}
#endif
friend class CUDASimulation;
friend class DeviceAgentVector_impl;
private:
char *data;
const VarOffsetStruct &offsets;
};
class HostNewAgentAPI {
public:
explicit HostNewAgentAPI(NewAgentStorage &_s)
: s(&_s) { }
HostNewAgentAPI(const HostNewAgentAPI &hna)
: s(hna.s) { }
HostNewAgentAPI& operator=(const HostNewAgentAPI &hna) {
if (&hna != this)
*s = *hna.s;
return *this;
}
template<typename T>
void setVariable(const std::string &var_name, const T val) {
if (!var_name.empty() && var_name[0] == '_') {
THROW exception::ReservedName("Agent variable names cannot begin with '_', this is reserved for internal usage, "
"in HostNewAgentAPI::setVariable().");
}
s->setVariable<T>(var_name, val);
}
template<typename T, unsigned int N = 0>
void setVariable(const std::string &var_name, const unsigned int index, const T val) {
if (!var_name.empty() && var_name[0] == '_') {
THROW exception::ReservedName("Agent variable names cannot begin with '_', this is reserved for internal usage, "
"in HostNewAgentAPI::setVariable().");
}
s->setVariable<T, N>(var_name, index, val);
}
#ifndef SWIG
template<typename T, unsigned int N>
void setVariable(const std::string& var_name, const std::array<T, N>& val) {
if (!var_name.empty() && var_name[0] == '_') {
THROW exception::ReservedName("Agent variable names cannot begin with '_', this is reserved for internal usage, "
"in HostNewAgentAPI::setVariable().");
}
s->setVariable<T, N>(var_name, val);
}
#else
template<typename T>
void setVariableArray(const std::string &var_name, const std::vector<T> &val) {
if (!var_name.empty() && var_name[0] == '_') {
THROW exception::ReservedName("Agent variable names cannot begin with '_', this is reserved for internal usage, "
"in HostNewAgentAPI::setVariable().");
}
s->setVariableArray<T>(var_name, val);
}
#endif
template<typename T>
T getVariable(const std::string &var_name) const {
return s->getVariable<T>(var_name);
}
template<typename T, unsigned int N = 0>
T getVariable(const std::string &var_name, const unsigned int index) {
return s->getVariable<T, N>(var_name, index);
}
#ifndef SWIG
template<typename T, unsigned int N>
std::array<T, N> getVariable(const std::string& var_name) {
return s->getVariable<T, N>(var_name);
}
#else
template<typename T>
std::vector<T> getVariableArray(const std::string &var_name) {
return s->getVariableArray<T>(var_name);
}
#endif
id_t getID() const {
try {
return s->getVariable<id_t>(ID_VARIABLE_NAME);
} catch (...) {
// Rewrite all exceptions
THROW exception::UnknownInternalError("Internal Error: Unable to read internal ID variable, in HostNewAgentAPI::getID()\n");
}
}
private:
// Can't use reference here, makes it non-assignable
NewAgentStorage *s;
};
} // namespace flamegpu
#endif // INCLUDE_FLAMEGPU_RUNTIME_AGENT_HOSTNEWAGENTAPI_H_