1
#ifndef CHILON_VARIANT_HPP
2
#define CHILON_VARIANT_HPP
3
4
#include <chilon/meta/at.hpp>
5
#include <chilon/meta/index_of.hpp>
6
#include <chilon/meta/max.hpp>
7
#include <chilon/meta/return.hpp>
8
#include <chilon/meta/void.hpp>
9
#include <chilon/meta/call_type.hpp>
10
#include <chilon/meta/contains.hpp>
11
#include <chilon/meta/require.hpp>
12
13
#include <chilon/singleton.hpp>
14
#include <chilon/hash.hpp>
15
16
#include <boost/ptr_container/ptr_array.hpp>
17
18
#include <utility>
19
#include <memory>
20
21
#include <assert.h>
22
23
namespace chilon {
24
25
/// Thrown when a variant is initalised from another variant which
26
/// current stores a type that the lhs variant cannot store.
27
struct invalid_variant_initalisation {};
28
29
/**
30
 * @brief A simple variant which can store one of any of the types in
31
 *        T on the heap.
32
 * @detailed When it is constructed the variant is empty by default.
33
 *           A verison of the variant that stores data on the stack can be
34
 *           enabled by defining CHILON_VARIANT_USES_STACK. This version
35
 *           can never be empty and constructs the first type when default
36
 *           constructed. This version is less convenient to use in places
37
 *           where only forward declarations of the variant types are
38
 *           available.
39
 * @tparam T pack of valid types the variant may store.
40
 */
41
template <class... T>
42
struct variant {
43
    enum { n_elements = sizeof...(T) };
44
#ifdef CHILON_VARIANT_USES_STACK
45
    enum { max_size = meta::max<sizeof(T)...>::value };
46
#else
47
  private:
48
    struct copier {
49
        template <class U> void operator()(U& v) const { v_ = v; }
50
        void operator()() const { v_.set_empty(); }
51
52
        copier(variant& v) : v_(v) {}
53
        variant& v_;
54
    };
55
56
    struct comparator {
57
        template <class U> bool operator()(U const& v) const {
58
            return v == v_.at<U>();
59
        }
60
        bool operator()() const { return v_.empty(); }
61
62
        comparator(variant const& v) : v_(v) {}
63
        variant const& v_;
64
    };
65
66
  public:
67
    bool operator==(variant const& rhs) const {
68
        return type_index_ == rhs.type_index_ &&
69
               variant_apply(rhs, comparator(*this));
70
    };
71
72
    bool operator!=(variant const& rhs) const {
73
        return ! (*this == rhs);
74
    };
75
76
    template <class... U>
77
    variant& operator=(variant<U...> const& rhs) {
78
        variant_apply(rhs, copier(*this));
79
        return *this;
80
    }
81
82
    variant& operator=(variant&& rhs) {
83
        delete_if_not_empty();
84
        data_ = rhs.data_;
85
        type_index_ = rhs.type_index_;
86
        rhs.data_ = 0;
87
        return *this;
88
    }
89
90
    template <class V>
91
    variant& operator=(std::auto_ptr<V>& rhs) {
92
        int const new_index = meta::index_of<V, T...>::value;
93
        static_assert(
94
            new_index < n_elements, "type does not exist in variant");
95
        delete_if_not_empty();
96
        type_index_ = new_index;
97
        data_ = rhs.release();
98
        return *this;
99
    }
100
101
    template <class V>
102
    variant& operator=(V const& rhs) {
103
        int const new_index = meta::index_of<V, T...>::value;
104
        static_assert(
105
            new_index < n_elements, "type does not exist in variant");
106
#ifdef CHILON_VARIANT_USES_STACK
107
        type_index_ = new_index;
108
        cast<V>() = rhs;
109
#else
110
        delete_if_not_empty();
111
112
        type_index_ = new_index;
113
        data_ = new V(rhs);
114
#endif
115
        return *this;
116
    }
117
118
    template <class V>
119
    V *release() {
120
        int const index = meta::index_of<V, T...>::value;
121
        static_assert(
122
            index < n_elements, "type does not exist in variant");
123
        assert(type_index_ == index);
124
        V *data = static_cast<V *>(data_);
125
        data_ = 0;
126
        return data;
127
    }
128
129
    bool empty() const { return ! data_; }
130
    void set_empty() {
131
        if (! empty()) {
132
            variant_apply(*this, deleter());
133
            data_ = 0;
134
135
            int const void_idx = meta::index_of<void, T...>::value;
136
            if (void_idx < n_elements) type_index_ = void_idx;
137
        }
138
    }
139
140
  private:
141
    void delete_if_not_empty() {
142
        if (! empty())
143
            variant_apply(*this, deleter());
144
    }
145
146
  public:
147
#endif
148
149
    typedef typename meta::head<T...>::type head_type;
150
151
    /**
152
     * Change variant to represent a default constructed V
153
     */
154
    template <class V>
155
    V& construct_default() {
156
        int const new_index = meta::index_of<V, T...>::value;
157
        static_assert(
158
            new_index < n_elements, "type does not exist in variant");
159
#ifdef CHILON_VARIANT_USES_STACK
160
        type_index_ = new_index;
161
        return *new (data_) V;
162
#else
163
        set_empty();
164
        type_index_ = new_index;
165
        data_ = new V();
166
        return cast<V>();
167
#endif
168
    }
169
170
    template <class V, CHILON_REQUIRE(meta::is_void<V>)>
171
    void construct_default() { set_empty(); }
172
173
    /**
174
     * Unsafe access to type at index I.
175
     */
176
    template <int I>
177
    typename meta::at<I, T...>::type& at() {
178
        static_assert(I < n_elements, "index too high");
179
#ifndef NDEBUG
180
        assert(type_index_ == I);
181
#endif
182
        return cast<typename meta::at<I, T...>::type>();
183
    }
184
185
    /**
186
     * Unsafe const access to type at index I.
187
     */
188
    template <int I>
189
    typename meta::at<I, T...>::type const& at() const {
190
        static_assert(I < n_elements, "type not found in variant");
191
#ifndef NDEBUG
192
        assert(type_index_ == I);
193
#endif
194
        return cast<typename meta::at<I, T...>::type>();
195
    }
196
197
    template <int I,
198
        CHILON_REQUIRE(meta::is_void<typename meta::at<I, T...>::type >) >
199
    void at() const {};
200
201
    template <class Y>
202
    Y& at() {
203
        return at<meta::index_of<Y, T...>::value>();
204
    }
205
206
    template <class Y>
207
    Y const& at() const {
208
        return at<meta::index_of<Y, T...>::value>();
209
    }
210
211
    template <class U>
212
    bool is() const {
213
        int const index = meta::index_of<U, T...>::value;
214
        static_assert(index < n_elements, "type does not exist in variant");
215
        return type_index_ == index;
216
    }
217
218
    int const type_index() const { return type_index_; }
219
220
  private:
221
    // unsafe casts
222
    template <class Y>
223
    Y const& cast() const {
224
        return (*reinterpret_cast<Y const *>(data_));
225
    }
226
227
    template <class Y>
228
    Y& cast() {
229
        return (*reinterpret_cast<Y *>(data_));
230
    }
231
232
  public:
233
    // default constructor constructs the first element in the tuple
234
#ifdef CHILON_VARIANT_USES_STACK
235
    variant() : type_index_(0) {
236
        new (data_) head_type;
237
    }
238
239
  private:
240
    char data_[max_size];
241
#else
242
  private:
243
    struct initialiser {
244
        template <class U>
245
        void operator()(U const& v) const {
246
            v_.data_ = new U(v);
247
        }
248
249
        void operator()() const {
250
            v_.set_empty();
251
        }
252
253
        initialiser(variant& v) : v_(v) {}
254
      protected:
255
        variant& v_;
256
    };
257
258
    struct careful_initialiser : initialiser {
259
        template <class U>
260
        CHILON_RETURN_REQUIRE(void, meta::contains<U, T...>)
261
        operator()(U const& v) const {
262
            this->v_.type_index_ = meta::index_of<U, T...>::value;
263
            initialiser::operator()(v);
264
        }
265
266
        template <class U>
267
        CHILON_RETURN_NOT_REQUIRE(void, meta::contains<U, T...>)
268
        operator()(U const& v) const {
269
            throw invalid_variant_initalisation();
270
        }
271
272
        careful_initialiser(variant& v) : initialiser(v) {}
273
    };
274
275
    struct deleter {
276
        template <class U> void operator()(U& v) const { delete &v; }
277
        void operator()() const {}
278
    };
279
280
  public:
281
    variant() : data_(0), type_index_(0) {
282
        construct_default< typename meta::head<T...>::type >();
283
    }
284
285
    variant(variant const& rhs) : type_index_(rhs.type_index_) {
286
        variant_apply(rhs, initialiser(*this));
287
    }
288
289
    variant(variant&& rhs)
290
      : data_(rhs.data_), type_index_(rhs.type_index_)
291
    { rhs.data_ = 0; }
292
293
    template <class U,
294
              CHILON_REQUIRE_I(meta::index_of<U, T...>::value < n_elements)>
295
    variant(U const& u)
296
      : data_(new U(u)),
297
        type_index_(meta::index_of<U, T...>::value)
298
    {}
299
300
    template <class U,
301
              CHILON_REQUIRE_I(meta::index_of<U, T...>::value < n_elements)>
302
    variant(U&& u)
303
      : data_(new U(std::move(u))),
304
        type_index_(meta::index_of<U, T...>::value)
305
    {}
306
307
    // TODO: check variant is not subvariant first, then handle
308
    //       it like a regular member type
309
    template <class... U>
310
    variant(variant<U...> const& rhs) {
311
        variant_apply(rhs, careful_initialiser(*this));
312
    }
313
314
    ~variant() { set_empty(); }
315
316
    template <class U>
317
    variant(std::auto_ptr<U>& rhs)
318
      : type_index_(meta::index_of<U, T...>::value)
319
    {
320
        static_assert(
321
             meta::index_of<U, T...>::value < n_elements,
322
             "type does not exist in variant");
323
        data_ = rhs.release();
324
    }
325
  protected:
326
    void *data_;
327
#endif
328
    int  type_index_;
329
};
330
331
#if 0 && ! defined(CHILON_VARIANT_USES_STACK)
332
/**
333
 * @brief If a void entry is used as the first entry in a variant, then
334
 *        the variant can be empty. The void entry is not considered part
335
 *        of the variant in the indexing system.
336
 */
337
template <class... T>
338
struct variant<void, T...> : variant<T...> {
339
    typedef variant<T...> base_t;
340
341
    template <class U>
342
    variant<void, T...>& operator=(U&& rhs) {
343
        base_t::operator=(std::move<U>(rhs));
344
    }
345
346
    variant() : base_t() {}
347
348
    template <class U>
349
    variant(U&& rhs) : base_t(std::forward<U>(rhs)) {}
350
};
351
#endif
352
353
namespace detail {
354
    template <class T, class F>
355
    struct variant_apply_helper {};
356
357
    template <class F, class T>
358
    struct variant_call_type;
359
360
    template <class F, class... T>
361
    struct variant_call_type<F, variant<T...> > :
362
        meta::call_type_ref<F, typename meta::at<0, T...>::type> {};
363
364
    template <class F, class... T>
365
    struct variant_apply_lookup_table {
366
        typedef variant<T...> type;
367
368
        struct apply {
369
            virtual typename variant_call_type<F, type>::type
370
            operator()(type const& v, F const& f) =0;
371
372
            virtual typename variant_call_type<F, type>::type
373
            operator()(type& v, F const& f) =0;
374
        };
375
376
        boost::ptr_array<apply, type::n_elements> appliers_;
377
378
        template <int I, class U>
379
        struct apply_idx : apply {
380
            virtual auto operator()(type const& v, F const& f)
381
                CHILON_RETURN(f(v.template at<I>()))
382
            virtual auto operator()(type& v, F const& f)
383
                CHILON_RETURN(f(v.template at<I>()))
384
        };
385
386
        template <int I>
387
        struct apply_idx<I, void> : apply {
388
            virtual auto operator()(type const& v, F const& f)
389
                CHILON_RETURN(f())
390
391
            virtual auto operator()(type& v, F const& f)
392
                CHILON_RETURN(f())
393
        };
394
395
        template <int I, int L>
396
        struct create_applier {
397
            static void exec(decltype(appliers_)& appliers) {
398
                appliers.replace(
399
                    I,
400
                    new apply_idx<I, typename meta::at<I, T...>::type >()
401
                );
402
                create_applier<I + 1, L>::exec(appliers);
403
            }
404
        };
405
406
        template <int I>
407
        struct create_applier<I, I> {
408
            static void exec(decltype(appliers_)& appliers) {}
409
        };
410
411
        variant_apply_lookup_table() {
412
            create_applier<0, type::n_elements>::exec(appliers_);
413
        }
414
    };
415
416
    template <class... T, class F>
417
    struct variant_apply_helper< variant<T...>, F > {
418
        typedef variant<T...> type;
419
420
        inline static typename variant_call_type<F, type>::type
421
        exec(type const& v, F const& f) {
422
            return chilon::singleton<
423
                variant_apply_lookup_table<F, T...>
424
            >::instance().appliers_[v.type_index()](v, f);
425
        }
426
427
        inline static typename variant_call_type<F, type>::type
428
        exec(type& v, F const& f) {
429
            return chilon::singleton<
430
                variant_apply_lookup_table<F, T...>
431
            >::instance().appliers_[v.type_index()](v, f);
432
        }
433
434
        inline static typename variant_call_type<F, type>::type
435
        exec(int const idx, type const& v, F const& f) {
436
            return chilon::singleton<
437
                variant_apply_lookup_table<F, T...>
438
            >::instance().appliers_[idx](v, f);
439
        }
440
441
        inline static typename variant_call_type<F, type>::type
442
        exec(int const idx, type& v, F const& f) {
443
            return chilon::singleton<
444
                variant_apply_lookup_table<F, T...>
445
            >::instance().appliers_[idx](v, f);
446
        }
447
    };
448
}
449
450
template <class F, class T>
451
inline auto variant_apply(T& v, F const& f)
452
    CHILON_RETURN(detail::variant_apply_helper<T, F>::exec(v, f))
453
454
template <class F, class T>
455
inline auto variant_apply(T const& v, F const& f)
456
    CHILON_RETURN(detail::variant_apply_helper<T, F>::exec(v, f))
457
458
template <class F, class T>
459
inline auto variant_construct_apply(int const idx, T const& v, F const& f)
460
    CHILON_RETURN(detail::variant_apply_helper<T, F>::exec(idx, v, f))
461
462
template <class F, class T>
463
inline auto variant_construct_apply(int const idx, T& v, F const& f)
464
    CHILON_RETURN(detail::variant_apply_helper<T, F>::exec(idx, v, f))
465
466
template <class T, class... Y>
467
inline T& variant_as(variant<Y...>& v) {
468
    return v.template at<T>();
469
}
470
471
template <class T, class... Y>
472
inline T& variant_as(variant<Y...> const& v) {
473
    return v.template at<T>();
474
}
475
476
template <class T>
477
inline T& variant_as(T& v) { return v; }
478
479
template <class T>
480
inline T const& variant_as(T const& v) { return v; }
481
482
/**
483
 * call construct_default on a variant.
484
 */
485
template <class T, class... Y>
486
inline void construct_default(variant<Y...>& v) {
487
    v.template construct_default<T>();
488
}
489
490
/**
491
 * construct_default on non-variant does nothing
492
 */
493
template <class T>
494
inline void construct_default(T const& v) {}
495
496
struct hash_variant {
497
    size_t operator()() const { return 0; }
498
499
    template <class T>
500
    size_t operator()(T const& t) const { return chilon::hash(t); }
501
};
502
503
/**
504
 * register with boost::hash
505
 */
506
template <class... T>
507
std::size_t inline hash_value(variant<T...> const& t) {
508
    return variant_apply(t, hash_variant());
509
}
510
511
}
512
513
namespace std {
514
    template <class... T>
515
    struct hash<chilon::variant<T...>> {
516
        std::size_t operator()(chilon::variant<T...> const& arg) const {
517
            return chilon::hash_value(arg);
518
        }
519
    };
520
}
521
522
#endif