Skip to content
Snippets Groups Projects
Forked from Eclipse Projects / aidge / aidge_core
2349 commits behind the upstream repository.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
DynamicAttributes.hpp 4.58 KiB
/********************************************************************************
 * Copyright (c) 2023 CEA-List
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 ********************************************************************************/

#ifndef AIDGE_CORE_UTILS_DYNAMICATTRIBUTES_H_
#define AIDGE_CORE_UTILS_DYNAMICATTRIBUTES_H_

#include <map>
#include <vector>
#include <type_traits>
#include <typeinfo>
#include <cassert>
#include <string>

#include "aidge/utils/Any.hpp"
#include "aidge/utils/Attributes.hpp"


namespace Aidge {

///\todo store also a fix-sized code that indicates the type
///\todo managing complex types or excluding non-trivial, non-aggregate types
class DynamicAttributes : public Attributes {
public:
    /**
     * \brief Returning an Attribute identified by its name
     * \tparam T expected Attribute type
     * \param name Attribute name
     * \details assert if T is not the actual Attribute type or if the Attribute does not
     *  exist
     * \note at() throws if the Attribute does not exist, using find to test for Attribute existance
     */
    template<class T> T& getAttr(const std::string& name)
    {
        return libany::any_cast<T&>(mAttrs.at(name));
    }

    template<class T> const T& getAttr(const std::string& name) const
    {
        return libany::any_cast<const T&>(mAttrs.at(name));
    }

    ///\brief Add a new Attribute, identified by its name. If it already exists, asserts.
    ///\tparam T expected Attribute type
    ///\param name Attribute name
    ///\param value Attribute value
    template<class T> void addAttr(const std::string& name, T&& value)
    {
        const auto& res = mAttrs.emplace(std::make_pair(name, libany::any(std::forward<T>(value))));
        assert(res.second && "attribute already exists");
    }

    ///\brief Set an Attribute value, identified by its name. If it already exists, its value (and type, if different) is changed.
    ///\tparam T expected Attribute type
    ///\param name Attribute name
    ///\param value Attribute value
    template<class T> void setAttr(const std::string& name, T&& value)
    {
        auto res = mAttrs.emplace(std::make_pair(name, libany::any(std::forward<T>(value))));
        if (!res.second)
            res.first->second = std::move(libany::any(std::forward<T>(value)));
    }
    //////////////////////////////////////
    ///     Generic Attributes API
    //////////////////////////////////////
    bool hasAttr(const std::string& name) const override final {
        return (mAttrs.find(name) != mAttrs.end());
    }

    std::string getAttrType(const std::string& name) const override final {
        return mAttrs.at(name).type().name();
    }

    std::vector<std::string> getAttrsName() const override final {
        std::vector<std::string> attrsName;
        for(auto const& it: mAttrs)
            attrsName.push_back(it.first);
        return attrsName;
    }

    #ifdef PYBIND
    /**
     * @detail See https://github.com/pybind/pybind11/issues/1590 as to why a
     * generic type caster for std::any is not feasable.
    */
    py::object getAttrPy(const std::string& name) const {
        py::object res = py::none();
        const auto& attrType = mAttrs.at(name).type();
        if(attrType == typeid(int))
            res = py::cast(getAttr<int>(name));
        else if(attrType == typeid(float))
            res = py::cast(getAttr<float>(name));
        else if(attrType == typeid(bool))
            res = py::cast(getAttr<bool>(name));
        else if(attrType == typeid(std::string))
            res = py::cast(getAttr<std::string>(name));
        else if(attrType == typeid(std::vector<bool>))
            res = py::cast(getAttr<std::vector<bool>>(name));
        else if(attrType == typeid(std::vector<int>))
            res = py::cast(getAttr<std::vector<int>>(name));
        else if(attrType == typeid(std::vector<float>))
            res = py::cast(getAttr<std::vector<float>>(name));
        else if(attrType == typeid(std::vector<std::string>))
            res = py::cast(getAttr<std::vector<std::string>>(name));
        else {
            throw py::key_error("Failed to convert attribute type " + name + ", this issue may come from typeid function which gave an unknown key : [" + attrType.name() + "]. Please open an issue asking to add the support for this key.");
        }
        return res;
    };
    #endif

private:
    std::map<std::string, libany::any> mAttrs;
};

}

#endif /* AIDGE_CORE_UTILS_DYNAMICATTRIBUTES_H_ */