// Copyright (C) 2013-2020 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. #if !defined (COMMONAPI_INTERNAL_COMPILATION) #error "Only can be included directly, this file may disappear or change contents." #endif #ifndef COMMONAPI_VARIANT_HPP_ #define COMMONAPI_VARIANT_HPP_ #include #include #include #include #include #include #include #include #include namespace CommonAPI { template class InputStream; template class OutputStream; template struct MaxSize; template<> struct MaxSize<> { static const unsigned int value = 0; }; template struct MaxSize { static const unsigned int current_type_size = sizeof(Type_); static const unsigned int next_type_size = MaxSize::value; static const unsigned int value = current_type_size > next_type_size ? current_type_size : next_type_size; }; template struct VariantTypeSelector; template struct VariantTypeSelector { typedef SearchType_ type; }; /** * \brief A templated generic variant class which provides type safe access and operators * * A templated generic variant class which provides type safe access and operators */ template class Variant { private: typedef std::tuple_size> TypesTupleSize; public: static const unsigned int maxSize = MaxSize::value; /** * \brief Construct an empty variant * * Construct an empty variant */ Variant(); /** * \brief Copy constructor. Must have identical templates. * * Copy constructor. Must have identical templates. * * @param _source Variant to copy */ Variant(const Variant &_source); /** * \brief Copy constructor. Must have identical templates. * * Copy constructor. Must have identical templates. * * @param _source Variant to copy */ Variant(Variant &&_source); ~Variant(); /** * \brief Assignment of another variant. Must have identical templates. * * Assignment of another variant. Must have identical templates. * * @param _source Variant to assign */ Variant &operator=(const Variant &_source); /** * \brief Assignment of another variant. Must have identical templates. * * Assignment of another variant. Must have identical templates. * * @param _source Variant to assign */ Variant &operator=(Variant &&_source); /** * \brief Assignment of a contained type. Must be one of the valid templated types. * * Assignment of a contained type. Must be one of the valid templated types. * * @param _value Value to assign */ template typename std::enable_if>::value, Variant&>::type operator=(const Type_ &_value); /** * \brief Equality of another variant. Must have identical template list and content. * * Equality of another variant. Must have identical template list and content. * * @param _other Variant to compare */ bool operator==(const Variant &_other) const; /** * \brief Not-Equality of another variant. Must have identical template list and content. * * Not-Equality of another variant. Must have identical template list and content. * * @param _other Variant to compare */ bool operator!=(const Variant &_other) const; /** * \brief Testif the contained type is the same as the template on this method. * * Testif the contained type is the same as the template on this method. * * @return Is same type */ template bool isType() const; /** * \brief Construct variant with content type set to value. * * Construct variant with content type set to value. * * @param _value Value to place */ template Variant(const Type_ &_value, typename std::enable_if::value>::type* = 0, typename std::enable_if::value>::type* = 0, typename std::enable_if::value>::type* = 0); /** * \brief Construct variant with content type set to value. * * Construct variant with content type set to value. * * @param _value Value to place */ template Variant(Type_ &&_value, typename std::enable_if::value>::type* = 0, typename std::enable_if::value>::type* = 0, typename std::enable_if::value>::type* = 0); /** * \brief Get value of variant, template to content type. Throws exception if type is not contained. * * Get value of variant, template to content type. Throws exception if type is not contained. */ template const Type_ &get() const; /** * \brief Get index in template list of type actually contained, starting at 1 at the end of the template list * * Get index in template list of type actually contained, starting at 1 at the end of the template list * * @return Index of contained type */ uint8_t getValueType() const { return valueType_; } uint8_t getMaxValueType() const { return uint8_t(TypesTupleSize::value); } private: template void set(const Type_ &_value, const bool clear); template void set(Type_ &&_value, const bool clear); template friend struct TypeWriter; template friend struct AssignmentVisitor; template friend struct TypeEqualsVisitor; template friend struct PartialEqualsVisitor; template friend struct InputStreamReadVisitor; template friend struct ApplyVoidIndexVisitor; public: inline bool hasValue() const { return (valueType_ != 0 && valueType_ <= TypesTupleSize::value ); } typename std::aligned_storage::type valueStorage_; uint8_t valueType_; }; template struct ApplyVoidIndexVisitor; template struct ApplyVoidIndexVisitor { static const uint8_t index = 0; static void visit(Variant_ &, uint8_t &) { COMMONAPI_ERROR("ApplyVoidIndexVisitor::visit type not found"); } }; template struct ApplyVoidIndexVisitor { static const uint8_t index = ApplyVoidIndexVisitor::index + 1; static void visit(Variant_ &_variant, uint8_t &_index) { if (index == _index) { new (&_variant.valueStorage_) Type_(); _variant.valueType_ = index; } else { ApplyVoidIndexVisitor< Variant_, Types_... >::visit(_variant, _index); } } }; template struct ApplyVoidVisitor; template struct ApplyVoidVisitor { static const uint8_t index = 0; static void visit(Visitor_ &, Variant_ &) { COMMONAPI_ERROR("ApplyVoidIndexVisitor::visit - type not found"); } static void visit(Visitor_ &, const Variant_ &) { COMMONAPI_ERROR("ApplyVoidIndexVisitor::visit(const) - type not found"); } }; template struct ApplyVoidVisitor { static const uint8_t index = ApplyVoidVisitor::index + 1; static void visit(Visitor_ &_visitor, Variant_ &_variant) { if (_variant.getValueType() == index) { _visitor(_variant.template get()); } else { ApplyVoidVisitor< Visitor_, Variant_, Types_... >::visit(_visitor, _variant); } } static void visit(Visitor_ &_visitor, const Variant_ &_variant) { if (_variant.getValueType() == index) { _visitor(_variant.template get()); } else { ApplyVoidVisitor< Visitor_, Variant_, Types_... >::visit(_visitor, _variant); } } }; template struct ApplyBoolVisitor; template struct ApplyBoolVisitor { static const uint8_t index = 0; static bool visit(Visitor_ &, Variant_ &) { COMMONAPI_ERROR("ApplyBoolVisitor::visit - type not found"); return false; } }; template struct ApplyBoolVisitor { static const uint8_t index = ApplyBoolVisitor::index + 1; static bool visit(Visitor_ &_visitor, Variant_ &_variant) { if (_variant.getValueType() == index) { return _visitor(_variant.template get()); } else { return ApplyBoolVisitor< Visitor_, Variant_, Types_... >::visit(_visitor, _variant); } } }; template struct ApplyStreamVisitor; template struct ApplyStreamVisitor { static const uint8_t index = 0; static void visit(Visitor_ &, Variant_ &, const Deployment_ *) { COMMONAPI_ERROR("ApplyStreamVisitor::visit - type not found"); } static void visit(Visitor_ &, const Variant_ &, const Deployment_ *) { COMMONAPI_ERROR("ApplyStreamVisitor::visit(const) - type not found"); } }; template struct ApplyStreamVisitor { static const uint8_t index = ApplyStreamVisitor::index + 1; static void visit(Visitor_ &_visitor, Variant_ &_variant, const Deployment_ *_depl) { if (_variant.getValueType() == index) { _visitor(_variant.template get(), (_depl ? std::getvalues_)>::value-index>(_depl->values_) : nullptr)); } else { ApplyStreamVisitor< Visitor_, Variant_, Deployment_, Types_... >::visit(_visitor, _variant, _depl); } } static void visit(Visitor_ &_visitor, const Variant_ &_variant, const Deployment_ *_depl) { if (_variant.getValueType() == index) { _visitor(_variant.template get(), (_depl ? std::getvalues_)>::value-index>(_depl->values_) : nullptr)); } else { ApplyStreamVisitor< Visitor_, Variant_, Deployment_, Types_... >::visit(_visitor, _variant, _depl); } } }; template struct DeleteVisitor { public: DeleteVisitor(typename std::aligned_storage::type &_storage) : storage_(_storage) { } template void operator()(const Type_ &) const { (reinterpret_cast(&storage_))->~Type_(); } private: typename std::aligned_storage::type &storage_; }; template struct OutputStreamWriteVisitor { public: OutputStreamWriteVisitor(OutputStream &_output) : output_(_output) { } template void operator()(const Type_ &_value, const Deployment_ *_depl = nullptr) const { Deployable itsValue(_value, _depl); output_ << itsValue; } private: OutputStream &output_; }; template struct InputStreamReadVisitor { public: InputStreamReadVisitor(InputStream &_input, Variant &_target) : input_(_input), target_(_target) { } template void operator()(const Type_ &_value, const Deployment_ *_depl = nullptr) { (void)_value; Deployable itsValue(_depl); input_ >> itsValue; target_.Variant::template set(std::move(itsValue.getValue()), false); } private: InputStream &input_; Variant &target_; }; template struct TypeOutputStreamWriteVisitor { public: TypeOutputStreamWriteVisitor(Derived_ &_output) : output_(_output) { } template void operator()(const Type_ &_value, const Deployment_ *_depl = nullptr) const { Deployable _itsValue(_value, _depl); output_ << _itsValue; } private: Derived_ &output_; }; template struct TypeEqualsVisitor { public: TypeEqualsVisitor(const Type_ &_me) : me_(_me) { } bool operator()(const Type_ &_other) const { return (me_ == _other); } template bool operator()(const OtherType_ &) const { return false; } private: const Type_& me_; }; template struct PartialEqualsVisitor { public: PartialEqualsVisitor(const Variant &_me) : me_(_me) { } template bool operator()(const Type_ &_other) const { TypeEqualsVisitor visitor(_other); return ApplyBoolVisitor< TypeEqualsVisitor, const Variant, Types_... >::visit(visitor, me_); } private: const Variant &me_; }; template struct AssignmentVisitor { public: AssignmentVisitor(Variant &_me, const bool _clear = true) : me_(_me), clear_(_clear) { } template void operator()(const Type_ &_value) const { me_.Variant::template set(_value, clear_); } template void operator()(Type_ &_value) const { me_.Variant::template set(_value, clear_); } private: Variant &me_; const bool clear_; }; template struct TypeSelector; template struct TypeSelector { }; template struct TypeSelector { typedef Type_ type; }; template struct TypeSelector { typedef Type_ type; }; template struct TypeSelector { typedef Type_ type; }; template struct TypeSelector { typedef Type_ type; }; template struct TypeSelector { typedef Type_ type; }; template struct TypeSelector { typedef Type_& type; }; template struct TypeSelector { typedef Type_ type; }; template struct TypeSelector { typedef const Type_ &type; }; template struct TypeSelector { typedef Type_ type; }; template struct TypeSelector { typedef const Type_ *type; }; template struct TypeSelector { typedef const Type_ &type; }; template struct TypeSelector { typedef typename TypeSelector::type type; }; template struct TypeIndex; template<> struct TypeIndex<> { static const uint8_t index = 0; template static uint8_t get() { return 0; } }; template struct TypeIndex { static const uint8_t index = TypeIndex::index + 1; template static uint8_t get(typename std::enable_if::value>::type* = 0) { return index; } template static uint8_t get(typename std::enable_if::value>::type* = 0) { return TypeIndex::template get(); } }; template Variant::Variant() : valueType_(TypesTupleSize::value) { ApplyVoidIndexVisitor, Types_...>::visit(*this, valueType_); } template Variant::Variant(const Variant &_source) : valueType_(_source.valueType_) { AssignmentVisitor visitor(*this, false); ApplyVoidVisitor< AssignmentVisitor , Variant, Types_... >::visit(visitor, _source); } template Variant::Variant(Variant &&_source) : valueType_(_source.valueType_) { AssignmentVisitor visitor(*this, false); ApplyVoidVisitor< AssignmentVisitor , Variant, Types_... >::visit(visitor, _source); } template Variant::~Variant() { if (hasValue()) { DeleteVisitor visitor(valueStorage_); ApplyVoidVisitor< DeleteVisitor, Variant, Types_... >::visit(visitor, *this); } } template Variant& Variant::operator=(const Variant &_source) { AssignmentVisitor visitor(*this, hasValue()); ApplyVoidVisitor< AssignmentVisitor, Variant, Types_... >::visit(visitor, _source); return *this; } template Variant& Variant::operator=(Variant &&_source) { AssignmentVisitor visitor(*this, hasValue()); ApplyVoidVisitor< AssignmentVisitor, Variant, Types_... >::visit(visitor, _source); return *this; } template template typename std::enable_if>::value, Variant&>::type Variant::operator=(const Type_ &_value) { set::type>(_value, hasValue()); return *this; } template template bool Variant::isType() const { typedef typename TypeSelector::type selected_type_t; uint8_t itsType = TypeIndex::template get(); if (itsType == valueType_) { return true; } else { return false; } } template template Variant::Variant(const Type_ &_value, typename std::enable_if::value>::type*, typename std::enable_if::value>::type*, typename std::enable_if>::value>::type*) : valueType_(0x0) { set::type>(_value, false); } template template Variant::Variant(Type_ &&_value, typename std::enable_if::value>::type*, typename std::enable_if::value>::type*, typename std::enable_if>::value>::type*) : valueType_(0x0) { set::type>(std::move(_value), false); } template template const Type_ & Variant::get() const { typedef typename TypeSelector::type selected_type_t; uint8_t itsType = TypeIndex::template get(); if (itsType == valueType_) { return *(reinterpret_cast(&valueStorage_)); } else { #if defined(__EXCEPTIONS) || defined(_CPPUNWIND) std::bad_cast toThrow; throw toThrow; #else printf("Variant.hpp:%i %s: Incorrect access to variant; attempting to get type not currently contained", __LINE__, __FUNCTION__); abort(); #endif } } template template void Variant::set(const U_ &_value, const bool _clear) { typedef typename TypeSelector::type selected_type_t; if (_clear) { DeleteVisitor visitor(valueStorage_); ApplyVoidVisitor< DeleteVisitor, Variant, Types_... >::visit(visitor, *this); } new (&valueStorage_) selected_type_t(std::move(_value)); valueType_ = TypeIndex::template get(); } template template void Variant::set(U_ &&_value, const bool _clear) { typedef typename TypeSelector::type selected_type_t; selected_type_t&& any_container_value = std::move(_value); if(_clear) { DeleteVisitor visitor(valueStorage_); ApplyVoidVisitor< DeleteVisitor, Variant, Types_... >::visit(visitor, *this); } new (&valueStorage_) selected_type_t(std::move(any_container_value)); valueType_ = TypeIndex::template get(); } template bool Variant::operator==(const Variant &_other) const { PartialEqualsVisitor visitor(*this); return ApplyBoolVisitor< PartialEqualsVisitor, const Variant, Types_... >::visit(visitor, _other); } template bool Variant::operator!=(const Variant &_other) const { return !(*this == _other); } } // namespace CommonAPI #endif // COMMONAPI_VARIANT_HPP_