Skip to content

Commit

Permalink
Feature add variant_cast (#129)
Browse files Browse the repository at this point in the history
* Added global "variant_cast" method

Added 4 overloads of the method "variant_cast"
In order to move values out, to return values by reference
or to return a pointer to the underlying values, when the type does match:

template<class T>
T variant_cast(variant&& operand);

template<class T>
T variant_cast(variant& operand);

template<class T>
T variant_cast(const variant& operand);

template<class T>
const T* variant_cast(const variant* operand) RTTR_NOEXCEPT;

template<class T>
T* variant_cast(variant* operand) RTTR_NOEXCEPT;

Additionally:
- added non-const version of variant::get_value()
- fixed copy-right year in root CMakeLists.txt file

Fixes #108
  • Loading branch information
acki-m authored Mar 8, 2018
1 parent 0b4d72f commit 6e28c69
Show file tree
Hide file tree
Showing 8 changed files with 511 additions and 7 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
####################################################################################
# #
# Copyright (c) 2014, 2015 - 2017 Axel Menzel <info@rttr.org> #
# Copyright (c) 2014 - 2018 Axel Menzel <info@rttr.org> #
# #
# This file is part of RTTR (Run Time Type Reflection) #
# License: MIT License #
Expand Down
3 changes: 3 additions & 0 deletions src/rttr/detail/misc/std_type_traits.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,9 @@ using decay_t = typename std::decay<T>::type;
template<typename T>
using add_const_t = typename std::add_const<T>::type;

template<typename T>
using add_lvalue_reference_t = typename std::add_lvalue_reference<T>::type;

/////////////////////////////////////////////////////////////////////////////////////

} // end namespace detail
Expand Down
4 changes: 2 additions & 2 deletions src/rttr/detail/variant/variant_data_policy.h
Original file line number Diff line number Diff line change
Expand Up @@ -652,7 +652,7 @@ struct RTTR_API variant_data_policy_empty
}
case variant_policy_operation::GET_VALUE:
{
arg.get_value<void*>() = nullptr;
arg.get_value<const void*>() = nullptr;
break;
}
case variant_policy_operation::GET_TYPE:
Expand Down Expand Up @@ -767,7 +767,7 @@ struct RTTR_API variant_data_policy_void
}
case variant_policy_operation::GET_VALUE:
{
arg.get_value<void*>() = nullptr;
arg.get_value<const void*>() = nullptr;
break;
}
case variant_policy_operation::GET_TYPE:
Expand Down
111 changes: 107 additions & 4 deletions src/rttr/detail/variant/variant_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@

namespace rttr
{
namespace detail
{
template<typename T>
using variant_t = remove_cv_t<remove_reference_t<T>>;
}

/////////////////////////////////////////////////////////////////////////////////////////

Expand Down Expand Up @@ -130,13 +135,24 @@ RTTR_INLINE bool variant::operator>(const variant& other) const

/////////////////////////////////////////////////////////////////////////////////////////

template<typename T>
RTTR_INLINE T& variant::get_value()
{
using namespace detail;
auto result = unsafe_variant_cast<variant_t<T>>(this);

return *result;
}

/////////////////////////////////////////////////////////////////////////////////////////

template<typename T>
RTTR_INLINE const T& variant::get_value() const
{
const void* value;
m_policy(detail::variant_policy_operation::GET_VALUE, m_data, value);
using nonRef = detail::remove_cv_t<T>;
return *reinterpret_cast<const nonRef*>(value);
using namespace detail;
auto result = unsafe_variant_cast<variant_t<T>>(this);

return *result;
}

/////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -390,6 +406,93 @@ RTTR_INLINE T variant::convert(bool* ok) const
return convert_impl<T>(ok);
}

/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////

namespace detail
{

/////////////////////////////////////////////////////////////////////////////////////////

template<class T>
RTTR_INLINE T* unsafe_variant_cast(variant* operand) RTTR_NOEXCEPT
{
const void* value;
operand->m_policy(detail::variant_policy_operation::GET_VALUE, operand->m_data, value);
return reinterpret_cast<T*>(const_cast<void*>(value));
}

/////////////////////////////////////////////////////////////////////////////////////////

template<class T>
RTTR_INLINE const T* unsafe_variant_cast(const variant* operand) RTTR_NOEXCEPT
{
return unsafe_variant_cast<const T>(const_cast<variant*>(operand));
}

} // end namespace detail

