/* * * Copyright (C) 2016-2021, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by * * OFFIS e.V. * R&D Division Health * Escherweg 2 * D-26121 Oldenburg, Germany * * * Module: ofstd * * Author: Jan Schlamelcher * * Purpose: Implementing tagged unions similar to C++17's std::variant. * */ #ifndef OFVARIANT_H #define OFVARIANT_H #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ #include "dcmtk/ofstd/ofdiag.h" /** @file * Declares OFvariant and related functionality. * @defgroup ofvisit_variant OFvisit – OFvariant * Apply a visitor to an OFvariant object. * @see @ref ofget_variant "OFget" – @copybrief ofget_variant * @defgroup ofget_variant OFget – OFvariant * Get a pointer to the value stored in an OFvariant holding the selected alternative. * @see @ref ofvisit_variant "OFvisit" – @copybrief ofvisit_variant */ #ifdef HAVE_CXX11 #include #include #include // suppress warnings generated by old gcc 4.x compiler versions #include DCMTK_DIAGNOSTIC_PUSH #include DCMTK_DIAGNOSTIC_IGNORE_STRICT_ALIASING_WARNING template class OFvariant_traits { public: using is_constructible = std::false_type; static constexpr inline std::size_t alignment() { return 1; } static constexpr inline std::size_t size() { return 0; } static void index_of(); }; template class OFvariant_traits : OFvariant_traits { public: using OFvariant_traits::index_of; using first_alternative = Alternative0; static constexpr inline std::size_t alignment() { return alignof(aligned); } static constexpr inline std::size_t size() { return sizeof( first_alternative ) > OFvariant_traits::size() ? sizeof( first_alternative ) : OFvariant_traits::size() ; } static std::integral_constant index_of( first_alternative ); private: struct aligned { alignas(OFvariant_traits::alignment()) char c; first_alternative a; }; }; template struct OFvariant_select_index_type {}; template struct OFvariant_select_index_type { using type = typename std::conditional < AlternativeCount <= std::numeric_limits::max(), std::enable_if, OFvariant_select_index_type >::type::type; }; template class OFvariant; template struct OFvariant_alternative; template struct OFvariant_alternative> : OFvariant_alternative> {}; template struct OFvariant_alternative<0,OFvariant> { using type = Alternative0; }; template class OFvariant { using traits = OFvariant_traits<0,Alternatives...>; using index_type = typename OFvariant_select_index_type::type; template using index_of = decltype(traits::index_of(std::declval())); public: using variant = OFvariant; OFvariant() : m_Content() , m_Index( 0 ) { new (m_Content) typename traits::first_alternative; } template()> OFvariant( T&& t ) : m_Content() , m_Index( Index ) { new (m_Content) typename OFvariant_alternative::type( std::forward( t ) ); } OFvariant( OFvariant& rhs ) : OFvariant( const_cast( rhs ) ) { } OFvariant( const OFvariant& rhs ) : m_Content() , m_Index( rhs.index() ) { copy_construct( rhs ); } OFvariant( OFvariant&& rhs ) : m_Content() , m_Index( rhs.index() ) { move_construct( std::move( rhs ) ); } template()> OFvariant& operator=( T&& t ) { if( m_Index != Index ) { destroy(); m_Index = Index; new (m_Content) typename OFvariant_alternative::type( std::forward( t ) ); } else { *reinterpret_cast::type*>( m_Content ) = std::forward( t ); } return *this; } OFvariant& operator=( OFvariant& rhs ) { return *this = const_cast( rhs ); } OFvariant& operator=( const OFvariant& rhs ) { if( this != &rhs ) { if( m_Index != rhs.m_Index ) { destroy(); m_Index = rhs.m_Index; copy_construct( rhs ); } else { using functor = void(OFvariant::*)(const OFvariant&); static const functor assignment[] = { &OFvariant::template copy_assign_alternative... }; (this->*assignment[m_Index])( rhs ); } } return *this; } OFvariant& operator=( OFvariant&& rhs ) { if( this != &rhs ) { if( m_Index != rhs.m_Index ) { destroy(); m_Index = rhs.m_Index; move_construct( std::move( rhs ) ); } else { using functor = void(OFvariant::*)(OFvariant&&); static const functor assignment[] = { &OFvariant::template move_assign_alternative... }; (this->*assignment[m_Index])( std::move( rhs ) ); } } return *this; } ~OFvariant() { destroy(); } std::size_t index() const { return m_Index; } private: template friend T* OFget( OFvariant* ); template friend const T* OFget( const OFvariant* ); template friend ReturnType OFvisit( FN&&, OFvariant& ); template friend ReturnType OFvisit( FN&&, const OFvariant& ); template void copy_construct_alternative( const OFvariant& rhs ) { new (m_Content) Alternative( *reinterpret_cast( rhs.m_Content ) ); } template void move_construct_alternative( OFvariant&& rhs ) { new (m_Content) Alternative( std::move( *reinterpret_cast( rhs.m_Content ) ) ); } template void copy_assign_alternative( const OFvariant& rhs ) { *reinterpret_cast( m_Content ) = *reinterpret_cast( rhs.m_Content ); } template void move_assign_alternative( OFvariant&& rhs ) { *reinterpret_cast( m_Content ) = std::move( *reinterpret_cast( rhs.m_Content ) ); } template void destructor() { reinterpret_cast( m_Content )->~Alternative(); } template ReturnType visit_alternative( FN&& fn ) { return fn( *reinterpret_cast( m_Content ) ); } template ReturnType const_visit_alternative( FN&& fn ) const { return fn( *reinterpret_cast( m_Content ) ); } void copy_construct( const OFvariant& rhs ) { using functor = void(OFvariant::*)(const OFvariant&); static const functor constructor[] = { &OFvariant::template copy_construct_alternative... }; assert( m_Index < sizeof...(Alternatives) ); (this->*constructor[m_Index])( rhs ); } void move_construct( OFvariant&& rhs ) { using functor = void(OFvariant::*)(OFvariant&&); static const functor constructor[] = { &OFvariant::template move_construct_alternative... }; assert( m_Index < sizeof...(Alternatives) ); (this->*constructor[m_Index])( std::move( rhs ) ); } void destroy() { using functor = void(OFvariant::*)(); static const functor destructor[] = { &OFvariant::template destructor... }; assert( m_Index < sizeof...(Alternatives) ); (this->*destructor[m_Index])(); } alignas(traits::alignment()) std::uint8_t m_Content[traits::size()]; index_type m_Index; }; template struct OFvariant_index_of_type : OFvariant_index_of_type {}; template struct OFvariant_index_of_type : std::integral_constant {}; template constexpr bool OFholds_alternative( const OFvariant& v ) { return v.index() == OFvariant_index_of_type<0,T,Alternatives...>::value; } template T* OFget( OFvariant* v ) { if( OFholds_alternative( *v ) ) return reinterpret_cast( v->m_Content ); return nullptr; } template const T* OFget( const OFvariant* v ) { if( OFholds_alternative( *v ) ) return reinterpret_cast( v->m_Content ); return nullptr; } template ReturnType OFvisit( FN&& fn, OFvariant& v ) { using functor = ReturnType(OFvariant::*)(FN&&); static const functor visit[] = { &OFvariant::template visit_alternative... }; return (v.*visit[v.index()])( std::forward( fn ) ); } template ReturnType OFvisit( FN&& fn, const OFvariant& v ) { using functor = ReturnType(OFvariant::*)(FN&&) const; static const functor visit[] = { &OFvariant::template const_visit_alternative... }; return (v.*visit[v.index()])( std::forward( fn ) ); } #include DCMTK_DIAGNOSTIC_POP #elif !defined(DOXYGEN) // fallback implementation // Include the actual implementation (that emulates variadic templates) #include "dcmtk/ofstd/variadic/variant.h" #else // NOT C++11 && NOT DOXYGEN /** A class template that represents a type-safe union. * #include "dcmtk/ofstd/ofvriant.h" for using this class
* @headerfile ofvriant.h "dcmtk/ofstd/ofvriant.h" * @tparam Alternatives a set of types that may be stored in this variant. All types must be (possibly * cv-qualified) object types. * @details * OFvariant is a custom implementation of a subset of C++17's std::variant, * see http://en.cppreference.com/w/cpp/utility/variant for a description of std::variant. * An instance of OFvariant at any given time holds a value of one of its alternative types. * As with unions, if a variant holds a value of some object type T, the object representation of T is * allocated directly within the object representation of the variant itself if possible. * @note If no suitable alignment specifiers were available for the target platform, OFvariant will * use a fallback implementation that stores the alternative on the heap – as opposite to std::variant. * @details * The preferred way to access an OFvariant object is visitation utilizing @ref ofvisit_variant "OFvisit". * If a certain alternative is expected to be held by the variant, @ref ofget_variant "OFget" may be used to * access it directly. * @see @ref ofvisit_variant "OFvisit" – @copybrief ofvisit_variant * @see @ref ofget_variant "OFget" – @copybrief ofget_variant * @see OFmonostate – @copybrief OFmonostate * @see @ref OFin_place_helpers "OFin_place" – @copydoc OFin_place_helpers_brief */ template class OFvariant { public: /** Constructs a variant holding a default constructed value of the first alternative. * @pre The first alternative must be default constructible. * @see OFmonostate – @copybrief OFmonostate */ OFvariant(); /** Copy constructs a variant holding a copy of the value rhs holds. * @param rhs a const reference to another object of equal type. * @pre All alternatives must be copy constructible. */ OFvariant( const OFvariant& rhs ); /** Move constructs a variant by moving the value rhs holds. * @param rhs an rvalue reference to another object of equal type. * @pre All alternatives must be move constructible. */ OFvariant( OFvariant&& rhs ); /** Constructs a variant holding the alternative that most closely matches the given * argument. * @tparam T the type of the argument, will be deduced automatically. * @param t an object of type `T` that will be converted to one of the alternatives. * @remark There must be at least one alternative that can be constructed from * the given parameter `t` and there must be exactly one such alternative that * takes precedence over the others. * @attention t will be perfectly forwarded if C++11 support is available, i.e. the * alternative may be move constructed from `t` if possible. Support for perfect * forwarding is NOT available without C++11 support, therefore the alternative * will be copy constructed in this case, this means: the selected alternative * must be copy constructible if pre C++11 compilers shall be supported. * @details *

