Unverified Commit 2b8f9e40 authored by pfeatherstone's avatar pfeatherstone Committed by GitHub
Browse files

[TYPE_SAFE_UNION] upgrade (#2443)



* [TYPE_SAFE_UNION] upgrade

* MSVC doesn't like keyword not

* MSVC doesn't like keyword and

* added tests for emplate(), copy semantics, move semantics, swap, overloaded and apply_to_contents with non void return types

* - didn't need is_void anymore
- added result_of_t
- didn't really need ostream_helper or istream_helper
- split apply_to_contents into apply_to_contents (return void) and visit (return anything so long as visitor is publicly accessible)

* - updated abstract file

* - added get_type_t
- removed deserialize_helper dupplicate
- don't use std::decay_t, that's c++14

* - removed white spaces
- don't need a return-statement when calling apply_to_contents_impl()
- use unchecked_get() whenever possible to minimise explicit use of pointer casting. lets keep that to a minimum

* - added type_safe_union_size
- added type_safe_union_size_v if C++14 is available
- added tests for above

* - test type_safe_union_size_v

* testing nested unions with visitors.

* re-added comment

* added index() in abstract file

* - refactored reset() to clear()
- added comment about clear() in abstract file
- in deserialize(), only reset the object if necessary

* - removed unecessary comment about exceptions
- removed unecessary // -------------
- struct is_valid is not mentioned in abstract. Instead rather requiring T to be a valid type, it is ensured!
- get_type and get_type_t are private. Client code shouldn't need this.
- shuffled some functions around
- type_safe_union_size and type_safe_union_size_v are removed. not needed
- reset() -> clear()
- bug fix in deserialize() index counts from 1, not 0
- improved the abstract file

* refactored index() to get_current_type_id() as per suggestion

* maybe slightly improved docs

* - HURRAY, don't need std::result_of or std::invoke_result for visit() to work. Just privately define your own type trait, in this case called return_type and return_type_t. it works!
- apply_to_contents() now always calls visit()

* example with private visitor using friendship with non-void return types.

* Fix up contracts

It can't be a post condition that T is a valid type, since the choice of T is up to the caller, it's not something these functions decide.  Making it a precondition.

* Update dlib/type_safe_union/type_safe_union_kernel_abstract.h

* Update dlib/type_safe_union/type_safe_union_kernel_abstract.h

* Update dlib/type_safe_union/type_safe_union_kernel_abstract.h

* - added more tests for copy constructors/assignments, move constructors/assignments, and converting constructors/assignments
- helper_copy -> helper_forward
- added validate_type<T> in a couple of places

* - helper_move only takes non-const lvalue references. So we are not using std::move with universal references !
- use enable_if<is_valid<T>> in favor of validate_type<T>()

* - use enable_if<is_valid<T>> in favor of validate_type<T>()

* - added is_valid_check<>. This wraps enable_if<is_valid<T>,bool> and makes use of SFINAE more robust
Co-authored-by: default avatarpfeatherstone <peter@me>
Co-authored-by: default avatarpf <pf@me>
Co-authored-by: default avatarDavis E. King <davis685@gmail.com>
parent bf410006
...@@ -429,7 +429,6 @@ namespace ...@@ -429,7 +429,6 @@ namespace
b = 3; b = 3;
b = std::move(a); b = std::move(a);
DLIB_TEST(a.get<int>() == 3);
DLIB_TEST(b.get<std::string>() == "asdf"); DLIB_TEST(b.get<std::string>() == "asdf");
} }
...@@ -458,11 +457,214 @@ namespace ...@@ -458,11 +457,214 @@ namespace
DLIB_TEST(!a.contains<ptr_t>()); DLIB_TEST(!a.contains<ptr_t>());
DLIB_TEST(*b.get<ptr_t>() == "asdf"); DLIB_TEST(*b.get<ptr_t>() == "asdf");
} }
}
}; {
//testing copy semantics and move semantics
struct mytype
{
mytype(int i_ = 0) : i(i_) {}
mytype(const mytype& other) : i(other.i) {}
mytype& operator=(const mytype& other) {i = other.i; return *this;}
mytype(mytype&& other) : i(other.i) {other.i = 0;}
mytype& operator=(mytype&& other) {i = other.i ; other.i = 0; return *this;}
int i = 0;
};
using tsu = type_safe_union<int,mytype>;
{
mytype a(10);
tsu ta(a); //copy constructor
DLIB_TEST(a.i == 10);
DLIB_TEST(ta.cast_to<mytype>().i == 10);
}
{
mytype a(10);
tsu ta;
ta = a; //copy assign
DLIB_TEST(a.i == 10);
DLIB_TEST(ta.cast_to<mytype>().i == 10);
}
{
mytype a(10);
tsu ta(std::move(a)); //move constructor
DLIB_TEST(a.i == 0);
DLIB_TEST(ta.cast_to<mytype>().i == 10);
}
{
mytype a(10);
tsu ta;
ta = std::move(a); //move assign
DLIB_TEST(a.i == 0);
DLIB_TEST(ta.cast_to<mytype>().i == 10);
}
{
tsu ta(mytype(10));
DLIB_TEST(ta.cast_to<mytype>().i == 10);
tsu tb(ta); //copy constructor
DLIB_TEST(ta.cast_to<mytype>().i == 10);
DLIB_TEST(tb.cast_to<mytype>().i == 10);
}
{
tsu ta(mytype(10));
DLIB_TEST(ta.cast_to<mytype>().i == 10);
tsu tb;
tb = ta; //copy assign
DLIB_TEST(ta.cast_to<mytype>().i == 10);
DLIB_TEST(tb.cast_to<mytype>().i == 10);
}
{
tsu ta(mytype(10));
DLIB_TEST(ta.cast_to<mytype>().i == 10);
tsu tb(std::move(ta)); //move constructor
DLIB_TEST(ta.is_empty());
DLIB_TEST(tb.cast_to<mytype>().i == 10);
}
{
tsu ta(mytype(10));
DLIB_TEST(ta.cast_to<mytype>().i == 10);
tsu tb;
tb = std::move(ta); //move assign
DLIB_TEST(ta.is_empty());
DLIB_TEST(tb.cast_to<mytype>().i == 10);
}
}
{
//testing emplace(), copy semantics, move semantics, swap, overloaded, and new visitor
type_safe_union<int, float, std::string> a, b;
a.emplace<std::string>("hello world");
DLIB_TEST(a.contains<std::string>());
b = a; //copy
DLIB_TEST(a.contains<std::string>());
DLIB_TEST(b.contains<std::string>());
DLIB_TEST(a.cast_to<std::string>() == "hello world");
DLIB_TEST(b.cast_to<std::string>() == "hello world");
a = 1;
DLIB_TEST(a.contains<int>());
DLIB_TEST(a.cast_to<int>() == 1);
b = std::move(a);
DLIB_TEST(b.contains<int>());
DLIB_TEST(b.cast_to<int>() == 1);
DLIB_TEST(a.is_empty());
DLIB_TEST(a.get_current_type_id() == 0);
swap(a, b);
DLIB_TEST(a.contains<int>());
DLIB_TEST(a.cast_to<int>() == 1);
DLIB_TEST(b.is_empty());
DLIB_TEST(b.get_current_type_id() == 0);
//visit can return non-void types
auto ret = a.visit(overloaded(
[](int) {
return std::string("int");
},
[](float) {
return std::string("float");
},
[](const std::string&) {
return std::string("std::string");
}
));
static_assert(std::is_same<std::string, decltype(ret)>::value, "bad return type");
DLIB_TEST(ret == "int");
//apply_to_contents can only return void
a = std::string("hello there!");
std::string str;
a.apply_to_contents(overloaded(
[&str](int) {
str = std::string("int");
},
[&str](float) {
str = std::string("float");
},
[&str](const std::string& item) {
str = item;
}
));
DLIB_TEST(str == "hello there!");
}
{
//nested unions
using tsu_a = type_safe_union<int,float,std::string>;
using tsu_b = type_safe_union<int,float,std::string,tsu_a>;
tsu_b object(dlib::in_place_tag<tsu_a>{}, std::string("hello from bottom node"));
DLIB_TEST(object.contains<tsu_a>());
DLIB_TEST(object.get<tsu_a>().get<std::string>() == "hello from bottom node");
auto ret = object.visit(overloaded(
[](int) {
return std::string("int");
},
[](float) {
return std::string("float");
},
[](std::string) {
return std::string("std::string");
},
[](const tsu_a& item) {
return item.visit(overloaded(
[](int) {
return std::string("nested int");
},
[](float) {
return std::string("nested float");
},
[](std::string str) {
return str;
}
));
}
));
static_assert(std::is_same<std::string, decltype(ret)>::value, "bad type");
DLIB_TEST(ret == "hello from bottom node");
}
{
//"private" visitor
using tsu = type_safe_union<int,float,std::string>;
class visitor_private
{
private:
std::string operator()(int)
{
return std::string("int");
}
std::string operator()(float)
{
return std::string("float");
}
std::string operator()(const std::string& str)
{
return str;
}
friend tsu;
};
visitor_private visitor;
tsu a = std::string("hello from private visitor");
auto ret = a.visit(visitor);
static_assert(std::is_same<std::string, decltype(ret)>::value, "bad type");
DLIB_TEST(ret == "hello from private visitor");
}
}
};
class type_safe_union_tester : public tester class type_safe_union_tester : public tester
{ {
......
...@@ -4,18 +4,14 @@ ...@@ -4,18 +4,14 @@
#define DLIB_TYPE_SAFE_UNIOn_h_ #define DLIB_TYPE_SAFE_UNIOn_h_
#include "type_safe_union_kernel_abstract.h" #include "type_safe_union_kernel_abstract.h"
#include "../algs.h"
#include "../noncopyable.h"
#include "../serialize.h"
#include <new> #include <new>
#include <iostream> #include <iostream>
#include <type_traits> #include <type_traits>
#include <functional>
#include "../serialize.h"
namespace dlib namespace dlib
{ {
// ----------------------------------------------------------------------------------------
class bad_type_safe_union_cast : public std::bad_cast class bad_type_safe_union_cast : public std::bad_cast
{ {
public: public:
...@@ -25,38 +21,92 @@ namespace dlib ...@@ -25,38 +21,92 @@ namespace dlib
} }
}; };
// ---------------------------------------------------------------------------------------- template<typename T>
struct in_place_tag {};
struct _void{};
inline void serialize( const _void&, std::ostream&){} namespace internal
inline void deserialize( _void&, std::istream&){} {
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------------------------- template <typename T, typename... Rest>
struct is_any : std::false_type {};
template <
typename T1, template <typename T, typename First>
typename T2 = _void, struct is_any<T,First> : std::is_same<T,First> {};
typename T3 = _void,
typename T4 = _void, template <typename T, typename First, typename... Rest>
typename T5 = _void, struct is_any<T,First,Rest...> : std::integral_constant<bool, std::is_same<T,First>::value || is_any<T,Rest...>::value> {};
typename T6 = _void,
typename T7 = _void, // ---------------------------------------------------------------------
typename T8 = _void, namespace detail
typename T9 = _void, {
typename T10 = _void, struct empty {};
typename T11 = _void, template<typename T>
typename T12 = _void, struct variant_type_placeholder {using type = T;};
typename T13 = _void,
typename T14 = _void, template <
typename T15 = _void, size_t nVariantTypes,
typename T16 = _void, size_t Counter,
typename T17 = _void, size_t I,
typename T18 = _void, typename... VariantTypes
typename T19 = _void, >
typename T20 = _void struct variant_get_type_impl {};
>
class type_safe_union : noncopyable template <
size_t nVariantTypes,
size_t Counter,
size_t I,
typename VariantTypeFirst,
typename... VariantTypeRest
>
struct variant_get_type_impl<nVariantTypes, Counter, I, VariantTypeFirst, VariantTypeRest...>
: std::conditional<(Counter < nVariantTypes),
typename std::conditional<(I == Counter),
variant_type_placeholder<VariantTypeFirst>,
variant_get_type_impl<nVariantTypes, Counter+1, I, VariantTypeRest...>
>::type,
empty
>::type {};
}
template <size_t I, typename... VariantTypes>
struct variant_get_type : detail::variant_get_type_impl<sizeof...(VariantTypes), 0, I, VariantTypes...> {};
// ---------------------------------------------------------------------
namespace detail
{
template <
size_t nVariantTypes,
size_t Counter,
typename T,
typename... VariantTypes
>
struct variant_type_id_impl : std::integral_constant<int,-1> {};
template <
size_t nVariantTypes,
size_t Counter,
typename T,
typename VariantTypeFirst,
typename... VariantTypesRest
>
struct variant_type_id_impl<nVariantTypes,Counter,T,VariantTypeFirst,VariantTypesRest...>
: std::conditional<(Counter < nVariantTypes),
typename std::conditional<std::is_same<T,VariantTypeFirst>::value,
std::integral_constant<int,Counter+1>,
variant_type_id_impl<nVariantTypes,Counter + 1, T, VariantTypesRest...>
>::type,
std::integral_constant<int,-1>
>::type {};
}
template <typename T, typename... VariantTypes>
struct variant_type_id : detail::variant_type_id_impl<sizeof...(VariantTypes), 0, T, VariantTypes...> {};
// ---------------------------------------------------------------------
}
template <typename... Types>
class type_safe_union
{ {
/*! /*!
CONVENTION CONVENTION
...@@ -65,46 +115,141 @@ namespace dlib ...@@ -65,46 +115,141 @@ namespace dlib
- mem == the aligned block of memory on the stack which is - mem == the aligned block of memory on the stack which is
where objects in the union are stored where objects in the union are stored
!*/ !*/
public:
template <typename T>
static constexpr int get_type_id ()
{
return internal::variant_type_id<T,Types...>::value;
}
private: private:
template<typename T>
struct is_valid : internal::is_any<T,Types...> {};
template <typename T, typename U> template<typename T>
void invoke_on ( using is_valid_check = typename std::enable_if<is_valid<T>::value, bool>::type;
T& obj,
U& item template <size_t I>
) const struct get_type : internal::variant_get_type<I,Types...> {};
template <size_t I>
using get_type_t = typename get_type<I>::type;
using T0 = get_type_t<0>;
template<typename F, typename T>
struct return_type
{
using type = decltype(std::declval<F>()(std::declval<T>()));
};
template<typename F, typename T>
using return_type_t = typename return_type<F,T>::type;
typename std::aligned_union<0, Types...>::type mem;
int type_identity = 0;
template<
size_t I,
typename F
>
auto visit_impl(
F&&
) -> typename std::enable_if<
(I == sizeof...(Types)) &&
std::is_same<void, return_type_t<F, T0&>>::value
>::type
{ {
obj(item);
} }
template <typename T> template<
void invoke_on ( size_t I,
T& , typename F
_void >
) const auto visit_impl(
F&&
) -> typename std::enable_if<
(I == sizeof...(Types)) &&
! std::is_same<void, return_type_t<F, T0&>>::value,
return_type_t<F, T0&>
>::type
{
return return_type_t<F, T0&>{};
}
template<
size_t I,
typename F
>
auto visit_impl(
F&& f
) -> typename std::enable_if<
(I < sizeof...(Types)),
return_type_t<F, T0&>
>::type
{ {
if (type_identity == (I+1))
return std::forward<F>(f)(unchecked_get<get_type_t<I>>());
else
return visit_impl<I+1>(std::forward<F>(f));
} }
// member data template<
typename std::aligned_union<0, T1,T2,T3,T4,T5, size_t I,
T6,T7,T8,T9,T10, typename F
T11,T12,T13,T14,T15, >
T16,T17,T18,T19,T20>::type mem; auto visit_impl(
int type_identity; F&&
) const -> typename std::enable_if<
(I == sizeof...(Types)) &&
std::is_same<void, return_type_t<F, const T0&>>::value
>::type
{
}
// -------------------------------------------- template<
size_t I,
typename F
>
auto visit_impl(
F&&
) const -> typename std::enable_if<
(I == sizeof...(Types)) &&
! std::is_same<void, return_type_t<F, const T0&>>::value,
return_type_t<F, const T0&>
>::type
{
return return_type_t<F, const T0&>{};
}
template<
size_t I,
typename F
>
auto visit_impl(
F&& f
) const -> typename std::enable_if<
(I < sizeof...(Types)),
return_type_t<F, const T0&>
>::type
{
if (type_identity == (I+1))
return std::forward<F>(f)(unchecked_get<get_type_t<I>>());
else
return visit_impl<I+1>(std::forward<F>(f));
}
template <typename T> template <typename T>
void validate_type() const const T& unchecked_get() const
{ {
// ERROR: You are trying to get a type of object that isn't return *reinterpret_cast<const T*>(&mem);
// representable by this type_safe_union.
static_assert(is_any<T,T1,T2,T3,T4,T5,
T6,T7,T8,T9,T10,
T11,T12,T13,T14,T15,
T16,T17,T18,T19,T20>::value, "Type T isn't one of the ones given to this object's template arguments.");
} }
template <typename T>
T& unchecked_get()
{
return *reinterpret_cast<T*>(&mem);
}
struct destruct_helper struct destruct_helper
{ {
...@@ -115,124 +260,158 @@ namespace dlib ...@@ -115,124 +260,158 @@ namespace dlib
} }
}; };
void destruct ( void destruct ()
)
/*!
ensures
- #is_empty() == true
!*/
{ {
// destruct whatever is in this object visit(destruct_helper{});
apply_to_contents(destruct_helper());
// mark this object as being empty
type_identity = 0; type_identity = 0;
} }
template <typename T> template <typename T, typename... Args>
void construct ( void construct (
) Args&&... args
{ )
if (type_identity != get_type_id<T>()) {
destruct();
new(&mem) T(std::forward<Args>(args)...);
type_identity = get_type_id<T>();
}
struct helper_forward
{
helper_forward(type_safe_union& me) : _me(me) {}
template<typename T>
void operator()(T&& x)
{ {
destruct(); using U = typename std::decay<T>::type;
new(&mem) T();
type_identity = get_type_id<T>(); if (_me.type_identity != get_type_id<U>())
{
_me.construct<U>(std::forward<T>(x));
}
else
{
_me.template unchecked_get<U>() = std::forward<T>(x);
}
} }
}
template <typename T> type_safe_union& _me;
void construct ( };
T&& item
) struct helper_move
{ {
using U = typename std::decay<T>::type; helper_move(type_safe_union& me) : _me(me) {}
if (type_identity != get_type_id<U>())
template<typename T>
void operator()(T& x)
{ {
destruct(); if (_me.type_identity != get_type_id<T>())
new(&mem) U(std::forward<T>(item)); {
type_identity = get_type_id<U>(); _me.construct<T>(std::move(x));
}
else
{
_me.template unchecked_get<T>() = std::move(x);
}
} }
}
template <typename T> type_safe_union& _me;
T& unchecked_get( };
)
/*!
requires
- contains<T>() == true
ensures
- returns a non-const reference to the T object
!*/
{
return *reinterpret_cast<T*>(&mem);
}
template <typename T> struct swap_helper
const T& unchecked_get( {
) const swap_helper(type_safe_union& me) : _me(me) {}
/*!
requires template<typename T>
- contains<T>() == true void operator()(T& x)
ensures {
- returns a const reference to the T object using std::swap;
!*/ swap(_me.unchecked_get<T>(), x); //really you want to use cast_to<T>(), BUT, swap is supposed to be nothrow
{ }
return *reinterpret_cast<const T*>(&mem);
type_safe_union& _me;
};
public:
type_safe_union() = default;
type_safe_union (
const type_safe_union& item
) : type_safe_union()
{
item.visit(helper_forward{*this});
} }
template <typename T> type_safe_union& operator=(
void operator() (T& item) const type_safe_union& item
/* )
This function is used by the swap function of this class. See that
function to see how this works.
*/
{ {
exchange(get<T>(), item); if (item.is_empty())
destruct();
else
item.visit(helper_forward{*this});
return *this;
} }
public: type_safe_union (
type_safe_union&& item
) : type_safe_union()
{
item.visit(helper_move{*this});
item.destruct();
}
typedef T1 type1; type_safe_union& operator= (
typedef T2 type2; type_safe_union&& item
typedef T3 type3; )
typedef T4 type4; {
typedef T5 type5; if (item.is_empty())
typedef T6 type6; {
typedef T7 type7; destruct();
typedef T8 type8; }
typedef T9 type9; else
typedef T10 type10; {
typedef T11 type11; item.visit(helper_move{*this});
typedef T12 type12; item.destruct();
typedef T13 type13; }
typedef T14 type14; return *this;
typedef T15 type15;
typedef T16 type16;
typedef T17 type17;
typedef T18 type18;
typedef T19 type19;
typedef T20 type20;
type_safe_union() : type_identity(0)
{
} }
template <typename T> template <
typename T,
is_valid_check<typename std::decay<T>::type> = true
>
type_safe_union ( type_safe_union (
T&& item T&& item
) : type_identity(0) ) : type_safe_union()
{
helper_forward{*this}(std::forward<T>(item));
}
template <
typename T,
is_valid_check<typename std::decay<T>::type> = true
>
type_safe_union& operator= (
T&& item
)
{ {
validate_type<typename std::decay<T>::type>(); helper_forward{*this}(std::forward<T>(item));
construct(std::forward<T>(item)); return *this;
} }
template <
typename T,
typename... Args,
is_valid_check<T> = true
>
type_safe_union ( type_safe_union (
type_safe_union&& item in_place_tag<T>,
) : type_safe_union() Args&&... args
)
{ {
swap(item); construct<T,Args...>(std::forward<Args>(args)...);
} }
~type_safe_union() ~type_safe_union()
...@@ -240,373 +419,205 @@ namespace dlib ...@@ -240,373 +419,205 @@ namespace dlib
destruct(); destruct();
} }
template <typename T> void clear()
static int get_type_id ( {
) destruct();
{
if (std::is_same<T,T1>::value) return 1;
if (std::is_same<T,T2>::value) return 2;
if (std::is_same<T,T3>::value) return 3;
if (std::is_same<T,T4>::value) return 4;
if (std::is_same<T,T5>::value) return 5;
if (std::is_same<T,T6>::value) return 6;
if (std::is_same<T,T7>::value) return 7;
if (std::is_same<T,T8>::value) return 8;
if (std::is_same<T,T9>::value) return 9;
if (std::is_same<T,T10>::value) return 10;
if (std::is_same<T,T11>::value) return 11;
if (std::is_same<T,T12>::value) return 12;
if (std::is_same<T,T13>::value) return 13;
if (std::is_same<T,T14>::value) return 14;
if (std::is_same<T,T15>::value) return 15;
if (std::is_same<T,T16>::value) return 16;
if (std::is_same<T,T17>::value) return 17;
if (std::is_same<T,T18>::value) return 18;
if (std::is_same<T,T19>::value) return 19;
if (std::is_same<T,T20>::value) return 20;
// return a number that doesn't match any of the
// valid states of type_identity
return -1;
} }
template <typename T> template <
bool contains ( typename T,
) const typename... Args,
is_valid_check<T> = true
>
void emplace(
Args&&... args
)
{ {
return type_identity == get_type_id<T>(); construct<T,Args...>(std::forward<Args>(args)...);
} }
bool is_empty ( template <typename F>
) const auto visit(
F&& f
) -> decltype(visit_impl<0>(std::forward<F>(f)))
{ {
return type_identity == 0; return visit_impl<0>(std::forward<F>(f));
} }
template <typename F>
auto visit(
F&& f
) const -> decltype(visit_impl<0>(std::forward<F>(f)))
{
return visit_impl<0>(std::forward<F>(f));
}
public: template <typename F>
void apply_to_contents(
template < F&& f
typename t1, typename t2, typename t3, typename t4, typename t5, )
typename t6, typename t7, typename t8, typename t9, typename t10,
typename t11, typename t12, typename t13, typename t14, typename t15,
typename t16, typename t17, typename t18, typename t19, typename t20
>
friend void serialize (
const type_safe_union<t1,t2,t3,t4,t5,t6,t7,t8,t9,t10, t11,t12,t13,t14,t15,t16,t17,t18,t19,t20>& item,
std::ostream& out
);
template <
typename T
>
void apply_to_contents (
T& obj
)
{ {
switch (type_identity) visit(std::forward<F>(f));
{
// do nothing because we are empty
case 0: break;
case 1: invoke_on(obj,unchecked_get<T1>()); break;
case 2: invoke_on(obj,unchecked_get<T2>()); break;
case 3: invoke_on(obj,unchecked_get<T3>()); break;
case 4: invoke_on(obj,unchecked_get<T4>()); break;
case 5: invoke_on(obj,unchecked_get<T5>()); break;
case 6: invoke_on(obj,unchecked_get<T6>()); break;
case 7: invoke_on(obj,unchecked_get<T7>()); break;
case 8: invoke_on(obj,unchecked_get<T8>()); break;
case 9: invoke_on(obj,unchecked_get<T9>()); break;
case 10: invoke_on(obj,unchecked_get<T10>()); break;
case 11: invoke_on(obj,unchecked_get<T11>()); break;
case 12: invoke_on(obj,unchecked_get<T12>()); break;
case 13: invoke_on(obj,unchecked_get<T13>()); break;
case 14: invoke_on(obj,unchecked_get<T14>()); break;
case 15: invoke_on(obj,unchecked_get<T15>()); break;
case 16: invoke_on(obj,unchecked_get<T16>()); break;
case 17: invoke_on(obj,unchecked_get<T17>()); break;
case 18: invoke_on(obj,unchecked_get<T18>()); break;
case 19: invoke_on(obj,unchecked_get<T19>()); break;
case 20: invoke_on(obj,unchecked_get<T20>()); break;
}
} }
template < template <typename F>
typename T void apply_to_contents(
> F&& f
void apply_to_contents ( ) const
const T& obj
)
{ {
switch (type_identity) visit(std::forward<F>(f));
{
// do nothing because we are empty
case 0: break;
case 1: invoke_on(obj,unchecked_get<T1>()); break;
case 2: invoke_on(obj,unchecked_get<T2>()); break;
case 3: invoke_on(obj,unchecked_get<T3>()); break;
case 4: invoke_on(obj,unchecked_get<T4>()); break;
case 5: invoke_on(obj,unchecked_get<T5>()); break;
case 6: invoke_on(obj,unchecked_get<T6>()); break;
case 7: invoke_on(obj,unchecked_get<T7>()); break;
case 8: invoke_on(obj,unchecked_get<T8>()); break;
case 9: invoke_on(obj,unchecked_get<T9>()); break;
case 10: invoke_on(obj,unchecked_get<T10>()); break;
case 11: invoke_on(obj,unchecked_get<T11>()); break;
case 12: invoke_on(obj,unchecked_get<T12>()); break;
case 13: invoke_on(obj,unchecked_get<T13>()); break;
case 14: invoke_on(obj,unchecked_get<T14>()); break;
case 15: invoke_on(obj,unchecked_get<T15>()); break;
case 16: invoke_on(obj,unchecked_get<T16>()); break;
case 17: invoke_on(obj,unchecked_get<T17>()); break;
case 18: invoke_on(obj,unchecked_get<T18>()); break;
case 19: invoke_on(obj,unchecked_get<T19>()); break;
case 20: invoke_on(obj,unchecked_get<T20>()); break;
}
} }
template < template <typename T>
typename T bool contains (
>
void apply_to_contents (
T& obj
) const ) const
{ {
switch (type_identity) return type_identity == get_type_id<T>();
{
// do nothing because we are empty
case 0: break;
case 1: invoke_on(obj,unchecked_get<T1>()); break;
case 2: invoke_on(obj,unchecked_get<T2>()); break;
case 3: invoke_on(obj,unchecked_get<T3>()); break;
case 4: invoke_on(obj,unchecked_get<T4>()); break;
case 5: invoke_on(obj,unchecked_get<T5>()); break;
case 6: invoke_on(obj,unchecked_get<T6>()); break;
case 7: invoke_on(obj,unchecked_get<T7>()); break;
case 8: invoke_on(obj,unchecked_get<T8>()); break;
case 9: invoke_on(obj,unchecked_get<T9>()); break;
case 10: invoke_on(obj,unchecked_get<T10>()); break;
case 11: invoke_on(obj,unchecked_get<T11>()); break;
case 12: invoke_on(obj,unchecked_get<T12>()); break;
case 13: invoke_on(obj,unchecked_get<T13>()); break;
case 14: invoke_on(obj,unchecked_get<T14>()); break;
case 15: invoke_on(obj,unchecked_get<T15>()); break;
case 16: invoke_on(obj,unchecked_get<T16>()); break;
case 17: invoke_on(obj,unchecked_get<T17>()); break;
case 18: invoke_on(obj,unchecked_get<T18>()); break;
case 19: invoke_on(obj,unchecked_get<T19>()); break;
case 20: invoke_on(obj,unchecked_get<T20>()); break;
}
} }
template < bool is_empty (
typename T
>
void apply_to_contents (
const T& obj
) const ) const
{ {
switch (type_identity) return type_identity == 0;
{ }
// do nothing because we are empty
case 0: break; int get_current_type_id() const
{
case 1: invoke_on(obj,unchecked_get<T1>()); break; return type_identity;
case 2: invoke_on(obj,unchecked_get<T2>()); break;
case 3: invoke_on(obj,unchecked_get<T3>()); break;
case 4: invoke_on(obj,unchecked_get<T4>()); break;
case 5: invoke_on(obj,unchecked_get<T5>()); break;
case 6: invoke_on(obj,unchecked_get<T6>()); break;
case 7: invoke_on(obj,unchecked_get<T7>()); break;
case 8: invoke_on(obj,unchecked_get<T8>()); break;
case 9: invoke_on(obj,unchecked_get<T9>()); break;
case 10: invoke_on(obj,unchecked_get<T10>()); break;
case 11: invoke_on(obj,unchecked_get<T11>()); break;
case 12: invoke_on(obj,unchecked_get<T12>()); break;
case 13: invoke_on(obj,unchecked_get<T13>()); break;
case 14: invoke_on(obj,unchecked_get<T14>()); break;
case 15: invoke_on(obj,unchecked_get<T15>()); break;
case 16: invoke_on(obj,unchecked_get<T16>()); break;
case 17: invoke_on(obj,unchecked_get<T17>()); break;
case 18: invoke_on(obj,unchecked_get<T18>()); break;
case 19: invoke_on(obj,unchecked_get<T19>()); break;
case 20: invoke_on(obj,unchecked_get<T20>()); break;
}
} }
void swap ( void swap (
type_safe_union& item type_safe_union& item
) )
{ {
// if both *this and item contain the same type of thing
if (type_identity == item.type_identity) if (type_identity == item.type_identity)
{ {
// swap the things in this and item. item.visit(swap_helper{*this});
item.apply_to_contents(*this);
} }
else if (type_identity == 0) else if (is_empty())
{ {
// *this doesn't contain anything. So swap this and item and item.visit(helper_move{*this});
// then destruct item.
item.apply_to_contents(*this);
item.destruct(); item.destruct();
} }
else if (item.type_identity == 0) else if (item.is_empty())
{ {
// *this doesn't contain anything. So swap this and item and visit(helper_move{item});
// then destruct this.
apply_to_contents(item);
destruct(); destruct();
} }
else else
{ {
type_safe_union temp; type_safe_union tmp;
// swap *this into temp swap(tmp); // this -> tmp
apply_to_contents(temp); swap(item); // item -> this
// swap item into *this tmp.swap(item); // tmp (this) -> item
item.apply_to_contents(*this);
// swap temp into item
temp.apply_to_contents(item);
} }
} }
template <typename T> template <
typename T,
is_valid_check<T> = true
>
T& get( T& get(
) )
{ {
validate_type<T>(); if (type_identity != get_type_id<T>())
construct<T>(); construct<T>();
return *reinterpret_cast<T*>(&mem); return unchecked_get<T>();
} }
template <typename T> template <
typename T,
is_valid_check<T> = true
>
const T& cast_to ( const T& cast_to (
) const ) const
{ {
validate_type<T>();
if (contains<T>()) if (contains<T>())
return *reinterpret_cast<const T*>(&mem); return unchecked_get<T>();
else else
throw bad_type_safe_union_cast(); throw bad_type_safe_union_cast();
} }
template <typename T> template <
typename T,
is_valid_check<T> = true
>
T& cast_to ( T& cast_to (
) )
{ {
validate_type<T>();
if (contains<T>()) if (contains<T>())
return *reinterpret_cast<T*>(&mem); return unchecked_get<T>();
else else
throw bad_type_safe_union_cast(); throw bad_type_safe_union_cast();
} }
template <typename T>
type_safe_union& operator= (T&& item) { get<typename std::decay<T>::type>() = std::forward<T>(item); return *this; }
type_safe_union& operator= (type_safe_union&& item) { swap(item); return *this; }
}; };
// ---------------------------------------------------------------------------------------- template <typename ...Types>
template <
typename T1, typename T2, typename T3, typename T4, typename T5,
typename T6, typename T7, typename T8, typename T9, typename T10,
typename T11, typename T12, typename T13, typename T14, typename T15,
typename T16, typename T17, typename T18, typename T19, typename T20
>
inline void swap ( inline void swap (
type_safe_union<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10, T11,T12,T13,T14,T15,T16,T17,T18,T19,T20>& a, type_safe_union<Types...>& a,
type_safe_union<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10, T11,T12,T13,T14,T15,T16,T17,T18,T19,T20>& b type_safe_union<Types...>& b
) { a.swap(b); } ) { a.swap(b); }
// ----------------------------------------------------------------------------------------
template <
typename from,
typename T1, typename T2, typename T3, typename T4, typename T5,
typename T6, typename T7, typename T8, typename T9, typename T10,
typename T11, typename T12, typename T13, typename T14, typename T15,
typename T16, typename T17, typename T18, typename T19, typename T20
>
struct is_convertible<from,
type_safe_union<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10, T11,T12,T13,T14,T15,T16,T17,T18,T19,T20> >
{
const static bool value = is_convertible<from,T1>::value ||
is_convertible<from,T2>::value ||
is_convertible<from,T3>::value ||
is_convertible<from,T4>::value ||
is_convertible<from,T5>::value ||
is_convertible<from,T6>::value ||
is_convertible<from,T7>::value ||
is_convertible<from,T8>::value ||
is_convertible<from,T9>::value ||
is_convertible<from,T10>::value ||
is_convertible<from,T11>::value ||
is_convertible<from,T12>::value ||
is_convertible<from,T13>::value ||
is_convertible<from,T14>::value ||
is_convertible<from,T15>::value ||
is_convertible<from,T16>::value ||
is_convertible<from,T17>::value ||
is_convertible<from,T18>::value ||
is_convertible<from,T19>::value ||
is_convertible<from,T20>::value;
};
// ---------------------------------------------------------------------------------------- namespace detail
namespace impl_tsu
{ {
struct serialize_helper struct serialize_helper
{ {
/* serialize_helper(std::ostream& out_) : out(out_) {}
This is a function object to help us serialize type_safe_unions
*/
std::ostream& out;
serialize_helper(std::ostream& out_): out(out_) {}
template <typename T> template <typename T>
void operator() (const T& item) const { serialize(item, out); } void operator() (const T& item) const
}; {
} serialize(item, out);
}
template < std::ostream& out;
typename T1, typename T2, typename T3, typename T4, typename T5, };
typename T6, typename T7, typename T8, typename T9, typename T10,
typename T11, typename T12, typename T13, typename T14, typename T15, template<
typename T16, typename T17, typename T18, typename T19, typename T20 size_t I,
typename... Types
> >
void serialize ( inline typename std::enable_if<(I == sizeof...(Types))>::type deserialize_helper(
const type_safe_union<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10, T11,T12,T13,T14,T15,T16,T17,T18,T19,T20>& item, std::istream&,
int,
type_safe_union<Types...>&
)
{
}
template<
size_t I,
typename... Types
>
inline typename std::enable_if<(I < sizeof...(Types))>::type deserialize_helper(
std::istream& in,
int index,
type_safe_union<Types...>& x
)
{
using T = typename internal::variant_get_type<I, Types...>::type;
if (index == (I+1))
{
deserialize(x.template get<T>(), in);
}
else
{
deserialize_helper<I+1>(in, index, x);
}
}
}
template<typename... Types>
inline void serialize (
const type_safe_union<Types...>& item,
std::ostream& out std::ostream& out
) )
{ {
try try
{ {
// save the type_identity serialize(item.get_current_type_id(), out);
serialize(item.type_identity, out); item.visit(detail::serialize_helper(out));
item.apply_to_contents(dlib::impl_tsu::serialize_helper(out));
} }
catch (serialization_error& e) catch (serialization_error& e)
{ {
...@@ -614,66 +625,73 @@ namespace dlib ...@@ -614,66 +625,73 @@ namespace dlib
} }
} }
// ---------------------------------------------------------------------------------------- template<typename... Types>
inline void deserialize (
template < type_safe_union<Types...>& item,
typename T1, typename T2, typename T3, typename T4, typename T5,
typename T6, typename T7, typename T8, typename T9, typename T10,
typename T11, typename T12, typename T13, typename T14, typename T15,
typename T16, typename T17, typename T18, typename T19, typename T20
>
void deserialize (
type_safe_union<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10, T11,T12,T13,T14,T15,T16,T17,T18,T19,T20>& item,
std::istream& in std::istream& in
) )
{ {
try try
{ {
typedef type_safe_union<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10, T11,T12,T13,T14,T15,T16,T17,T18,T19,T20> tsu_type; int index = -1;
deserialize(index, in);
int type_identity; if (index == 0)
deserialize(type_identity, in); item.clear();
switch (type_identity) else if (index > 0 && index <= (int)sizeof...(Types))
{ detail::deserialize_helper<0>(in, index, item);
// swap an empty type_safe_union into item since it should be in the empty state else
case 0: tsu_type().swap(item); break; throw serialization_error("bad index value. Should be in range [0,sizeof...(Types))");
case 1: deserialize(item.template get<T1>(), in); break;
case 2: deserialize(item.template get<T2>(), in); break;
case 3: deserialize(item.template get<T3>(), in); break;
case 4: deserialize(item.template get<T4>(), in); break;
case 5: deserialize(item.template get<T5>(), in); break;
case 6: deserialize(item.template get<T6>(), in); break;
case 7: deserialize(item.template get<T7>(), in); break;
case 8: deserialize(item.template get<T8>(), in); break;
case 9: deserialize(item.template get<T9>(), in); break;
case 10: deserialize(item.template get<T10>(), in); break;
case 11: deserialize(item.template get<T11>(), in); break;
case 12: deserialize(item.template get<T12>(), in); break;
case 13: deserialize(item.template get<T13>(), in); break;
case 14: deserialize(item.template get<T14>(), in); break;
case 15: deserialize(item.template get<T15>(), in); break;
case 16: deserialize(item.template get<T16>(), in); break;
case 17: deserialize(item.template get<T17>(), in); break;
case 18: deserialize(item.template get<T18>(), in); break;
case 19: deserialize(item.template get<T19>(), in); break;
case 20: deserialize(item.template get<T20>(), in); break;
default: throw serialization_error("Corrupt data detected while deserializing type_safe_union");
}
} }
catch (serialization_error& e) catch(serialization_error& e)
{ {
throw serialization_error(e.info + "\n while deserializing an object of type type_safe_union"); throw serialization_error(e.info + "\n while deserializing an object of type type_safe_union");
} }
} }
// ---------------------------------------------------------------------------------------- #if __cplusplus >= 201703L
} template<typename ...Base>
struct overloaded_helper : Base...
{
template<typename... T>
overloaded_helper(T&& ... t) : Base{std::forward<T>(t)}... {}
using Base::operator()...;
};
#else
#endif // DLIB_TYPE_SAFE_UNIOn_h_ template<typename Base, typename ... BaseRest>
struct overloaded_helper: Base, overloaded_helper<BaseRest...>
{
template<typename T, typename ... TRest>
overloaded_helper(T&& t, TRest&& ...trest) :
Base{std::forward<T>(t)},
overloaded_helper<BaseRest...>{std::forward<TRest>(trest)...}
{}
using Base::operator();
using overloaded_helper<BaseRest...>::operator();
};
template<typename Base>
struct overloaded_helper<Base> : Base
{
template<typename T>
overloaded_helper<Base>(T&& t) : Base{std::forward<T>(t)}
{}
using Base::operator();
};
#endif //__cplusplus >= 201703L
template<typename... T>
overloaded_helper<typename std::decay<T>::type...> overloaded(T&&... t)
{
return overloaded_helper<typename std::decay<T>::type...>{std::forward<T>(t)...};
}
}
#endif // DLIB_TYPE_SAFE_UNIOn_h_
\ No newline at end of file
...@@ -3,9 +3,6 @@ ...@@ -3,9 +3,6 @@
#undef DLIB_TYPE_SAFE_UNION_KERNEl_ABSTRACT_ #undef DLIB_TYPE_SAFE_UNION_KERNEl_ABSTRACT_
#ifdef DLIB_TYPE_SAFE_UNION_KERNEl_ABSTRACT_ #ifdef DLIB_TYPE_SAFE_UNION_KERNEl_ABSTRACT_
#include "../algs.h"
#include "../noncopyable.h"
namespace dlib namespace dlib
{ {
...@@ -21,29 +18,31 @@ namespace dlib ...@@ -21,29 +18,31 @@ namespace dlib
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
template < template<typename T>
typename T1, struct in_place_tag {};
typename T2 = _void, // _void indicates parameter not used. /*!
typename T3 = _void, This is an empty class type used as a special disambiguation tag to be
typename T4 = _void, passed as the first argument to the constructor of type_safe_union that performs
typename T5 = _void, in-place construction of an object.
typename T6 = _void,
typename T7 = _void, Here is an example of its usage:
typename T8 = _void,
typename T9 = _void, struct A
typename T10 = _void, {
typename T11 = _void, int i = 0;
typename T12 = _void, int j = 0;
typename T13 = _void,
typename T14 = _void, A(int i_, int j_) : i(i_), j(j_) {}
typename T15 = _void, };
typename T16 = _void,
typename T17 = _void, using tsu = type_safe_union<A,std::string>;
typename T18 = _void,
typename T19 = _void, tsu a(in_place_tag<A>{}, 0, 1);
typename T20 = _void !*/
> // ----------------------------------------------------------------------------------------
class type_safe_union : noncopyable
template <typename... Types>
class type_safe_union
{ {
/*! /*!
REQUIREMENTS ON ALL TEMPLATE ARGUMENTS REQUIREMENTS ON ALL TEMPLATE ARGUMENTS
...@@ -71,55 +70,93 @@ namespace dlib ...@@ -71,55 +70,93 @@ namespace dlib
public: public:
typedef T1 type1;
typedef T2 type2;
typedef T3 type3;
typedef T4 type4;
typedef T5 type5;
typedef T6 type6;
typedef T7 type7;
typedef T8 type8;
typedef T9 type9;
typedef T10 type10;
typedef T11 type11;
typedef T12 type12;
typedef T13 type13;
typedef T14 type14;
typedef T15 type15;
typedef T16 type16;
typedef T17 type17;
typedef T18 type18;
typedef T19 type19;
typedef T20 type20;
type_safe_union( type_safe_union(
); ) = default;
/*! /*!
ensures ensures
- this object is properly initialized - this object is properly initialized
!*/ !*/
template <typename T>
type_safe_union ( type_safe_union (
T&& item const type_safe_union& item
)
/*!
ensures
- copy constructs *this from item
!*/
type_safe_union& operator=(
const type_safe_union& item
); );
/*! /*!
requires
- T must be one of the types given to this object's template arguments
ensures ensures
- this object is properly initialized - copy assigns *this from item
- #get<T>() == item
(i.e. this object will contain a copy of item, or we move item if it's an rvalue)
!*/ !*/
type_safe_union ( type_safe_union (
type_safe_union&& item type_safe_union&& item
); );
/*! /*!
ensures ensures
- move constructs *this from item. - move constructs *this from item.
!*/ !*/
type_safe_union& operator= (
type_safe_union&& item
);
/*!
ensures
- move assigns *this from item.
!*/
template <
typename T
>
type_safe_union (
T&& item
);
/*!
requires
- std::decay_t<T> must be one of the types given to this object's template arguments
ensures
- constructs *this from item using perfect forwarding (converting constructor)
- #get<T>() == std::forward<T>(item)
(i.e. this object will either contain a copy of item or will have moved item into *this
depending on the reference type)
!*/
template <
typename T
>
type_safe_union& operator= (
T&& item
);
/*!
requires
- std::decay_t<T> must be one of the types given to this object's template arguments
ensures
- assigns *this from item using perfect forwarding (converting assignment)
- #get<T> == std::forward<T>(item)
(i.e. this object will either contain a copy of item or will have moved item into *this
depending on the reference type)
!*/
template <
typename T,
typename... Args
>
type_safe_union (
in_place_tag<T>,
Args&&... args
);
/*!
requires
- T must be one of the types given to this object's template arguments
ensures
- constructs *this with type T using constructor-arguments args...
(i.e. efficiently performs *this = T(args...))
!*/
~type_safe_union( ~type_safe_union(
); );
/*! /*!
...@@ -127,14 +164,36 @@ namespace dlib ...@@ -127,14 +164,36 @@ namespace dlib
- all resources associated with this object have been freed - all resources associated with this object have been freed
!*/ !*/
void clear();
/*!
ensures
- all resources associated with this object have been freed
- #is_empty() == true
!*/
template <
typename T,
typename... Args
>
void emplace(
Args&&... args
);
/*!
requires
- T must be one of the types given to this object's template arguments
ensures
- re-assigns *this with type T using constructor-arguments args...
(i.e. efficiently performs *this = T(args...))
!*/
template <typename T> template <typename T>
static int get_type_id ( static constexpr int get_type_id (
); );
/*! /*!
ensures ensures
- if (T is the same type as one of the template arguments) then - if (T is the same type as one of the template arguments) then
- returns a number indicating which template argument it is. - returns a number indicating which template argument it is. In particular,
(e.g. if T is the same type as T3 then this function returns 3) if it's the first template argument it returns 1, if the second then 2, and so on.
- else - else
- returns -1 - returns -1
!*/ !*/
...@@ -160,68 +219,64 @@ namespace dlib ...@@ -160,68 +219,64 @@ namespace dlib
- returns false - returns false
!*/ !*/
template <typename T> int get_current_type_id(
void apply_to_contents ( ) const;
T& obj
);
/*! /*!
requires
- obj is a function object capable of operating on all the types contained
in this type_safe_union. I.e. obj(this->get<U>()) must be a valid
expression for all the possible U types.
ensures ensures
- if (is_empty() == false) then - returns type_identity, i.e, the index of the currently held type.
- Let U denote the type of object currently contained in this type_safe_union For example if the current type is the first template argument it returns 1, if it's the second then 2, and so on.
- calls obj(this->get<U>()) If the current object is empty, i.e. is_empty() == true, then
- The object returned by this->get<U>() will be non-const - returns 0
!*/ !*/
template <typename T> template <typename F>
void apply_to_contents ( auto visit(
const T& obj F&& f
); );
/*! /*!
requires requires
- obj is a function object capable of operating on all the types contained - f is a callable object capable of operating on all the types contained
in this type_safe_union. I.e. obj(this->get<U>()) must be a valid in this type_safe_union. I.e. std::forward<F>(f)(this->get<U>()) must be a valid
expression for all the possible U types. expression for all the possible U types.
ensures ensures
- if (is_empty() == false) then - if (is_empty() == false) then
- Let U denote the type of object currently contained in this type_safe_union - Let U denote the type of object currently contained in this type_safe_union
- calls obj(this->get<U>()) - returns std::forward<F>(f)(this->get<U>())
- The object returned by this->get<U>() will be non-const - The object passed to f() (i.e. by this->get<U>()) will be non-const.
!*/ !*/
template <typename T> template <typename F>
void apply_to_contents ( auto visit(
T& obj F&& f
) const; ) const;
/*! /*!
requires requires
- obj is a function object capable of operating on all the types contained - f is a callable object capable of operating on all the types contained
in this type_safe_union. I.e. obj(this->get<U>()) must be a valid in this type_safe_union. I.e. std::forward<F>(f)(this->get<U>()) must be a valid
expression for all the possible U types. expression for all the possible U types.
ensures ensures
- if (is_empty() == false) then - if (is_empty() == false) then
- Let U denote the type of object currently contained in this type_safe_union - Let U denote the type of object currently contained in this type_safe_union
- calls obj(this->get<U>()) - returns std::forward<F>(f)(this->get<U>())
- The object returned by this->get<U>() will be const - The object passed to f() (i.e. by this->get<U>()) will be const.
!*/ !*/
template <typename T> template <typename F>
void apply_to_contents ( void apply_to_contents(
const T& obj F&& f
);
/*!
ensures:
equivalent to calling visit(std::forward<F>(f)) with void return type
!*/
template <typename F>
void apply_to_contents(
F&& f
) const; ) const;
/*! /*!
requires ensures:
- obj is a function object capable of operating on all the types contained equivalent to calling visit(std::forward<F>(f)) with void return type
in this type_safe_union. I.e. obj(this->get<U>()) must be a valid
expression for all the possible U types.
ensures
- if (is_empty() == false) then
- Let U denote the type of object currently contained in this type_safe_union
- calls obj(this->get<U>())
- The object returned by this->get<U>() will be const
!*/ !*/
template <typename T> template <typename T>
...@@ -268,28 +323,6 @@ namespace dlib ...@@ -268,28 +323,6 @@ namespace dlib
- throws bad_type_safe_union_cast - throws bad_type_safe_union_cast
!*/ !*/
template <typename T>
type_safe_union& operator= (
T&& item
);
/*!
requires
- T must be one of the types given to this object's template arguments
ensures
- #get<T>() == item
(i.e. this object will contain a copy of item, or we move item if it's an rvalue)
- returns *this
!*/
type_safe_union& operator= (
type_safe_union&& item
);
/*!
ensures
- Allows moving item into *this. In fact, this is done by this->swap(item).
- returns *this
!*/
void swap ( void swap (
type_safe_union& item type_safe_union& item
); );
...@@ -340,7 +373,51 @@ namespace dlib ...@@ -340,7 +373,51 @@ namespace dlib
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
template<typename... T>
overloaded_helper<typename std::decay<T>::type...> overloaded(T&&... t)
{
return overloaded_helper<typename std::decay<T>::type...>{std::forward<T>(t)...};
}
/*!
This is a helper function for passing many callable objects (usually lambdas)
to either apply_to_contents() or visit(), that combine to make a complete
visitor. A picture paints a thousand words:
using tsu = type_safe_union<int,float,std::string>;
tsu a = std::string("hello there");
std::string result;
a.apply_to_contents(overloaded(
[&result](int) {
result = std::string("int");
},
[&result](float) {
result = std::string("float");
},
[&result](const std::string& item) {
result = item;
}
));
assert(result == "hello there");
result = "";
result = a.visit(overloaded(
[](int) {
return std::string("int");
},
[](float) {
return std::string("float");
},
[](const std::string& item) {
return item;
}
));
assert(result == "hello there");
!*/
} }
#endif // DLIB_TYPE_SAFE_UNION_KERNEl_ABSTRACT_ #endif // DLIB_TYPE_SAFE_UNION_KERNEl_ABSTRACT_
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment