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_DYNAMICATTRIBUTES_H_
#define AIDGE_CORE_UTILS_DYNAMICATTRIBUTES_H_
#include <map>
#include <vector>
#include <type_traits>
#include <typeinfo>
#include <cassert>
#include <string>
#include "aidge/utils/future_std/any.hpp"
#include "aidge/utils/Attributes.hpp"
#ifdef PYBIND
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
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)
// If attribute does not exist in C++, it might have been created or modified in Python
auto it = mAttrs.find(name);
if (it == mAttrs.end()) {
auto itPy = mAttrsPy.find(name);
if (itPy != mAttrsPy.end()) {
// Insert the attribute back in C++
mAttrs.emplace(std::make_pair(name, future_std::any(itPy->second.cast<T>())));
return future_std::any_cast<T&>(mAttrs.at(name));
}
template<class T> const T& getAttr(const std::string& name) const
{
// If attribute does not exist in C++, it might have been created or modified in Python
auto it = mAttrs.find(name);
if (it == mAttrs.end()) {
auto itPy = mAttrsPy.find(name);
if (itPy != mAttrsPy.end()) {
// Insert the attribute back in C++
mAttrs.emplace(std::make_pair(name, future_std::any(itPy->second.cast<T>())));
return future_std::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, const T& value)
const auto& res = mAttrs.emplace(std::make_pair(name, future_std::any(value)));
assert(res.second && "attribute already exists");
// We cannot handle Python object if the Python interpreter is not running
if (Py_IsInitialized()) {
// Keep a copy of the attribute in py::object that is updated everytime
mAttrsPy.emplace(std::make_pair(name, py::cast(value)));
}
///\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, const T& value)
{
auto res = mAttrs.emplace(std::make_pair(name, future_std::any(value)));
if (!res.second)
res.first->second = future_std::any(value);
// We cannot handle Python object if the Python interpreter is not running
if (Py_IsInitialized()) {
// Keep a copy of the attribute in py::object that is updated everytime
auto resPy = mAttrsPy.emplace(std::make_pair(name, py::cast(value)));
resPy.first->second = std::move(py::cast(value));
}
#endif
}
void delAttr(const std::string& name) {
mAttrs.erase(name);
#ifdef PYBIND
mAttrsPy.erase(name);
#endif
}
#ifdef PYBIND
void addAttrPy(const std::string& name, py::object&& value)
{
auto it = mAttrs.find(name);
assert(it == mAttrs.end() && "attribute already exists");
const auto& res = mAttrsPy.emplace(std::make_pair(name, value));
assert(res.second && "attribute already exists");
void setAttrPy(const std::string& name, py::object&& value) override final
{
auto resPy = mAttrsPy.emplace(std::make_pair(name, value));
if (!resPy.second)
resPy.first->second = std::move(value);
// Force getAttr() to take attribute value from mAttrsPy and update mAttrs
mAttrs.erase(name);
//////////////////////////////////////
/// Generic Attributes API
//////////////////////////////////////
bool hasAttr(const std::string& name) const override final {
// Attributes might have been created in Python, the second condition is necessary.
return (mAttrs.find(name) != mAttrs.end() || mAttrsPy.find(name) != mAttrsPy.end());
return (mAttrs.find(name) != mAttrs.end());
}
std::string getAttrType(const std::string& name) const override final {
// In order to remain consistent between C++ and Python, with or without PyBind, the name of the type is:
// - C-style for C++ created attributes
// - Python-style for Python created attributes
#ifdef PYBIND
// If attribute does not exist in C++, it might have been created in Python
auto it = mAttrs.find(name);
if (it == mAttrs.end()) {
auto itPy = mAttrsPy.find(name);
if (itPy != mAttrsPy.end()) {
return std::string(Py_TYPE(itPy->second.ptr())->tp_name);
}
}
#endif
return mAttrs.at(name).type().name();
}
std::set<std::string> getAttrsName() const override final {
std::set<std::string> attrsName;
for(auto const& it: mAttrs)
attrsName.insert(it.first);
// Attributes might have been created in Python
/**
* @detail See https://github.com/pybind/pybind11/issues/1590 as to why a
* generic type caster for std::any is not feasable.
* The strategy here is to keep a copy of each attribute in py::object that is updated everytime.
py::object getAttrPy(const std::string& name) const override final {
#ifdef PYBIND
// Stores C++ attributes (copy) and Python-only attributes
// Code should be compiled with -fvisibility=hidden
// See https://pybind11.readthedocs.io/en/stable/faq.html:
// “‘SomeClass’ declared with greater visibility than the type of its
// field ‘SomeClass::member’ [-Wattributes]”
// This map will only be populated if Python interpreter is running
// Stores C++ attributes only
// mutable because it may be updated in getAttr() from Python
mutable std::map<std::string, future_std::any> mAttrs;
std::map<std::string, future_std::any> mAttrs;
};
}
#endif /* AIDGE_CORE_UTILS_DYNAMICATTRIBUTES_H_ */