Usage Example:

* @code{.cpp} * OFvariant( 3 ); // OK * OFvariant( 3 ); // ill formed, both alternatives take equal precedence * OFvariant( "abc" ); // OK, but chooses OFBool! * @endcode */ template OFvariant( T t ); /** Destroys the value that the variant currently holds. */ ~OFvariant(); /** Copy assigns the value rhs holds to *this. * @param rhs a const reference to another object of equal type. * @pre all alternatives must be copy constructible and copy assignable. * @return `*this` * @post * @li if `*this` and `rhs` hold the same alternative, the value contained in `rhs` * is copy assigned to the value contained in `*this`. * @li if `*this` and `rhs` hold different alternatives, the value contained in `*this` * is destroyed and a new one is copy constructed from the value contained in `rhs`. */ OFvariant& operator=( const OFvariant& rhs ); /** Move assigns the value rhs holds to *this. * @param rhs an rvalue reference to another object of equal type. * @pre all alternatives must be move constructible and move assignable. * @return `*this` * @post * @li if `*this` and `rhs` hold the same alternative, the value contained in `rhs` * is move assigned to the value contained in `*this`. * @li if `*this` and `rhs` hold different alternatives, the value contained in `*this` * is destroyed and a new one is move constructed from the value contained in `rhs`. */ OFvariant& operator=( OFvariant&& rhs ); /** Converts the given argument to one of the alternatives and assigns it to *this. * @tparam T the type of the argument, will be deduced automatically. * @param t an object of type `T` that will be converted to one of the alternatives * for assignment. * @return `*this` * @pre There must be at least one alternative that can be constructed from * the given parameter `t` and there must be exactly one such alternative that * takes precedence over the others. * @attention `t` will be perfectly forwarded if C++11 support is available, i.e. the * alternative may be move constructed from t if possible. Support for perfect * forwarding is NOT available without C++11 support, therefore the alternative * will be copy constructed in this case, this means: the selected alternative * must be copy constructible if pre C++11 compilers shall be supported. * @details *

