static_assert( __cplusplus > 2020'99 ); #pragma once #include #include #include #include #include #include #include /*! * @file * @todo consider dropping string arg versions in favor of just using UA_Key versions (after making conversion constructor not explicit) * @todo consider how operator >> and << should function. JSON? If so, using what? * @todo consider [] versions of at * @todo decide exceptions to throw (especially for not found) * @todo comment at() * @todo auto creating option for get (and for general use) * @todo need to constrain type stored in UniversalAggregate and coerce to those when possible */ namespace Alepha::Hydrogen ::detail:: UniversalAggregate_m { inline namespace exports { class UniversalAggregate; using MemberType= std::variant< UniversalAggregate, std::string, int, std::vector< UniversalAggregate > >; class UniversalAggregate { private: // The value could be a UA, a vector of UAs or anything else. using CoreMap= std::map< std::string, MemberType >; CoreMap data; UniversalAggregate & get( UA_Key key, bool create= false ) { UniversalAggregate *ret= this; for( const auto &lv : key.levels ) { if( std::holds_alternative< std::string >(lv) ) { if( not ret->data.count( std::get< std::string >( lv ) ) ) { if( not create ) // throw an error { throw std::runtime_error( "desired level not found: " + std::get< std::string >( lv ) ); } else // create the missing level { ret->data[ std::get< std::string >( lv ) ]= UniversalAggregate{}; } } ret= &(std::get(ret->data.at( std::get< std::string >( lv ) ) ) ); continue; } } return *ret; } const UniversalAggregate & get( UA_Key key ) const { const UniversalAggregate *ret= this; for( const auto &lv : key.levels ) { if( std::holds_alternative< std::string >(lv) ) { if( not ret->data.count( std::get< std::string >( lv ) ) ) { throw std::runtime_error( "desired level not found: " + std::get< std::string >( lv ) ); } ret= &(std::get(ret->data.at( std::get< std::string >( lv ) ) ) ); continue; } } return *ret; } public: UniversalAggregate()= default; /*! @brief * @note direct insert on a vector is not supported. Use at() to obtain a reference to the vector */ void insert( const UA_Key &key, const MemberType &value ) { if( key.isVector() ) { throw std::runtime_error( "Operation not supported on vector" ); } auto& target= get( key.parent(), true ); target.data[ std::get< std::string >( key.levels.back() ) ]= value; } /*! @brief * */ void insert( const std::string &key, const MemberType &value ) { insert( UA_Key( key ), value ); } /*! @brief * @note direct erase on a vector is not supported. Use at() to obtain a reference to the vector */ void erase( const UA_Key &key ) { if( key.isVector() ) { throw std::runtime_error( "Operation not supported on vector" ); } auto& target= get( key.parent() ); target.data.erase( std::get< std::string >( key.levels.back() ) ); return; } /*! @brief * */ void erase( const std::string &key ) { erase( UA_Key( key ) ); } /*! @brief * */ const MemberType & at( const std::string &key ) const { return at( UA_Key( key ) ); } /*! @brief * */ const MemberType & at( const UA_Key &key ) const { if( key.isVector() ) { throw std::runtime_error( "Operation not supported on vector" ); } auto& target= get( key.parent() ); return target.data.at( std::get< std::string >( key.levels.back() ) ); } /*! @brief * */ MemberType & at( const std::string &key ) { return at( UA_Key( key ) ); } /*! @brief * */ MemberType & at( const UA_Key &key ) { if( key.isVector() ) { throw std::runtime_error( "Operation not supported on vector" ); } auto& target= get( key.parent() ); return target.data.at( std::get< std::string >( key.levels.back() ) ); } /*! @brief * */ size_t count( const std::string &key ) { return 0; } /*! @brief * @note direct count on a vector is not supported. Use at() to obtain a reference to the vector */ size_t count( const UA_Key &key ) { return 0; } }; } } namespace Alepha::Hydrogen::inline exports::inline UniversalAggregate_m { using namespace detail::UniversalAggregate_m::exports; }