forked from Alepha/Alepha
208 lines
4.8 KiB
C++
208 lines
4.8 KiB
C++
static_assert( __cplusplus > 2020'99 );
|
|
|
|
#pragma once
|
|
|
|
#include <Alepha/Alepha.h>
|
|
#include <Alepha/UA_Key.h>
|
|
|
|
#include <map>
|
|
#include <string>
|
|
#include <any>
|
|
#include <iostream>
|
|
#include <vector>
|
|
|
|
/*!
|
|
* @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<UniversalAggregate>(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<UniversalAggregate>(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;
|
|
}
|
|
|