Skip to content
Snippets Groups Projects
StaticAttributes.hpp 8.9 KiB
Newer Older
/********************************************************************************
 * 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>
Olivier BICHLER's avatar
Olivier BICHLER committed
#include <typeinfo>
Olivier BICHLER's avatar
Olivier BICHLER committed
#include <array>

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

namespace Aidge {
Olivier BICHLER's avatar
Olivier BICHLER committed
/**
 * @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
Olivier BICHLER's avatar
Olivier BICHLER committed
 * 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);
    }

Olivier BICHLER's avatar
Olivier BICHLER committed
    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) {
Olivier BICHLER's avatar
Olivier BICHLER committed
        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));
                AIDGE_THROW_OR_ABORT(std::runtime_error, "wrong type for attribute with index %lu", i);
            }
        }
        else {
            return getAttr<R, SIZE-1>(i);
        }
    }

Olivier BICHLER's avatar
Olivier BICHLER committed
    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");
    }

Olivier BICHLER's avatar
Olivier BICHLER committed
    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);
        }
    }

Olivier BICHLER's avatar
Olivier BICHLER committed
    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]) {
Olivier BICHLER's avatar
Olivier BICHLER committed
                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
Olivier BICHLER's avatar
Olivier BICHLER committed
    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());
    };
    #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_ */