Forked from
Eclipse Projects / aidge / aidge_core
1807 commits behind the upstream repository.
-
Cyril Moineau authoredCyril Moineau authored
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_ */