Program Listing for File LayerDescription.h
↰ Return to documentation for file (include/flamegpu/model/LayerDescription.h
)
#ifndef INCLUDE_FLAMEGPU_MODEL_LAYERDESCRIPTION_H_
#define INCLUDE_FLAMEGPU_MODEL_LAYERDESCRIPTION_H_
#include <string>
#include <memory>
#include "flamegpu/model/ModelDescription.h"
#include "flamegpu/model/AgentDescription.h"
#include "flamegpu/model/ModelData.h"
#include "flamegpu/model/LayerData.h"
#include "flamegpu/runtime/AgentFunction.cuh"
#include "flamegpu/model/AgentFunctionData.cuh"
namespace flamegpu {
class CLayerDescription {
friend struct LayerData;
friend class DependencyGraph;
public:
explicit CLayerDescription(std::shared_ptr<LayerData> data);
explicit CLayerDescription(std::shared_ptr<const LayerData> data);
CLayerDescription(const CLayerDescription& other_layer) = default;
CLayerDescription(CLayerDescription&& other_layer) = default;
CLayerDescription& operator=(const CLayerDescription& other_layer) = default;
CLayerDescription& operator=(CLayerDescription&& other_layer) = default;
bool operator==(const CLayerDescription& rhs) const;
bool operator!=(const CLayerDescription& rhs) const;
std::string getName() const;
flamegpu::size_type getIndex() const;
flamegpu::size_type getAgentFunctionsCount() const;
flamegpu::size_type getHostFunctionsCount() const;
#ifdef SWIG
inline flamegpu::size_type getHostFunctionCallbackCount() const;
#endif
CAgentFunctionDescription getAgentFunction(unsigned int index) const;
FLAMEGPU_HOST_FUNCTION_POINTER getHostFunction(unsigned int index) const;
#ifdef SWIG
inline HostFunctionCallback* getHostFunctionCallback(unsigned int index) const;
#endif
protected:
std::shared_ptr<LayerData> layer;
};
class LayerDescription : public CLayerDescription {
friend class DependencyGraph;
public:
explicit LayerDescription(std::shared_ptr<LayerData> data);
LayerDescription(const LayerDescription& other_layer) = default;
LayerDescription(LayerDescription&& other_layer) = default;
LayerDescription& operator=(const LayerDescription& other_layer) = default;
LayerDescription& operator=(LayerDescription&& other_layer) = default;
template<typename AgentFn>
void addAgentFunction(AgentFn a = AgentFn());
void addAgentFunction(const CAgentFunctionDescription& afd);
void addAgentFunction(const AgentFunctionDescription &afd);
void addAgentFunction(const std::string &agentName, const std::string &functionName);
void addAgentFunction(const char *agentName, const char *functionName);
void addHostFunction(FLAMEGPU_HOST_FUNCTION_POINTER func_p);
void addSubModel(const std::string &name);
void addSubModel(const CSubModelDescription &submodel);
#ifdef SWIG
inline void addHostFunction(HostFunctionCallback *func_callback);
#endif
private:
void _addHostFunction(HostFunctionCallback* func_callback);
AgentFunctionDescription AgentFunction(unsigned int index);
};
template<typename AgentFn>
void LayerDescription::addAgentFunction(AgentFn /*af*/) {
if (layer->sub_model) {
THROW exception::InvalidLayerMember("A layer containing agent functions and/or host functions, may not also contain a submodel, "
"in LayerDescription::addAgentFunction()\n");
}
if (layer->host_functions.size() || layer->host_functions_callbacks.size()) {
THROW exception::InvalidLayerMember("A layer containing host functions, may not also contain agent functions, "
"in LayerDescription::addAgentFunction()\n");
}
AgentFunctionWrapper * func_compare = AgentFn::fnPtr();
// Find the matching agent function in model hierarchy
auto mdl = layer->model.lock();
if (!mdl) {
THROW exception::ExpiredWeakPtr();
}
unsigned int matches = 0;
std::shared_ptr<AgentFunctionData> match_ptr;
for (auto a : mdl->agents) {
for (auto f : a.second->functions) {
if (f.second->func == func_compare) {
auto a_agent_out = f.second->agent_output.lock();
auto a_message_out = f.second->message_output.lock();
auto a_message_in = f.second->message_input.lock();
for (const auto &b : layer->agent_functions) {
if (auto parent = b->parent.lock()) {
// Check that layer does not already contain function with same agent + states
// If agent matches
if (parent->name == a.second->name) {
// If they share a state
if (b->initial_state == f.second->initial_state ||
b->initial_state == f.second->end_state ||
b->end_state == f.second->initial_state ||
b->end_state == f.second->end_state) {
THROW exception::InvalidAgentFunc("Agent functions '%s' cannot be added to this layer as agent function '%s' "
"within the layer shares an input or output state, this is not permitted, "
"in LayerDescription::addAgentFunction().",
f.second->name.c_str(), b->name.c_str());
}
}
// Check that the layer does not already contain function for the agent + state being output to
if (a_agent_out) {
// If agent matches
if (parent->name == a_agent_out->name) {
// If state matches
if (b->initial_state == f.second->agent_output_state) {
THROW exception::InvalidLayerMember("Agent functions '%s' cannot be added to this layer as agent function '%s' "
"within the layer requires the same agent state as an input, as this agent function births, "
"in LayerDescription::addAgentFunction().",
f.second->name.c_str(), b->name.c_str());
}
}
}
// Also check the inverse
auto b_agent_out = b->agent_output.lock();
if (b_agent_out) {
// If agent matches
if (a.second->name == b_agent_out->name) {
// If state matches
if (f.second->initial_state == b->agent_output_state) {
THROW exception::InvalidLayerMember("Agent functions '%s' cannot be added to this layer as agent function '%s' "
"within the layer agent births to the same agent state as this agent function requires as an input, "
"in LayerDescription::addAgentFunction().",
f.second->name.c_str(), b->name.c_str());
}
}
}
}
// Check the layer does not already contain function which outputs to same message list
auto b_message_out = b->message_output.lock();
auto b_message_in = b->message_input.lock();
if ((a_message_out && b_message_out && a_message_out == b_message_out) ||
(a_message_out && b_message_in && a_message_out == b_message_in) ||
(a_message_in && b_message_out && a_message_in == b_message_out)) { // Pointer comparison should be fine here
THROW exception::InvalidLayerMember("Agent functions '%s' cannot be added to this layer as agent function '%s' "
"within the layer also inputs or outputs to the same messagelist, this is not permitted, "
"in LayerDescription::addAgentFunction().",
f.second->name.c_str(), b->name.c_str());
}
}
match_ptr = f.second;
++matches;
}
}
}
if (matches == 1) {
// Add it and check it succeeded
if (layer->agent_functions.emplace(match_ptr).second)
return;
THROW exception::InvalidAgentFunc("Attempted to add same agent function to same layer twice, "
"in LayerDescription::addAgentFunction().");
}
if (matches > 1) {
THROW exception::InvalidAgentFunc("There are %u possible agent functions to add to layer, please use a more specific method for adding this agent function to a layer, "
"in LayerDescription::addAgentFunction().", matches);
}
THROW exception::InvalidAgentFunc("Agent function was not found, "
"in LayerDescription::addAgentFunction().");
}
#ifdef SWIG
void LayerDescription::addHostFunction(HostFunctionCallback* func_callback) {
this->_addHostFunction(func_callback);
}
flamegpu::size_type CLayerDescription::getHostFunctionCallbackCount() const {
// Safe down-cast
return static_cast<flamegpu::size_type>(layer->host_functions_callbacks.size());
}
HostFunctionCallback* CLayerDescription::getHostFunctionCallback(unsigned int index) const {
if (index < layer->host_functions_callbacks.size()) {
auto it = layer->host_functions_callbacks.begin();
for (unsigned int i = 0; i < index; ++i)
++it;
return *it;
}
THROW exception::OutOfBoundsException("Index %d is out of bounds (only %d items exist) "
"in LayerDescription.getHostFunctionCallback()\n",
index, layer->host_functions_callbacks.size());
}
#endif // SWIG
} // namespace flamegpu
#endif // INCLUDE_FLAMEGPU_MODEL_LAYERDESCRIPTION_H_