Usage Example:

* @code{.cpp} * OFvariant v1; * v1 = 3 // OK * OFvariant v2; * v2 = 3 // ill formed, both alternatives take equal precedence * OFvariant v3; * v3 = "abc"; // OK, but chooses OFBool! * @endcode */ template OFvariant& operator=( T t ); /** Get the index of alternative that is currently being held. * @return the zero based index of that alternative that is currently being held by * `*this`, i.e. `0` for the first alternative, `1` for the second, etc. */ size_t index() const; }; /** Try to get a pointer to the given alternative from an OFvariant object. * @ingroup ofget_variant * @relates OFvariant * @tparam Alternative the chosen alternative that shall be accessed. * @tparam Alternatives the alternatives the given variant could hold, will be deduced * automatically. * @param v a reference to an OFvariant object potentially holding `Alternative`. * @return the address of the contained value of type `Alternative` if such a value is * contained. `OFnullptr` otherwise. * @details *

Usage Example:

* @code{.cpp} * OFvariant v; * // ... some value is assigned to v ... * if( int* pI = OFget( v ) ) * { * COUT << "Yes, it really is an int with the value " << *pI << '!' << OFendl; * *pI = "27"; // now, let's directly assign something else * } * @endcode */ template Alternative* OFget( OFvariant* v ); /** Try to get a pointer to the given alternative from an OFvariant object. * @ingroup ofget_variant * @relates OFvariant * @tparam Alternative the chosen alternative that shall be accessed. * @tparam Alternatives the alternatives the given variant could hold, will be deduced * automatically. * @param v a const reference to an OFvariant object potentially holding `Alternative`. * @return the address of the contained value of type `const Alternative` if such a value is * contained. `OFnullptr` otherwise. * @details *

