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_