Skip to content
Snippets Groups Projects
Forked from Eclipse Projects / aidge / aidge_core
1807 commits behind the upstream repository.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
StaticAttributes.hpp 10.12 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_STATICATTRIBUTES_H_
#define AIDGE_CORE_UTILS_STATICATTRIBUTES_H_

#include <tuple>
#include <cassert>
#include <cstddef>
#include <typeinfo>
#include <array>

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

namespace Aidge {
/**
 * @brief This class is designed to handle static attributes (i.e. known at compile-time)
 * with named accessors, with minimal overhead (the name strings are not stored in each object
 * instance and it remains possible to access attribute without overhead at compile-time).
*/
template <class ATTRS_ENUM, class ...T>
class StaticAttributes : public Attributes {
public:
    using Attrs = std::tuple<T...>;

    // Helper class to pass to the constructor
    template <ATTRS_ENUM attrsEnum>
    class attr {
    public:
        constexpr attr(const typename std::tuple_element<static_cast<std::size_t>(attrsEnum),std::tuple<T...>>::type& v) : value(v) {}
        const typename std::tuple_element<static_cast<std::size_t>(attrsEnum),std::tuple<T...>>::type value;
    };

/*
    // Direct tuple initialization
    StaticAttributes(T... attrs) : mAttrs({attrs...}) {

    }
*/

    // Constructor for Attributes initialization.
    // Compile-time garantee that every attribute is initialized.
    template <ATTRS_ENUM ...attrsEnum> // non-type attribute pack
    constexpr StaticAttributes(const attr<attrsEnum>&&... attrs) {
        // Check number of attrs consistency
        static_assert(sizeof...(attrs) == std::tuple_size<std::tuple<T...>>::value, "wrong number of attributes in constructor");
        // static_assert(size(EnumStrings<ATTRS_ENUM>::data) == std::tuple_size<std::tuple<T...>>::value, "wrong number of attributes in enum string");

        // Check no duplicates
        constexpr std::array<ATTRS_ENUM, std::tuple_size<std::tuple<T...>>::value> pe = { attrsEnum... };
        static_assert(!hasDuplicates(pe), "duplicate attribute"); // requires C++14

        // Init attrs with constructor arguments
        const std::array<ATTRS_ENUM, std::tuple_size<std::tuple<T...>>::value> p = { ((void)(getAttr<attrsEnum>() = attrs.value), attrsEnum) ... };
        (void)p; // avoid unused warning
    }

    // Compile-time access with enum
    template <ATTRS_ENUM attrsEnum>
    constexpr typename std::tuple_element<static_cast<std::size_t>(attrsEnum),std::tuple<T...>>::type& getAttr() {
        return std::get<static_cast<std::size_t>(attrsEnum)>(mAttrs);
    }

    template <ATTRS_ENUM attrsEnum>
    constexpr const typename std::tuple_element<static_cast<std::size_t>(attrsEnum),std::tuple<T...>>::type& getAttr() const {
        return std::get<static_cast<std::size_t>(attrsEnum)>(mAttrs);
    }

    // Runtime access with enum
    template <typename R>
    constexpr R& getAttr(ATTRS_ENUM attrsEnum) {
        return getAttr<R>(static_cast<std::size_t>(attrsEnum));
    }

    template <typename R>
    constexpr const R& getAttr(ATTRS_ENUM attrsEnum) const {
        return getAttr<R>(static_cast<std::size_t>(attrsEnum));
    }

    // Runtime access with name
    template <typename R>
    R& getAttr(const char* name) {
        for (std::size_t i = 0; i < size(EnumStrings<ATTRS_ENUM>::data); ++i) {
            if (strcmp(EnumStrings<ATTRS_ENUM>::data[i], name) == 0) {
                return getAttr<R>(i);
            }
        }

        AIDGE_THROW_OR_ABORT(std::runtime_error, "attribute \"%s\" not found", name);
    }

    template <typename R>
    const R& getAttr(const char* name) const {
        for (std::size_t i = 0; i < size(EnumStrings<ATTRS_ENUM>::data); ++i) {
            if (strcmp(EnumStrings<ATTRS_ENUM>::data[i], name) == 0) {
                return getAttr<R>(i);
            }
        }

        AIDGE_THROW_OR_ABORT(std::runtime_error, "attribute \"%s\" not found", name);
    }

    template <typename R, std::size_t SIZE = std::tuple_size<std::tuple<T...>>::value>
    typename std::enable_if<(SIZE > 0), R&>::type getAttr(std::size_t i) {
        if (i == SIZE-1) {
            if (std::is_same<R, typename std::tuple_element<SIZE-1,std::tuple<T...>>::type>::value) {
                return reinterpret_cast<R&>(std::get<SIZE-1>(mAttrs));
            }
            else {
                AIDGE_THROW_OR_ABORT(std::runtime_error, "wrong type for attribute with index %lu", i);
            }
        }
        else {
            return getAttr<R, SIZE-1>(i);
        }
    }

    template <typename R, std::size_t SIZE = std::tuple_size<std::tuple<T...>>::value>
    [[noreturn]] typename std::enable_if<(SIZE == 0), R&>::type getAttr(std::size_t /*i*/) {
        AIDGE_THROW_OR_ABORT(std::runtime_error, "attribute not found");
    }