Usage Example:

* @code{.cpp} * const OFvariant v( ... some value is assigned to v ... ); * if( int* pI = OFget( v ) ) // error, the result is const! * if( const int* pI = OFget( v ) ) // OK * { * COUT << "Yes, it really is an int with the value " << *pI << '!' << OFendl; * *pI = "27"; // Error, *pI is const! * } * @endcode */ template const Alternative* OFget( const OFvariant* v ); /** Applies the given visitor to the given OFvariant object. * @ingroup ofvisit_variant * @relates OFvariant * @details@anchor ofget_alternative_const_variant * @tparam Result the type of the returned value. Pre C++11 compiles do not allow determining the * result type automatically in a portable way, therefore, it must be explicitly given by * the caller. * @tparam Visitor the type of the visitor, will be deduced automatically. * @tparam Alternatives the alternatives the given variant could hold, will be deduced * automatically. * @param visitor the visitor that will be invoked with the alternative currently being * held by the given OFvariant object. * @param v a reference to an OFvariant object that is going to be visited. * @return Let `CurrentAlternative` be the alternative that v currently holds: the result * of `visitor( *OFget( &v ) )` will be converted to `Result` and then * returned. * @pre all possible results must be convertible to `Result`. * @pre `visitor` must be able to take all possible alternatives. * @note If C++11 support is available, the visitor will be forwarded using perfect forwarding. * If not, the visitor may be copy constructed at least once, therefore, the visitor needs * to be copy constructible when pre C++11 compilers are targeted. * @details *

Usage Example:

