// Copyright (C) 2009 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#undef DLIB_TYPE_SAFE_UNION_KERNEl_ABSTRACT_
#ifdef DLIB_TYPE_SAFE_UNION_KERNEl_ABSTRACT_
namespace dlib
{
// ----------------------------------------------------------------------------------------
class bad_type_safe_union_cast : public std::bad_cast
{
/*!
This is the exception object thrown by type_safe_union::cast_to() if the
type_safe_union does not contain the type of object being requested.
!*/
};
// ----------------------------------------------------------------------------------------
template<typename T>
struct in_place_tag { using type = T; };
/*!
This is an empty class type used as a special disambiguation tag to be
passed as the first argument to the constructor of type_safe_union that performs
in-place construction of an object.
Here is an example of its usage:
struct A
{
int i = 0;
int j = 0;
A(int i_, int j_) : i(i_), j(j_) {}
};
using tsu = type_safe_union<A,std::string>;
tsu a(in_place_tag<A>{}, 0, 1); // a now contains an object of type A
It is also used with type_safe_union::for_each() to disambiguate types.
!*/
// ----------------------------------------------------------------------------------------
template<typename TSU>
struct type_safe_union_size
{
static constexpr size_t value = The number of types in the TSU.
};
/*!
requires
- TSU must be of type type_safe_union<Types...> with possible cv qualification
ensures
- value contains the number of types in TSU, i.e. sizeof...(Types...)
!*/
// ----------------------------------------------------------------------------------------
template <size_t I, typename TSU>
struct type_safe_union_alternative;
/*!
requires
- TSU is a type_safe_union
ensures
- type_safe_union_alternative<I, TSU>::type is the Ith type in the TSU.
- TSU::get_type_id<typename type_safe_union_alternative<I, TSU>::type>() == I
!*/
template<size_t I, typename TSU>
using type_safe_union_alternative_t = type_safe_union_alternative<I,TSU>::type;
/*!
ensures
- provides template alias for type_safe_union_alternative
!*/
// ----------------------------------------------------------------------------------------
template <typename... Types>
class type_safe_union
{
/*!
REQUIREMENTS ON ALL TEMPLATE ARGUMENTS
All template arguments must be default constructable and have
a global swap.
INITIAL VALUE
- is_empty() == true
- contains<U>() == false, for all possible values of U
WHAT THIS OBJECT REPRESENTS
This object is a type safe analogue of the classic C union object.
The type_safe_union, unlike a union, can contain non-POD types such
as std::string.
For example:
union my_union
{
int a;
std::string b; // Error, std::string isn't a POD
};
type_safe_union<int,std::string> my_type_safe_union; // No error
!*/
public:
type_safe_union(
) = default;
/*!
ensures
- #is_empty() == true
!*/
type_safe_union (
const type_safe_union& item
)
/*!
ensures
- copy constructs *this from item
!*/
type_safe_union& operator=(
const type_safe_union& item
);
/*!
ensures
- copy assigns *this from item
!*/
type_safe_union (
type_safe_union&& item
);
/*!
ensures
- 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(
);
/*!
ensures
- 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>
static constexpr int get_type_id (
);
/*!
ensures
- if (T is the same type as one of the template arguments) then
- returns a number indicating which template argument it is. In particular,
if it's the first template argument it returns 1, if the second then 2, and so on.
- else if (T is in_place_tag<U>) then
- equivalent to returning get_type_id<U>())
- else
- returns -1
!*/
template <typename T>
bool contains (
) const;
/*!
ensures
- if (this type_safe_union currently contains an object of type T) then
- returns true
- else
- returns false
!*/
bool is_empty (
) const;
/*!
ensures
- if (this type_safe_union currently contains any object at all) then
- returns true
- else
- returns false
!*/
int get_current_type_id(
) const;
/*!
ensures
- if (is_empty()) then
- returns 0
- else
- Returns the type id of the currently held type. This is the same as
get_type_id<WhateverTypeIsCurrentlyHeld>(). Therefore, if the current type is
the first template argument it returns 1, if it's the second then 2, and so on.
!*/
template <typename F>
void apply_to_contents(
F&& f
);
/*!
requires
- f is a callable object capable of operating on all the types contained
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.
ensures
- if (is_empty() == false) then
- Let U denote the type of object currently contained in this type_safe_union
- calls std::forward<F>(f)(this->get<U>())
- The object passed to f() (i.e. by this->get<U>()) will be non-const.
!*/
template <typename F>
void apply_to_contents(
F&& f
) const;
/*!
requires
- f is a callable object capable of operating on all the types contained
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.
ensures
- if (is_empty() == false) then
- Let U denote the type of object currently contained in this type_safe_union
- calls std::forward<F>(f)(this->get<U>())
- The object passed to f() (i.e. by this->get<U>()) will be const.
!*/
template <typename T>
T& get(
);
/*!
requires
- T must be one of the types given to this object's template arguments
ensures
- #is_empty() == false
- #contains<T>() == true
- if (contains<T>() == true)
- returns a non-const reference to the object contained in this type_safe_union.
- else
- Constructs an object of type T inside *this
- Any previous object stored in this type_safe_union is destructed and its
state is lost.
- returns a non-const reference to the newly created T object.
!*/
template <typename T>
T& get(
const in_place_tag<T>& tag
);
/*!
ensures
- equivalent to calling get<T>()
!*/
template <typename T>
const T& cast_to (
) const;
/*!
requires
- T must be one of the types given to this object's template arguments
ensures
- if (contains<T>() == true) then
- returns a const reference to the object contained in this type_safe_union.
- else
- throws bad_type_safe_union_cast
!*/
template <typename T>
T& cast_to (
);
/*!
requires
- T must be one of the types given to this object's template arguments
ensures
- if (contains<T>() == true) then
- returns a non-const reference to the object contained in this type_safe_union.
- else
- throws bad_type_safe_union_cast
!*/
void swap (
type_safe_union& item
);
/*!
ensures
- swaps *this and item
!*/
};
// ----------------------------------------------------------------------------------------
template<typename... Types>
inline void swap (
type_safe_union<Types...>& a,
type_safe_union<Types...>& b
) { a.swap(b); }
/*!
provides a global swap function
!*/
// ----------------------------------------------------------------------------------------
template<
typename TSU,
typename F
>
void for_each_type(
F&& f,
TSU&& tsu
);
/*!
requires
- tsu is an object of type type_safe_union<Types...> for some types Types...
- f is a callable object such that the following expression is valid for
all types U in Types...:
std::forward<F>(f)(in_place_tag<U>{}, std::forward<TSU>(tsu))
ensures
- This function iterates over all types U in Types... and calls:
std::forward<F>(f)(in_place_tag<U>{}, std::forward<TSU>(tsu))
!*/
// ----------------------------------------------------------------------------------------
template<
typename F,
typename TSU
>
auto visit(
F&& f,
TSU&& tsu
);
/*!
requires
- tsu is an object of type type_safe_union<Types...> for some types Types...
- f is a callable object capable of operating on all the types contained
in tsu. I.e. std::forward<F>(f)(this->get<U>()) must be a valid
expression for all the possible U types.
ensures
- if (tsu.is_empty() == false) then
- Let U denote the type of object currently contained in tsu.
- returns std::forward<F>(f)(this->get<U>())
- The object passed to f() (i.e. by this->get<U>()) will have the same reference
type as TSU.
!*/
// ----------------------------------------------------------------------------------------
template<typename... Types>
void serialize (
const type_safe_union<Types...>& item,
std::ostream& out
);
/*!
provides serialization support
Note that type_safe_union objects are serialized as follows:
- if (item.is_empty()) then
- perform: serialize(0, out)
- else
- perform: serialize(item.get_type_id<type_of_object_in_item>(), out);
serialize(item.get<type_of_object_in_item>(), out);
!*/
template<typename... Types>
void deserialize (
type_safe_union<Types...>& item,
std::istream& in
);
/*!
provides deserialization support
!*/
// ----------------------------------------------------------------------------------------
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(), visit() or for_each(), 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 = visit(overloaded(
[](int) {
return std::string("int");
},
[](float) {
return std::string("float");
},
[](const std::string& item) {
return item;
}
), a);
assert(result == "hello there");
std::vector<int> type_ids;
for_each_type(a, overloaded(
[&type_ids](in_place_tag<int>, tsu& me) {
type_ids.push_back(me.get_type_id<int>());
},
[&type_ids](in_place_tag<float>, tsu& me) {
type_ids.push_back(me.get_type_id<float>());
},
[&type_ids](in_place_tag<std::string>, tsu& me) {
type_ids.push_back(me.get_type_id<std::string>());
}
));
assert(type_ids == vector<int>({0,1,2}));
!*/
}
#endif // DLIB_TYPE_SAFE_UNION_KERNEl_ABSTRACT_