/////////////////////////////////////////////////////////////////////////////////////////

template<class T>
RTTR_INLINE T variant_cast(const variant& operand)
{
using namespace detail;
static_assert(std::is_constructible<T, const variant_t<T>&>::value,
"variant_cast<T>(variant&) requires T to be constructible from const remove_cv_t<remove_reference_t<T>>&");

auto result = unsafe_variant_cast<variant_t<T>>(&operand);

using ref_type = conditional_t<std::is_reference<T>::value, T, add_lvalue_reference_t<T>>;
return static_cast<ref_type>(*result);
}

/////////////////////////////////////////////////////////////////////////////////////////

template<class T>
RTTR_INLINE T variant_cast(variant& operand)
{
using namespace detail;
static_assert(std::is_constructible<T, variant_t<T>&>::value,
"variant_cast<T>(variant&) requires T to be constructible from remove_cv_t<remove_reference_t<T>>&");

auto result = unsafe_variant_cast<variant_t<T>>(&operand);

using ref_type = conditional_t<std::is_reference<T>::value, T, add_lvalue_reference_t<T>>;
return static_cast<ref_type>(*result);
}

/////////////////////////////////////////////////////////////////////////////////////////

template<class T>
RTTR_INLINE T variant_cast(variant&& operand)
{
using namespace detail;
static_assert(std::is_constructible<T, variant_t<T>>::value,
"variant_cast<T>(variant&&) requires T to be constructible from remove_cv_t<remove_reference_t<T>>");
auto result = unsafe_variant_cast<variant_t<T>>(&operand);
return std::move(*result);
}

/////////////////////////////////////////////////////////////////////////////////////////

template<class T>
RTTR_INLINE T* variant_cast(variant* operand) RTTR_NOEXCEPT
{
using namespace detail;
return (type::get<T>() == operand->get_type()) ?
unsafe_variant_cast<T>(operand) : nullptr;
}

/////////////////////////////////////////////////////////////////////////////////////////

template<class T>
RTTR_INLINE const T* variant_cast(const variant* operand) RTTR_NOEXCEPT
{
return variant_cast<T>(const_cast<variant*>(operand));
}

/////////////////////////////////////////////////////////////////////////////////////////

} // end namespace rttr
Expand Down
125 changes: 125 additions & 0 deletions src/rttr/variant.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ class instance;