    template <typename R, std::size_t SIZE = std::tuple_size<std::tuple<T...>>::value>
    typename std::enable_if<(SIZE > 0), const R&>::type getAttr(std::size_t i) const {
        if (i == SIZE-1) {
            if (std::is_same<R, typename std::tuple_element<SIZE-1,std::tuple<T...>>::type>::value) {
                return reinterpret_cast<const R&>(std::get<SIZE-1>(mAttrs));
            }
            else {
                AIDGE_THROW_OR_ABORT(std::runtime_error, "wrong type for attribute with index %lu", i);
            }
        }
        else {
            return getAttr<R, SIZE-1>(i);
        }
    }

    template <typename R, std::size_t SIZE = std::tuple_size<std::tuple<T...>>::value>
    [[noreturn]] typename std::enable_if<(SIZE == 0), const R&>::type getAttr(std::size_t /*i*/) const {
        AIDGE_THROW_OR_ABORT(std::runtime_error, "attribute not found");
    }

    template <std::size_t SIZE = std::tuple_size<std::tuple<T...>>::value>
    constexpr typename std::enable_if<(SIZE > 0), const std::type_info&>::type getAttrType(std::size_t i) const {
        if (i == SIZE-1) {
            return typeid(typename std::tuple_element<SIZE-1,std::tuple<T...>>::type);
        }
        else {
            return getAttrType<SIZE-1>(i);
        }
    }

    template <std::size_t SIZE = std::tuple_size<std::tuple<T...>>::value>
    [[noreturn]] typename std::enable_if<(SIZE == 0), const std::type_info&>::type getAttrType(std::size_t /*i*/) const {
        AIDGE_THROW_OR_ABORT(std::runtime_error, "attribute not found");
    }

    constexpr const std::tuple<T...>& getStaticAttributes() const {
        return mAttrs;
    }

    //////////////////////////////////////
    ///     Generic Attributes API
    //////////////////////////////////////
    // Runtime existance check with name
    bool hasAttr(const std::string& name) const override final {
        for (std::size_t i = 0; i < size(EnumStrings<ATTRS_ENUM>::data); ++i) {
            if (name == EnumStrings<ATTRS_ENUM>::data[i]) {
                return true;
            }
        }

        return false;
    }

    // Runtime type access with name
    std::string getAttrType(const std::string& name) const override final {
        for (std::size_t i = 0; i < size(EnumStrings<ATTRS_ENUM>::data); ++i) {
            if (name == EnumStrings<ATTRS_ENUM>::data[i]) {
                return getAttrType(i).name();
            }
        }

        AIDGE_THROW_OR_ABORT(std::runtime_error, "attribute \"%s\" not found", name.c_str());
    }

    std::set<std::string> getAttrsName() const override final {
        std::set<std::string> attrsName;
        for (std::size_t i = 0; i < size(EnumStrings<ATTRS_ENUM>::data); ++i) {
            attrsName.insert(EnumStrings<ATTRS_ENUM>::data[i]);
        }
        return attrsName;
    }

    #ifdef PYBIND
    /**
     * @brief Return a set of attributes defined.
     * This method is used to automatically retrieve attributes in the documentation.
     * This method is a duplicate of ``getAttrsName`` but static.
     *
     * @return std::set<std::string>
     */
    static std::set<std::string> staticGetAttrsName() {
        std::set<std::string> attrsName;
        for (std::size_t i = 0; i < size(EnumStrings<ATTRS_ENUM>::data); ++i) {
            attrsName.insert(EnumStrings<ATTRS_ENUM>::data[i]);
        }
        return attrsName;
    }


    py::object getAttrPy(const std::string& name) const override {
        for (std::size_t i = 0; i < size(EnumStrings<ATTRS_ENUM>::data); ++i) {
            if (name == EnumStrings<ATTRS_ENUM>::data[i]) {
                // https://github.com/pybind/pybind11/blob/f3e0602802c7840992c97f4960515777cad6a5c7/include/pybind11/pytypes.h#L1119-L1138
                // Normal accessor would not work has we convert the tuple to a py::object which can be anything
                return py::detail::accessor_policies::tuple_item::get(py::cast(mAttrs), static_cast<py::size_t>(i));
            }
        }

        AIDGE_THROW_OR_ABORT(py::value_error, "attribute \"%s\" not found", name.c_str());
    }


    void setAttrPy(const std::string& name, py::object&& value) override final{
        for (std::size_t i = 0; i < size(EnumStrings<ATTRS_ENUM>::data); ++i) {
            if (name == EnumStrings<ATTRS_ENUM>::data[i]) {
                // Cannot update attribute using reference has it would require templating
                // Use a dirty
                auto tmpAttr = py::cast(mAttrs);
                py::detail::accessor_policies::tuple_item::set(tmpAttr, static_cast<py::size_t>(i), value);
                mAttrs = py::cast<std::tuple<T...>>(tmpAttr);
                return;
            }
        }
        AIDGE_THROW_OR_ABORT(py::value_error, "attribute \"%s\" not found", name.c_str());
    }
    #endif

private:
    template <typename V, std::size_t N>
    static constexpr bool hasDuplicates(const std::array<V, N>& array) {
        for (std::size_t i = 1; i < N; i++) {
            for (std::size_t j = 0; j < i; j++) {
                if (array[i] == array[j]) {
                    return true;
                }
            }
        }

        return false;
    }

    std::tuple<T...> mAttrs;
};
}

#endif /* AIDGE_CORE_UTILS_STATICATTRIBUTES_H_ */