* @code{.cpp} * struct PowerVisitor * { * template * Number operator()( Number number ) * { * return number * number; * } * }; * struct PrintVisitor * { * template * void operator()( Number number ) * { * COUT << number << OFendl; * } * }; * struct AssignVisitor * { * template * void operator()( Number& number ) * { * number *= number; * } * }; * // ... * OFvariant v( 3.14 ); * OFvariant result = OFvisit >( PowerVisitor(), v ); * switch( result.index() ) * { * case 0: COUT << "int "; break; * case 1: COUT << "float "; break; * case 2: COUT << "double "; break; * } * OFvisit( PrintVisitor(), result ); * COUT << "double " << OFvisit( PowerVisitor(), v ) << OFendl; // OK, every alternative fits inside double * COUT << "int " << OFvisit( PowerVisitor(), v ) << OFendl; // OK, value will be truncated! * COUT << "string " << OFvisit( PowerVisitor(), v ) << OFendl; // ERROR! * OFvisit( AssignVisitor(), v ); * OFvisit( PrintVisitor(), v ); * @endcode * Output (if the error was removed): * @verbatim double 9.8596 double 9.8596 int 9 9.8596 @endverbatim */ template Result OFvisit( Visitor visitor, OFvariant& v ); /** Applies the given visitor to the given OFvariant object. * @ingroup ofvisit_variant * @relates OFvariant * @tparam Result the type of the returned value. Pre C++11 compiles do not allow determining the * result type automatically in a portable way, therefore, it must be explicitly given by * the caller. * @tparam Visitor the type of the visitor, will be deduced automatically. * @tparam Alternatives the alternatives the given variant could hold, will be deduced * automatically. * @param visitor the visitor that will be invoked with the alternative currently being * held by the given OFvariant object. * @param v a const reference to an OFvariant object that is going to be visited. * @return Let `CurrentAlternative` be the alternative that v currently holds: the result * of `visitor( *OFget( &v ) )` will be converted to `Result` and then * returned. * @pre all possible results must be convertible to `Result`. * @pre `visitor` must be able to take all possible alternatives. * @note If C++11 support is available, the visitor will be forwarded using perfect forwarding. * If not, the visitor may be copy constructed at least once, therefore, the visitor needs * to be copy constructible when pre C++11 compilers are targeted. * @details *

Usage Example:

* @code{.cpp} * struct PowerVisitor * { * template * Number operator()( Number number ) * { * return number * number; * } * }; * struct PrintVisitor * { * template * void operator()( Number number ) * { * COUT << number << OFendl; * } * }; * struct AssignVisitor * { * template * void operator()( Number& number ) * { * number *= number; * } * }; * // ... * const OFvariant v( 3.14 ); * OFvariant result = OFvisit >( PowerVisitor(), v ); * switch( result.index() ) * { * case 0: COUT << "int "; break; * case 1: COUT << "float "; break; * case 2: COUT << "double "; break; * } * OFvisit( PrintVisitor(), result ); * COUT << "double " << OFvisit( PowerVisitor(), v ) << OFendl; // OK, every alternative fits inside double * COUT << "int " << OFvisit( PowerVisitor(), v ) << OFendl; // OK, value will be truncated! * COUT << "string " << OFvisit( PowerVisitor(), v ) << OFendl; // ERROR! * OFvisit( AssignVisitor(), v ); // ERROR, v is const! * OFvisit( PrintVisitor(), v ); * @endcode * Output (if the errors were removed): * @verbatim double 9.8596 double 9.8596 int 9 3.14 @endverbatim */ template Result OFvisit( Visitor visitor, const OFvariant& v ); #endif // DOXYGEN /** A helper type for making OFvariant default constructible. * @relates OFvariant * @details * Use OFmonostate as the first alternative of an OFvariant otherwise holding a non default constructible type as * the first alternative for making the variant itself default constructible. * @note Be aware that any visitor applied to such an OFvariant object must also accept OFmonostate as an argument. * @details *

Example

* @code{.cpp} * template * struct NonDefaultConstructible * { * NonDefaultConstructible( T t ) : value( t ) {} * T value; * }; * typedef NonDefaultConstructible nint; * typedef NonDefaultConstructible nfloat; * // ... * OFvariant v( 3 ); // OK, but what if we don't know the value yet? * OFvariant v; // ERROR! * OFvariant v; // OK * v = 3; // assign the value when it's known * @endcode */ struct OFmonostate {}; #endif // OFVARIANT_H