-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathGeneric.h
176 lines (156 loc) · 4.72 KB
/
Generic.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
/*
This file is part of the Util library.
Copyright (C) 2013 Benjamin Eikel <[email protected]>
This library is subject to the terms of the Mozilla Public License, v. 2.0.
You should have received a copy of the MPL along with this library; see the
file LICENSE. If not, you can obtain one at http://mozilla.org/MPL/2.0/.
*/
#ifndef UTIL_GENERIC_H
#define UTIL_GENERIC_H
#include <memory>
#include <type_traits>
#include <typeinfo>
#include <utility>
namespace Util {
//! @defgroup generic_attr Generic Attributes
/**
* @brief Container for a value with generic type
*
* A Generic object is able to store a single object of different types. This
* allows for the usage of Generic as variable with a type that does not have
* to be known at compile-time.
* @code
* Util::Generic value;
* value = 42.0;
* std::cout << value.contains<std::string>() << std::endl;
* value = std::string("fourty-two");
* std::cout << value.contains<std::string>() << std::endl;
* std::cout << value.ref<std::string>() << std::endl;
* value = false;
* @endcode
*
* @author Benjamin Eikel
* @date 2013-05-28
* @ingroup generic_attr
*/
class Generic {
private:
//! Abstract base class for all template instantiations in subclasses
struct AbstractStorage {
virtual ~AbstractStorage() {
}
virtual AbstractStorage * clone() const = 0;
};
template<typename type_t>
struct Storage : public AbstractStorage {
//! Data that is stored here
type_t data;
//! Construct the storage taking a convertible object as parameter.
template<typename other_type_t,
typename = typename std::enable_if<std::is_convertible<
other_type_t,
type_t
>::value
>::type>
explicit Storage(other_type_t && object) :
AbstractStorage(), data(std::forward<other_type_t>(object)) {
}
virtual ~Storage() {
}
Storage * clone() const override {
return new Storage(*this);
}
};
std::unique_ptr<AbstractStorage> content;
public:
//! Construct with invalid value
Generic() : content() {
}
//! Construct with a copy of the given object
template<typename type_t, typename = typename std::enable_if<!std::is_convertible<type_t, Generic>::value>::type>
explicit Generic(type_t && object) :
content(new Storage<typename std::decay<type_t>::type>( std::forward<type_t>(object) )) {
}
//! Copy construct from another generic object
Generic(const Generic & other) :
content(other.content ? other.content->clone() : nullptr) {
}
//! Move construct from another generic object
Generic(Generic &&) = default;
//! Destroy the generic object
~Generic() = default;
//! Copy from another object
template<typename type_t>
Generic & operator=(type_t && object) {
*this = Generic(object);
return *this;
}
//! Move construct from another generic object
Generic & operator=(Generic &&) = default;
//! Check if the generic object contains any data
bool valid() const {
return content.operator bool();
}
//! Check if the stored data is of the given type
template<typename type_t>
bool contains() const {
return get<type_t>() != nullptr;
}
/**
* Access the data of the generic object by pointer.
*
* @return Pointer to the stored data, or @c nullptr if no data is
* stored or the data is not of the requested type.
*/
template<typename type_t>
type_t * get() {
if(!content) {
return nullptr;
}
auto ptr = dynamic_cast<Storage<typename std::decay<type_t>::type> *>(content.get());
if(ptr == nullptr) {
return nullptr;
}
return &ptr->data;
}
/**
* Access the data of the generic object for reading by pointer.
*
* @return Pointer to the stored data, or @c nullptr if no data is
* stored or the data is not of the requested type.
*/
template<typename type_t>
const type_t * get() const {
// Save a second implementation by using const_cast here
return const_cast<Generic *>(this)->get<type_t>();
}
/**
* Access the data of the generic object by reference.
*
* @throw std::bad_cast if no data is stored or the data is not of the
* requested type
* @return Reference to the stored data
*/
template<typename type_t>
type_t & ref() {
auto ptr = get<type_t>();
if(ptr == nullptr) {
throw std::bad_cast();
}
return *ptr;
}
/**
* Access the data of the generic object for reading by reference.
*
* @throw std::bad_cast if no data is stored or the data is not of the
* requested type
* @return Reference to the stored data
*/
template<typename type_t>
const type_t & ref() const {
// Save a second implementation by using const_cast here
return const_cast<Generic *>(this)->ref<type_t>();
}
};
}
#endif /* UTIL_GENERIC_H */