Radium Engine  1.5.20
Loading...
Searching...
No Matches
VariableSet.hpp
1#pragma once
2#include <Core/RaCore.hpp>
3
4#include <Core/Utils/Log.hpp>
5#include <Core/Utils/StdExperimentalTypeTraits.hpp>
6#include <Core/Utils/StdOptional.hpp>
7#include <Core/Utils/TypesUtils.hpp>
8
9#include <any>
10#include <functional>
11#include <map>
12#include <typeindex>
13#include <unordered_map>
14#include <vector>
15
16namespace Ra {
17namespace Core {
18
71class RA_CORE_API VariableSet
72{
73 public:
75 template <typename T>
77
79 template <typename T>
81
83 template <typename T>
85
91 template <typename T>
92 using VariableHandle = typename VariableContainer<T>::iterator;
93
97 template <typename H>
99
100 // ----------------------------------------------------------
103 VariableSet() : m_vtable( VariableSetFunctions::getInstance() ) {}
104 ~VariableSet() = default;
106 VariableSet( const VariableSet& other ) noexcept :
107 m_vtable( VariableSetFunctions::getInstance() ) {
108 *this = other;
109 }
111 VariableSet( VariableSet&& other ) noexcept : m_vtable( VariableSetFunctions::getInstance() ) {
112 *this = std::move( other );
113 }
115
116 // ------------------------------------------------------------------------------------------
117 // Global variable set operation
118 // ------------------------------------------------------------------------------------------
122 auto operator=( const VariableSet& other ) -> VariableSet&;
123
125 auto operator=( VariableSet&& other ) noexcept -> VariableSet&;
126
128 void clear();
129
134 void mergeKeepVariables( const VariableSet& from );
135
140 void mergeReplaceVariables( const VariableSet& from );
141
143 size_t size() const;
144
147 auto getStoredTypes() const -> const std::vector<std::type_index>& { return m_storedType; }
148
150 // ------------------------------------------------------------------------------------------
151 // Per variable operations
152 // ------------------------------------------------------------------------------------------
159 template <typename T>
160 auto insertVariable( const std::string& name, const T& value )
162
167 template <typename T>
168 auto getVariable( const std::string& name ) const -> const T&;
169 template <typename T>
170 auto getVariable( const std::string& name ) -> T&;
171
177 template <typename T>
178 auto getVariableHandle( const std::string& name ) const -> const VariableHandle<T>;
179
184 template <typename H>
185 bool isHandleValid( const H& handle ) const;
186
190 template <typename T>
191 auto setVariable( const std::string& name, const T& value )
193
198 template <typename T>
199 bool deleteVariable( const std::string& name );
200
208 template <typename H>
209 bool deleteVariable( H& handle );
210
214 template <typename T>
215 auto existsVariable( const std::string& name ) const -> Utils::optional<VariableHandle<T>>;
216
218
219 // ------------------------------------------------------------------------------------------
220 // Per type access
221 // ------------------------------------------------------------------------------------------
229 template <typename T>
230 auto existsVariableType() const -> Utils::optional<VariableContainer<T>*>;
231
237 template <typename T>
238 bool deleteAllVariables();
239
245 template <typename T>
246 auto getAllVariables() const -> VariableContainer<T>&;
247
254 template <typename H>
255 auto getAllVariablesFromHandle( const H& handle )
257
261 template <typename T>
262 size_t numberOf() const;
264
267
272 template <typename... TYPES>
274 using types = Utils::TypeList<TYPES...>;
275 };
276
282 {
283 public:
284 virtual ~DynamicVisitorBase() = default;
285
292 virtual void operator()( std::any&& in, std::any&& userParam ) const = 0;
293
297 [[nodiscard]] virtual bool accept( const std::type_index& id ) const = 0;
298 };
299
309 class RA_CORE_API DynamicVisitor : public DynamicVisitorBase
310 {
311 public:
313 ~DynamicVisitor() override = default;
314
321 void operator()( std::any&& in, std::any&& userParam ) const override;
322
326 [[nodiscard]] bool accept( const std::type_index& id ) const override;
327
335 template <typename T, typename F>
336 bool addOperator( F&& f );
337
341 template <typename T>
342 bool hasOperator();
343
350 template <typename T, typename F>
351 void addOrReplaceOperator( F&& f );
352
357 template <typename T>
358 bool removeOperator();
359
360 private:
362 using CallbackFunction = std::function<void( std::any&, std::any&& )>;
363
366
368 template <typename T, typename F, bool WithUserParam>
369 struct MakeVisitOperatorHelper {
370 auto makeOperator( F& f ) -> OperatorsStorageType::value_type;
371 };
372
375 template <typename T, typename F>
376 auto makeVisitorOperator( F& f ) -> OperatorsStorageType::value_type;
377
379 OperatorsStorageType m_visitorOperator;
380 };
381
392 template <typename F>
393 void visit( F&& visitor ) const;
394
396 template <typename F, typename T>
397 void visit( F&& visitor, T& userParams ) const;
398
400 template <typename F, typename T>
401 void visit( F&& visitor, T&& userParams ) const;
402
403 private:
427 template <typename P = bool>
428 void visitDynamic( DynamicVisitorBase& visitor, P&& params = P {} ) const;
429
447 template <typename F>
448 void visitStatic( F&& visitor ) const;
449
451 template <typename F, typename T>
452 void visitStatic( F&& visitor, T& userParams ) const;
453
455 template <typename F, typename T>
456 void visitStatic( F&& visitor, T&& userParams ) const;
457
461 template <typename T>
462 static auto getVariableVisitTypeIndex() -> std::type_index;
463
469 template <typename T>
470 auto addVariableType() -> Utils::optional<VariableContainer<T>*>;
471
475 template <typename F, typename T>
476 using VisitFunction =
477 decltype( std::declval<F>().operator()( std::declval<const std::string&>(),
478 std::declval<T&>() ) );
479
481 template <typename F, typename T, typename U>
482 using VisitFunctionWithUserParam =
483 decltype( std::declval<F>().operator()( std::declval<const std::string&>(),
485 std::declval<U&&>() ) );
486
488 template <typename F, typename T>
489 static constexpr bool has_visit_callable_v =
490 Ra::Core::Utils::is_detected<VisitFunction, F, T>::value;
491
493 template <typename F, typename T, typename U>
494 static constexpr bool has_visit_callable_with_user_param_v =
495 Ra::Core::Utils::is_detected<VisitFunctionWithUserParam, F, T, U>::value;
496
498 template <typename F, template <typename...> typename TYPESLIST, typename... TYPES>
499 void visitImpl( F&& visitor, TYPESLIST<TYPES...> ) const;
500
501 template <typename F, typename T>
502 void visitImplHelper( F& visitor ) const;
503
505 template <typename F, typename U, template <typename...> typename TYPESLIST, typename... TYPES>
506 void visitImplUserParam( F&& visitor, U&& userParam, TYPESLIST<TYPES...> ) const;
507
508 template <typename F, typename U, typename T>
509 void visitImplHelperUserParam( F& visitor, U&& userParams ) const;
510
512
516
518 class VariableSetFunctions
519 {
520 public:
521 using ClearFunctionType = std::function<void( VariableSet& )>;
522 using MergeFunctionType = std::function<void( const VariableSet&, VariableSet& )>;
523 using SizeFunctionType = std::function<size_t( const VariableSet& )>;
524 // Should be these simplified ????
525 using VisitFunctorType =
526 std::function<std::pair<bool, std::function<void( DynamicVisitorBase&, std::any&& )>>(
527 const VariableSet&,
528 const DynamicVisitorBase& )>;
529
530 VariableSetFunctions( const VariableSetFunctions& ) = delete;
531 void operator=( const VariableSetFunctions& ) = delete;
532
533 protected:
534 VariableSetFunctions() = default;
535 ~VariableSetFunctions() = default;
536
537 public:
538 // static variable ref singleton implementation.
539 static auto getInstance() -> VariableSetFunctions*;
540
541 std::vector<MergeFunctionType> m_mergeKeepFunctions;
542 std::vector<MergeFunctionType> m_mergeReplaceFunctions;
543 std::vector<SizeFunctionType> m_sizeFunctions;
544 std::vector<VisitFunctorType> m_visitFunctions;
545 std::vector<std::type_index> m_storedType;
546 };
547
548 VariableSetFunctions* m_vtable;
549
551
554 // Storage of the variable in a type-erased associative container
556 std::unordered_map<std::type_index, size_t> m_typeIndexToVtableIndex;
558 std::vector<std::type_index> m_storedType;
559
563 template <typename T>
564 auto createVariableStorage() -> VariableContainer<T>*;
565
568 template <typename T>
569 auto getVariableStorage() const -> VariableContainer<T>&;
570
573 template <typename T>
574 void removeVariableStorage();
575
577};
578
579// ------------------------------------------------------------------------------------------
580// Storage management
581// ------------------------------------------------------------------------------------------
582
583template <typename T>
584auto VariableSet::createVariableStorage() -> VariableContainer<T>* {
585 m_variables[std::type_index { typeid( T ) }].emplace<VariableContainer<T>>();
586 return std::any_cast<VariableContainer<T>>( &( m_variables[std::type_index { typeid( T ) }] ) );
587}
588
589template <typename T>
590auto VariableSet::getVariableStorage() const -> VariableContainer<T>& {
591 return std::any_cast<VariableContainer<T>&>( m_variables[std::type_index { typeid( T ) }] );
592}
593
594template <typename T>
595void VariableSet::removeVariableStorage() {
596 auto type = std::type_index { typeid( T ) };
597 m_variables.erase( type );
598 m_typeIndexToVtableIndex.erase( type );
599
600 auto newEnd = std::remove( m_storedType.begin(), m_storedType.end(), type );
601 m_storedType.erase( newEnd, m_storedType.end() );
602}
603
604// ------------------------------------------------------------------------------------------
605// Templated methods definition
606// ------------------------------------------------------------------------------------------
607template <typename T>
608auto VariableSet::insertVariable( const std::string& name, const T& value )
610 auto typeAccess = existsVariableType<T>();
611 // If it is the first parameter of the given type, first register the type
612 if ( !typeAccess ) { typeAccess = addVariableType<T>(); }
613 // insert the parameter.
614 return ( *typeAccess )->insert( { name, value } );
615}
616
617template <typename T>
618auto VariableSet::getVariable( const std::string& name ) -> T& {
619 return const_cast<T&>( const_cast<const VariableSet*>( this )->getVariable<T>( name ) );
620}
621
622template <typename T>
623auto VariableSet::getVariable( const std::string& name ) const -> const T& {
624 return getVariableHandle<T>( name )->second;
625}
626
627template <typename T>
628auto VariableSet::getVariableHandle( const std::string& name ) const -> const VariableHandle<T> {
629 assert( existsVariableType<T>() );
630 return getVariableStorage<T>().find( name );
631}
632
633template <typename H>
634bool VariableSet::isHandleValid( const H& handle ) const {
635 if ( !existsVariableType<VariableTypeFromHandle<H>>() ) { return false; }
636 return handle != getVariableStorage<VariableTypeFromHandle<H>>().end();
637}
638
639template <typename T>
640auto VariableSet::setVariable( const std::string& name, const T& value )
642 auto typeAccess = existsVariableType<T>();
643 // If it is the first parameter of the given type, first register the type
644 if ( !typeAccess ) { typeAccess = addVariableType<T>(); }
645 // insert the parameter.
646
647 return ( *typeAccess )->insert_or_assign( name, value );
648}
649
650template <typename T>
651bool VariableSet::deleteVariable( const std::string& name ) {
652 if ( auto typeAccess = existsVariableType<T>(); typeAccess ) {
653 auto removed = ( *typeAccess )->erase( name );
654 // remove the type related function when the container has no more data of this type
655 if ( numberOf<T>() == 0 ) { deleteAllVariables<T>(); }
656 return removed > 0;
657 }
658 return false;
659}
660
661template <typename H>
662bool VariableSet::deleteVariable( H& handle ) {
663 assert( isHandleValid( handle ) );
664 auto varname = handle->first;
665 handle = getVariableStorage<VariableTypeFromHandle<H>>().end();
666 return deleteVariable<VariableTypeFromHandle<H>>( varname );
667}
668
669template <typename T>
670auto VariableSet::existsVariable( const std::string& name ) const
671 -> Utils::optional<VariableHandle<T>> {
672
673 if ( auto typeAccess = existsVariableType<T>(); typeAccess ) {
674 auto itr = ( *typeAccess )->find( name );
675 if ( itr != ( *typeAccess )->cend() ) { return itr; }
676 }
677 return {};
678}
679
680template <typename T>
681auto VariableSet::getVariableVisitTypeIndex() -> std::type_index {
682 static std::type_index idT(
684 return idT;
685}
686
687template <typename T>
688auto VariableSet::addVariableType() -> Utils::optional<VariableContainer<T>*> {
689 auto storage = createVariableStorage<T>();
690
691 auto tidx = std::type_index( typeid( T ) );
692 auto it = std::find( m_vtable->m_storedType.begin(), m_vtable->m_storedType.end(), tidx );
693
694 // remember the stored type and its rank for the instance
695 m_typeIndexToVtableIndex[tidx] = it - m_vtable->m_storedType.begin();
696 m_storedType.push_back( tidx );
697
698 // If the type is not already there, add type operations and identifier in the global vtable
699 if ( it == m_vtable->m_storedType.end() ) {
700 // remember the stored type in the global vtable
701 m_vtable->m_storedType.emplace_back( tidx );
702 // used to merge (keep) the stored data from container "from" to container "to"
703 m_vtable->m_mergeKeepFunctions.emplace_back(
704 []( const VariableSet& from, VariableSet& to ) {
705 auto toStorageAccess = to.existsVariableType<T>();
706 if ( !toStorageAccess ) { toStorageAccess = to.addVariableType<T>(); }
707 auto& toStorage = *( toStorageAccess.value() );
708 auto& fromStorage = from.getVariableStorage<T>();
709 for ( const auto& t : fromStorage ) {
710 toStorage.insert( t );
711 }
712 } );
713 // used to merge (replace) the stored data from container "from" to container "to"
714 m_vtable->m_mergeReplaceFunctions.emplace_back(
715 []( const VariableSet& from, VariableSet& to ) {
716 auto toStorageAccess = to.existsVariableType<T>();
717 if ( !toStorageAccess ) { toStorageAccess = to.addVariableType<T>(); }
718 auto& toStorage = *( toStorageAccess.value() );
719 auto& fromStorage = from.getVariableStorage<T>();
720 for ( const auto& t : fromStorage ) {
721 toStorage.insert_or_assign( t.first, t.second );
722 }
723 } );
724 // use to compute gauge on the stored data
725 m_vtable->m_sizeFunctions.emplace_back(
726 []( const VariableSet& c ) { return c.getVariableStorage<T>().size(); } );
727 // used to visit the variableSet with a dynamic visitor
728 m_vtable->m_visitFunctions.emplace_back(
729 []( const VariableSet& c, const DynamicVisitorBase& v )
730 -> std::pair<bool, std::function<void( DynamicVisitorBase&, std::any&& )>> {
731 auto id = getVariableVisitTypeIndex<T>();
732 if ( v.accept( id ) ) {
733 auto& visitedStorage = c.getVariableStorage<T>();
734 auto coll = std::ref( visitedStorage );
735 return { true, [coll]( DynamicVisitorBase& visitor, std::any&& userParam ) {
736 for ( auto&& t : coll.get() ) {
737 visitor( { std::ref( t ) },
738 std::forward<std::any>( userParam ) );
739 }
740 } };
741 }
742 else { return { false, nullptr }; }
743 } );
744 }
745 return storage;
746}
747
748template <typename T>
749auto VariableSet::existsVariableType() const -> Utils::optional<VariableContainer<T>*> {
750 auto iter = m_variables.find( std::type_index { typeid( T ) } );
751 if ( iter == m_variables.cend() ) { return {}; }
752 else { return std::any_cast<VariableSet::VariableContainer<T>>( &( iter->second ) ); }
753}
754
755template <typename T>
756bool VariableSet::deleteAllVariables() {
757 if ( existsVariableType<T>() ) {
758 removeVariableStorage<T>();
759 return true;
760 }
761 return false;
762}
763
764template <typename T>
765auto VariableSet::getAllVariables() const -> VariableContainer<T>& {
766 // just assert on the existence of the type to prevent undefined behavior when dereferencing
767 // the optional
768 assert( existsVariableType<T>() );
769 // trows std::bad_any_cast if type does not exist
770 return getVariableStorage<T>();
771}
772
773template <typename H>
774auto VariableSet::getAllVariablesFromHandle( const H& )
776 assert( existsVariableType<VariableTypeFromHandle<H>>() );
777 return getVariableStorage<VariableTypeFromHandle<H>>();
778}
779
780template <typename T>
781size_t VariableSet::numberOf() const {
782 if ( auto variables = existsVariableType<T>(); variables ) { return ( *variables )->size(); }
783 return 0;
784}
785
786/* --------------- Visitors */
787
788template <typename P>
789void VariableSet::visitDynamic( DynamicVisitorBase& visitor, P&& params ) const {
790 for ( const auto& [type, index] : m_typeIndexToVtableIndex ) {
791 auto [accepted, loop] = m_vtable->m_visitFunctions[index]( *this, visitor );
792 if ( accepted ) { loop( visitor, std::forward<P>( params ) ); }
793 }
794}
795
796template <typename F>
797void VariableSet::visitStatic( F&& visitor ) const {
798 visitImpl( visitor, typename std::decay_t<F>::types {} );
799}
800
801template <typename F, template <typename...> typename TYPESLIST, typename... TYPES>
802void VariableSet::visitImpl( F&& visitor, TYPESLIST<TYPES...> ) const {
803 ( ..., visitImplHelper<std::decay_t<F>, TYPES>( visitor ) );
804}
805
806template <typename F, typename T>
807void VariableSet::visitImplHelper( F& visitor ) const {
808 static_assert( has_visit_callable_v<F, T>,
809 "Static visitors must provide a function with profile "
810 "void( const std::string& name, [const ]T[&] value) for each "
811 "declared visitable type T" );
812 if ( auto variables = existsVariableType<T>() ) {
813 for ( auto& element : *( variables.value() ) ) {
814 visitor( element.first, element.second );
815 }
816 }
817}
818
819template <typename F, typename U>
820void VariableSet::visitStatic( F&& visitor, U& userParams ) const {
821 visitImplUserParam(
822 visitor, std::forward<U>( userParams ), typename std::decay_t<F>::types {} );
823}
824
825template <typename F, typename U>
826void VariableSet::visitStatic( F&& visitor, U&& userParams ) const {
827 visitImplUserParam(
828 visitor, std::forward<U>( userParams ), typename std::decay_t<F>::types {} );
829}
830
831template <typename F, typename U, template <typename...> typename TYPESLIST, typename... TYPES>
832void VariableSet::visitImplUserParam( F&& visitor, U&& userParam, TYPESLIST<TYPES...> ) const {
833 ( ...,
834 visitImplHelperUserParam<std::decay_t<F>, U, TYPES>( visitor,
835 std::forward<U>( userParam ) ) );
836}
837
838template <typename F, typename U, typename T>
839void VariableSet::visitImplHelperUserParam( F& visitor, U&& userParams ) const {
840 static_assert( has_visit_callable_with_user_param_v<F, T, U>,
841 "Static visitors must provide a function with profile "
842 "void( const std::string& name, [const ]T[&] value, [const] U&&) for each "
843 "declared visitable type T" );
844 if ( auto variables = existsVariableType<T>(); variables ) {
845 for ( auto& element : *( variables.value() ) ) {
846 visitor( element.first, element.second, std::forward<U>( userParams ) );
847 }
848 }
849}
850
851template <typename T, typename F>
852bool VariableSet::DynamicVisitor::addOperator( F&& f ) {
853 auto [it, inserted] = m_visitorOperator.insert( makeVisitorOperator<T, F>( f ) );
854 return inserted;
855}
856
857template <typename T>
858bool VariableSet::DynamicVisitor::hasOperator() {
859 return m_visitorOperator.find( getVariableVisitTypeIndex<T>() ) != m_visitorOperator.end();
860}
861
862template <typename T, typename F>
863void VariableSet::DynamicVisitor::addOrReplaceOperator( F&& f ) {
864 auto op = makeVisitorOperator<T, F>( f );
865 m_visitorOperator.insert_or_assign( op.first, op.second );
866}
867
868template <typename T>
869bool VariableSet::DynamicVisitor::removeOperator() {
870 assert( hasOperator<T>() );
871 auto res = m_visitorOperator.erase( getVariableVisitTypeIndex<T>() ) > 0;
872 return res;
873}
874
875template <typename T, typename F>
876struct VariableSet::DynamicVisitor::MakeVisitOperatorHelper<T, F, true> {
877 inline auto makeOperator( F& f ) -> OperatorsStorageType::value_type {
878 return { getVariableVisitTypeIndex<T>(), [&f]( std::any& a, std::any&& userParam ) {
879 auto rp = std::any_cast<std::reference_wrapper<VariableSet::Variable<T>>&>( a );
880 auto& p = rp.get();
881 f( p.first, p.second, std::forward<std::any>( userParam ) );
882 } };
883 }
884};
885
886template <typename T, typename F>
887struct VariableSet::DynamicVisitor::MakeVisitOperatorHelper<T, F, false> {
888 inline auto makeOperator( F& f ) -> OperatorsStorageType::value_type {
889 return { getVariableVisitTypeIndex<T>(), [&f]( std::any& a, std::any&& ) {
890 auto rp = std::any_cast<std::reference_wrapper<VariableSet::Variable<T>>&>( a );
891 auto& p = rp.get();
892 f( p.first, p.second );
893 } };
894 }
895};
896
897template <class T, class F>
898inline auto VariableSet::DynamicVisitor::makeVisitorOperator( F& f )
899 -> OperatorsStorageType::value_type {
900 auto opBuilder = MakeVisitOperatorHelper < T, F,
901 std::is_invocable<F, const std::string&, T, std::any&&>::value ||
902 std::is_invocable<F, const std::string&, T&, std::any&&>::value > {};
903 return opBuilder.makeOperator( f );
904}
905
906template <typename F>
907inline void VariableSet::visit( F&& visitor ) const {
909 visitDynamic( visitor );
910 }
911 else { visitStatic( visitor ); }
912}
913
914template <typename F, typename T>
915inline void VariableSet::visit( F&& visitor, T& userParams ) const {
917 visitDynamic( visitor, std::forward<T&>( userParams ) );
918 }
919 else { visitStatic( visitor, std::forward<T&>( userParams ) ); }
920}
921
922template <typename F, typename T>
923inline void VariableSet::visit( F&& visitor, T&& userParams ) const {
925 visitDynamic( visitor, std::forward<T&&>( userParams ) );
926 }
927 else { visitStatic( visitor, std::forward<T&&>( userParams ) ); }
928}
929
930} // namespace Core
931} // namespace Ra
T begin(T... args)
Base class for dynamically configurable visitors Users can implement this interface to build custom v...
virtual bool accept(const std::type_index &id) const =0
Acceptance function for the visitor.
virtual void operator()(std::any &&in, std::any &&userParam) const =0
Execute a visiting operator on accepted types.
Base class for visitors with configurable per-type callbacks. Visiting will be prepared at running ti...
~DynamicVisitor() override=default
allows the class to be derived
Heterogeneous container storing "Variables", that maps a name (std::string) to a value (of any type T...
auto getStoredTypes() const -> const std::vector< std::type_index > &
Gets the stored data type.
typename VariableContainer< T >::iterator VariableHandle
Handle of a variable A handle on a variable with type T is an iterator into the BaseContainer....
typename Variable< T >::second_type VariableType
Type of the variable value.
VariableSet(const VariableSet &other) noexcept
A VariableSet is copyable.
typename VariableContainer< T >::value_type Variable
Variable type as stored in the VariableSet.
VariableSet(VariableSet &&other) noexcept
A VariableSet is movable.
auto getVariable(const std::string &name) const -> const T &
get the value of the given variable
auto existsVariableType() const -> Utils::optional< VariableContainer< T > * >
Test if the storage supports a given variable type.
typename std::iterator_traits< H >::value_type::second_type VariableTypeFromHandle
Type of the variable referenced by a VariableHandle.
T declval(T... args)
T emplace(T... args)
T end(T... args)
T erase(T... args)
T find(T... args)
T forward(T... args)
T insert(T... args)
T move(T... args)
hepler function to manage enum as underlying types in VariableSet
Definition Cage.cpp:3
STL namespace.
T push_back(T... args)
T ref(T... args)
T remove(T... args)
Base class for visitors with static supported types. Visiting will be prepared at compile time by unf...