Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature add variant_cast #129

Merged
merged 8 commits into from
Mar 8, 2018
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was that wrong before? and did you already had a get_value function before but not in the interface in variant (I suppose arg is a variant?)

Copy link
Contributor Author

@acki-m acki-m Mar 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that one. So such a functionality was not yet there. Its up to the user to call that with the right type.


/*!
* \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
* 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 extracts the value by reference
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove extracts

* int* b = variant_cast<int>(&var);
* std::cout << "a valid: " << a != nullptr << std::endl;
* std::cout << "b valid: " << b != nullptr << std::endl;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably add "// b is nullptr"

* \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 extracts the value by reference
* int* b = variant_cast<int>(&var);
* std::cout << "a valid: " << a != nullptr << std::endl;
* std::cout << "b valid: " << b != nullptr << std::endl;
* \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