namespace detail
{
template<class T>
RTTR_INLINE T* unsafe_variant_cast(variant* operand) RTTR_NOEXCEPT;
template<class T>
RTTR_INLINE const T* unsafe_variant_cast(const variant* operand) RTTR_NOEXCEPT;

struct data_address_container;
template<typename T>
struct empty_type_converter;
Expand Down Expand Up @@ -423,6 +428,32 @@ class RTTR_API variant
*/
bool is_sequential_container() const;

/*!
* \brief Returns a reference to the containing value as type \p T.
*
* \code{.cpp}
* struct custom_type
* {
* //...
* };
*
* variant var = custom_type{};
* if (var.is_type<custom_type>()) // yields to true
* custom_type& value = var.get_value<custom_type>(); // extracts the value by reference
* \endcode
*
* \remark Only call this method when it is possible to return the containing value as the given type \p T.
* Use therefore the method \ref is_type().
* Otherwise the call leads to undefined behaviour.
* Also make sure you don't clean this variant, when you still hold a reference to the containing value.
*
* \see is_type()
*
* \return A reference to the stored value.
*/
template<typename T>
T& get_value();

/*!
* \brief Returns a reference to the containing value as type \p T.
*
Expand Down Expand Up @@ -1067,13 +1098,107 @@ class RTTR_API variant
friend struct detail::variant_data_base_policy;
friend struct detail::variant_data_policy_nullptr_t;
friend RTTR_API bool detail::variant_compare_less(const variant&, const type&, const variant&, const type&, bool& ok);
template<class T>
friend RTTR_INLINE T* detail::unsafe_variant_cast(variant* operand) RTTR_NOEXCEPT;


detail::variant_data m_data;
detail::variant_policy_func m_policy;
};

/////////////////////////////////////////////////////////////////////////////////////////

/*!
* \brief Returns a reference to the containing value as type \p T.
*
* \code{.cpp}
*
* variant var = std::string("hello world");
* std:string& value_ref = variant_cast<std::string&>(var); // extracts the value by reference
* std:string value = variant_cast<std::string>(var); // copies the value
*
* \endcode
*
* \remark Extracting a value type, which is not stored in the variant, leads to undefined behaviour.
* No exception or error code will be returned!
*/
template<class T>
T variant_cast(const variant& operand);

/*!
* \brief Returns a reference to the containing value as type \p T.
*
* \code{.cpp}
*
* variant var = std::string("hello world");
* std:string& value_ref = variant_cast<std::string&>(var); // extracts the value by reference
* std:string value = variant_cast<std::string>(var); // copies the value
*
* \endcode
*
* \remark Extracting a value type, which is not stored in the variant, leads to undefined behaviour.
* No exception or error code will be returned!
*/
template<class T>
T variant_cast(variant& operand);

/*!
* \brief Move the containing value from the variant into a type \p T.
*
* \code{.cpp}
*
* variant var = std::string("hello world");
* std::string& a = variant_cast<std::string&>(var);
* std:string b = variant_cast<std::string>(std::move(var)); // move the value to 'b'
* std::cout << "a: " << a << std::endl; // is now empty (nothing to print)
* std::cout << "b: " << b << std::endl; // prints "hello world"
*
* \endcode
*
* \remark Extracting a value type, which is not stored in the variant, leads to undefined behaviour.
* No exception or error code will be returned!
*/
template<class T>
T variant_cast(variant&& operand);

/*!
* \brief Returns a pointer to the containing value with type \p T.
* When the containing value is of type \p T, a valid pointer to the type will be returned.
* Otherwise a `nullptr` is returned.
*
* \code{.cpp}
*
* variant var = std::string("hello world");
* std:string* a = variant_cast<std::string>(&var); // performs an internal type check and returns the value by reference
* int* b = variant_cast<int>(&var);
* std::cout << "a valid: " << a != nullptr << std::endl; // prints "1"
* std::cout << "b valid: " << b != nullptr << std::endl; // prints "0"
* \endcode
*
* \return A valid pointer, when the containing type is of type \p T; otherwise a `nullptr`.
*/
template<class T>
const T* variant_cast(const variant* operand) RTTR_NOEXCEPT;

/*!
* \brief Returns a pointer to the containing value with type \p T.
* When the containing value is of type \p T, a valid pointer to the type will be returned.
* Otherwise a `nullptr` is returned.
*
* \code{.cpp}
*
* variant var = std::string("hello world");
* std:string* a = variant_cast<std::string>(&var); // performs an internal type check and returns the value by reference
* int* b = variant_cast<int>(&var);
* std::cout << "a valid: " << a != nullptr << std::endl; // prints "1"
* std::cout << "b valid: " << b != nullptr << std::endl; // prints "0"
* \endcode
*
* \return A valid pointer, when the containing type is of type \p T; otherwise a `nullptr`.
*/
template<class T>
T* variant_cast(variant* operand) RTTR_NOEXCEPT;

} // end namespace rttr

#include "rttr/detail/variant/variant_impl.h"
Expand Down
1 change: 1 addition & 0 deletions src/unit_tests/unit_tests.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ set(SOURCE_FILES main.cpp
variant/variant_cmp_less_or_equal.cpp
variant/variant_cmp_greater_or_equal.cpp
variant/variant_misc_test.cpp
variant/variant_cast_test.cpp
variant/variant_conv_to_bool.cpp
variant/variant_conv_to_int8.cpp
variant/variant_conv_to_int16.cpp
Expand Down
Loading

0 comments on commit 6e28c69

Please sign in to comment.