Commit dc086aa7 authored by Davis King's avatar Davis King

Added the type_safe_union object.

--HG--
extra : convert_revision : svn%3Afdd8eb12-d10e-0410-9acb-85c331704f74/trunk%402806
parent 6dd317bd
...@@ -71,6 +71,7 @@ set (tests ...@@ -71,6 +71,7 @@ set (tests
timer.cpp timer.cpp
tokenizer.cpp tokenizer.cpp
tuple.cpp tuple.cpp
type_safe_union.cpp
) )
# create a variable called target_name and set it to the string "test" # create a variable called target_name and set it to the string "test"
......
...@@ -81,6 +81,7 @@ SRC += thread_pool.cpp ...@@ -81,6 +81,7 @@ SRC += thread_pool.cpp
SRC += timer.cpp SRC += timer.cpp
SRC += tokenizer.cpp SRC += tokenizer.cpp
SRC += tuple.cpp SRC += tuple.cpp
SRC += type_safe_union.cpp
#################################################### ####################################################
......
// Copyright (C) 2009 Davis E. King (davisking@users.sourceforge.net)
// License: Boost Software License See LICENSE.txt for the full license.
#include <sstream>
#include <string>
#include <cstdlib>
#include <ctime>
#include <dlib/type_safe_union.h>
#include "tester.h"
namespace
{
using namespace test;
using namespace dlib;
using namespace std;
logger dlog("test.type_safe_union");
class test
{
private:
enum kind
{
FLOAT, DOUBLE, CHAR, STRING, NONE
};
void operator() (float val)
{
DLIB_CASSERT(val == f_val,"");
last_kind = FLOAT;
}
void operator() (double val)
{
DLIB_CASSERT(val == d_val,"");
last_kind = DOUBLE;
}
void operator() (char val)
{
DLIB_CASSERT(val == c_val,"");
last_kind = CHAR;
}
void operator()(std::string& val)
{
DLIB_CASSERT(val == s_val,"");
last_kind = STRING;
}
// ------------------------------
friend class type_safe_union<float, double, char, std::string>;
typedef type_safe_union<float, double, char, std::string> tsu;
tsu a, b, c;
float f_val;
double d_val;
char c_val;
std::string s_val;
kind last_kind;
public:
void test_stuff()
{
DLIB_CASSERT(a.is_empty() == true,"");
DLIB_CASSERT(a.contains<char>() == false,"");
DLIB_CASSERT(a.contains<float>() == false,"");
DLIB_CASSERT(a.contains<double>() == false,"");
DLIB_CASSERT(a.contains<std::string>() == false,"");
DLIB_CASSERT(a.contains<long>() == false,"");
f_val = 4.345;
a.get<float>() = f_val;
DLIB_CASSERT(a.is_empty() == false,"");
DLIB_CASSERT(a.contains<char>() == false,"");
DLIB_CASSERT(a.contains<float>() == true,"");
DLIB_CASSERT(a.contains<double>() == false,"");
DLIB_CASSERT(a.contains<std::string>() == false,"");
DLIB_CASSERT(a.contains<long>() == false,"");
last_kind = NONE;
a.apply_to_contents(*this);
DLIB_CASSERT(last_kind == FLOAT,"");
// -----------
d_val = 4.345;
a.get<double>() = d_val;
last_kind = NONE;
a.apply_to_contents(*this);
DLIB_CASSERT(last_kind == DOUBLE,"");
// -----------
c_val = 'a';
a.get<char>() = c_val;
last_kind = NONE;
a.apply_to_contents(*this);
DLIB_CASSERT(last_kind == CHAR,"");
// -----------
s_val = "test string";
a.get<std::string>() = s_val;
last_kind = NONE;
a.apply_to_contents(*this);
DLIB_CASSERT(last_kind == STRING,"");
// -----------
DLIB_CASSERT(a.is_empty() == false,"");
DLIB_CASSERT(a.contains<char>() == false,"");
DLIB_CASSERT(a.contains<float>() == false,"");
DLIB_CASSERT(a.contains<double>() == false,"");
DLIB_CASSERT(a.contains<std::string>() == true,"");
DLIB_CASSERT(a.contains<long>() == false,"");
// -----------
a.swap(b);
DLIB_CASSERT(a.is_empty() == true,"");
DLIB_CASSERT(a.contains<char>() == false,"");
DLIB_CASSERT(a.contains<float>() == false,"");
DLIB_CASSERT(a.contains<double>() == false,"");
DLIB_CASSERT(a.contains<std::string>() == false,"");
DLIB_CASSERT(a.contains<long>() == false,"");
DLIB_CASSERT(b.is_empty() == false,"");
DLIB_CASSERT(b.contains<char>() == false,"");
DLIB_CASSERT(b.contains<float>() == false,"");
DLIB_CASSERT(b.contains<double>() == false,"");
DLIB_CASSERT(b.contains<std::string>() == true,"");
DLIB_CASSERT(b.contains<long>() == false,"");
last_kind = NONE;
b.apply_to_contents(*this);
DLIB_CASSERT(last_kind == STRING,"");
// -----------
b.swap(a);
DLIB_CASSERT(b.is_empty() == true,"");
DLIB_CASSERT(b.contains<char>() == false,"");
DLIB_CASSERT(b.contains<float>() == false,"");
DLIB_CASSERT(b.contains<double>() == false,"");
DLIB_CASSERT(b.contains<std::string>() == false,"");
DLIB_CASSERT(b.contains<long>() == false,"");
DLIB_CASSERT(a.is_empty() == false,"");
DLIB_CASSERT(a.contains<char>() == false,"");
DLIB_CASSERT(a.contains<float>() == false,"");
DLIB_CASSERT(a.contains<double>() == false,"");
DLIB_CASSERT(a.contains<std::string>() == true,"");
DLIB_CASSERT(a.contains<long>() == false,"");
last_kind = NONE;
a.apply_to_contents(*this);
DLIB_CASSERT(last_kind == STRING,"");
last_kind = NONE;
b.apply_to_contents(*this);
DLIB_CASSERT(last_kind == NONE,"");
a.get<char>() = 'a';
b.get<char>() = 'b';
DLIB_CASSERT(a.is_empty() == false,"");
DLIB_CASSERT(a.contains<char>() == true,"");
DLIB_CASSERT(b.is_empty() == false,"");
DLIB_CASSERT(b.contains<char>() == true,"");
DLIB_CASSERT(a.contains<float>() == false,"");
DLIB_CASSERT(b.contains<float>() == false,"");
DLIB_CASSERT(a.get<char>() == 'a',"");
DLIB_CASSERT(b.get<char>() == 'b',"");
swap(a,b);
DLIB_CASSERT(a.is_empty() == false,"");
DLIB_CASSERT(a.contains<char>() == true,"");
DLIB_CASSERT(b.is_empty() == false,"");
DLIB_CASSERT(b.contains<char>() == true,"");
DLIB_CASSERT(a.contains<float>() == false,"");
DLIB_CASSERT(b.contains<float>() == false,"");
DLIB_CASSERT(a.get<char>() == 'b',"");
DLIB_CASSERT(b.get<char>() == 'a',"");
}
};
class type_safe_union_tester : public tester
{
public:
type_safe_union_tester (
) :
tester ("test_type_safe_union",
"Runs tests on the type_safe_union object")
{}
void perform_test (
)
{
for (int i = 0; i < 10; ++i)
{
test a;
a.test_stuff();
}
}
} a;
}
// Copyright (C) 2009 Davis E. King (davisking@users.sourceforge.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_TYPE_SAFE_UNIOn_TOP_
#define DLIB_TYPE_SAFE_UNIOn_TOP_
#include "type_safe_union/type_safe_union_kernel.h"
#endif // DLIB_TYPE_SAFE_UNIOn_TOP_
// Copyright (C) 2009 Davis E. King (davisking@users.sourceforge.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_TYPE_SAFE_UNIOn_h_
#define DLIB_TYPE_SAFE_UNIOn_h_
#include "type_safe_union_kernel_abstract.h"
#include "../algs.h"
#include "../noncopyable.h"
#include <new>
namespace dlib
{
// ----------------------------------------------------------------------------------------
template <
typename T1,
typename T2 = T1,
typename T3 = T1,
typename T4 = T1,
typename T5 = T1,
typename T6 = T1,
typename T7 = T1,
typename T8 = T1,
typename T9 = T1,
typename T10 = T1
>
class type_safe_union : noncopyable
{
template <typename A, typename B>
struct max
{
const static size_t value = tmax<sizeof(A), sizeof(B)>::value;
};
template <typename A, typename B, typename C>
struct max<max<A,B>, C>
{
const static size_t value = tmax<max<A,B>::value, sizeof(C)>::value;
};
const static size_t max_size = max<max<max<max<max<max<max<max<max<T1,T2>,T3>,T4>,T5>,T6>,T7>,T8>,T9>,T10>::value;
union mem_block
{
// All of this garbage is to make sure this union is properly aligned
// (a union is always aligned such that everything in it would be properly
// aligned. So the assumption here is that one of these objects has
// a large enough alignment requirement to satisfy any object this
// type_safe_union might contain).
void* void_ptr;
struct {
void (type_safe_union::*callback)();
type_safe_union* o;
} stuff;
long double more_stuff;
char data[max_size];
};
int type_identity;
mem_block mem;
struct destruct_helper
{
template <typename T>
void operator() (T& item) const
{
item.~T();
}
};
void destruct (
)
/*!
ensures
- #is_empty() == true
!*/
{
// destruct whatever is in this object
apply_to_contents(destruct_helper());
// mark this object as being empty
type_identity = 0;
}
template <typename T>
int get_id (
) const
{
if (is_same_type<T,T1>::value) return 1;
if (is_same_type<T,T2>::value) return 2;
if (is_same_type<T,T3>::value) return 3;
if (is_same_type<T,T4>::value) return 4;
if (is_same_type<T,T5>::value) return 5;
if (is_same_type<T,T6>::value) return 6;
if (is_same_type<T,T7>::value) return 7;
if (is_same_type<T,T8>::value) return 8;
if (is_same_type<T,T9>::value) return 9;
if (is_same_type<T,T10>::value) return 10;
// return a number that doesn't match any of the
// valid states of type_identity
return 10000;
}
template <typename T>
void construct (
)
{
if (type_identity != get_id<T>())
{
destruct();
new((void*)mem.data) T();
type_identity = get_id<T>();
}
}
template <typename T>
void operator() (T& item)
/*
This function is used by the swap function of this class. See that
function to see how this works.
*/
{
exchange(get<T>(), item);
}
public:
type_safe_union() : type_identity(0)
{
}
~type_safe_union()
{
destruct();
}
template <typename T>
bool contains (
) const
{
return type_identity == get_id<T>();
}
bool is_empty (
) const
{
return type_identity == 0;
}
template <
typename T
>
void apply_to_contents (
T& obj
)
{
switch (type_identity)
{
// do nothing because we are empty
case 0: break;
case 1: obj(get<T1>()); break;
case 2: obj(get<T2>()); break;
case 3: obj(get<T3>()); break;
case 4: obj(get<T4>()); break;
case 5: obj(get<T5>()); break;
case 6: obj(get<T6>()); break;
case 7: obj(get<T7>()); break;
case 8: obj(get<T8>()); break;
case 9: obj(get<T9>()); break;
case 10: obj(get<T10>()); break;
}
}
template <
typename T
>
void apply_to_contents (
const T& obj
)
{
switch (type_identity)
{
// do nothing because we are empty
case 0: break;
case 1: obj(get<T1>()); break;
case 2: obj(get<T2>()); break;
case 3: obj(get<T3>()); break;
case 4: obj(get<T4>()); break;
case 5: obj(get<T5>()); break;
case 6: obj(get<T6>()); break;
case 7: obj(get<T7>()); break;
case 8: obj(get<T8>()); break;
case 9: obj(get<T9>()); break;
case 10: obj(get<T10>()); break;
}
}
void swap (
type_safe_union& item
)
{
// if both *this and item contain the same type of thing
if (type_identity == item.type_identity)
{
// swap the things in this and item.
item.apply_to_contents(*this);
}
else if (type_identity == 0)
{
// *this doesn't contain anything. So swap this and item and
// then destruct item.
item.apply_to_contents(*this);
item.destruct();
}
else if (item.type_identity == 0)
{
// *this doesn't contain anything. So swap this and item and
// then destruct this.
apply_to_contents(item);
destruct();
}
else
{
type_safe_union temp;
// swap *this into temp
apply_to_contents(temp);
// swap item into *this
item.apply_to_contents(*this);
// swap temp into item
temp.apply_to_contents(item);
}
}
template <typename T> T& get() { construct<T>(); return *reinterpret_cast<T*>(mem.data); }
};
// ----------------------------------------------------------------------------------------
template <
typename T1, typename T2, typename T3, typename T4, typename T5,
typename T6, typename T7, typename T8, typename T9, typename T10
>
inline void swap (
type_safe_union<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10>& a,
type_safe_union<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10>& b
) { a.swap(b); }
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_TYPE_SAFE_UNIOn_h_
// Copyright (C) 2009 Davis E. King (davisking@users.sourceforge.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_
#include "../algs.h"
#include "../noncopyable.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
template <
typename T1,
typename T2 = T1,
typename T3 = T1,
typename T4 = T1,
typename T5 = T1,
typename T6 = T1,
typename T7 = T1,
typename T8 = T1,
typename T9 = T1,
typename T10 = T1
>
class type_safe_union : noncopyable
{
/*!
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(
);
/*!
ensures
- this object is properly initialized
!*/
~type_safe_union(
);
/*!
ensures
- all resources associated with this object have been freed
!*/
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
!*/
template <typename T>
void apply_to_contents (
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
- if (is_empty() == false) then
- Let U denote the type of object currently contained in this type_safe_union
- calls obj(this->get<U>())
!*/
template <typename T>
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
- if (is_empty() == false) then
- Let U denote the type of object currently contained in this type_safe_union
- calls obj(this->get<U>())
!*/
template <typename T>
T& get(
);
/*!
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.
!*/
void swap (
type_safe_union& item
);
/*!
ensures
- swaps *this and item
!*/
};
// ----------------------------------------------------------------------------------------
template <
typename T1, typename T2, typename T3, typename T4, typename T5,
typename T6, typename T7, typename T8, typename T9, typename T10
>
inline void swap (
type_safe_union<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10>& a,
type_safe_union<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10>& b
) { a.swap(b); }
/*!
provides a global swap function
!*/
// ----------------------------------------------------------------------------------------
}
#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