summaryrefslogtreecommitdiff
path: root/zen/optional.h
blob: 88928ac04ecc2409f3677d3a37fda0032a4fb26e (plain)
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
// *****************************************************************************
// * This file is part of the FreeFileSync project. It is distributed under    *
// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0          *
// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
// *****************************************************************************

#ifndef OPTIONAL_H_2857428578342203589
#define OPTIONAL_H_2857428578342203589

//#include <cassert>
#include <type_traits>


namespace zen
{
/*
Optional return value without heap memory allocation!
 -> interface like a pointer, performance like a value

 Usage:
 ------
 Opt<MyEnum> someFunction();
{
   if (allIsWell)
       return enumVal;
   else
       return NoValue();
}

 Opt<MyEnum> optValue = someFunction();
 if (optValue)
       ... use *optValue ...
*/

struct NoValue {};

template <class T>
class Opt
{
public:
    Opt()        {}
    Opt(NoValue) {}
    Opt(const T& val) : valid_(true) { new (&rawMem_) T(val); } //throw X
    Opt(     T&& tmp) : valid_(true) { new (&rawMem_) T(std::move(tmp)); }

    Opt(const Opt& other) : valid_(other.valid_)
    {
        if (const T* val = other.get())
            new (&rawMem_) T(*val); //throw X
    }

    ~Opt() { if (T* val = get()) val->~T(); }

    Opt& operator=(NoValue) //support assignment to Opt<const T>
    {
        if (T* val = get())
        {
            valid_ = false;
            val->~T();
        }
        return *this;
    }

    Opt& operator=(const Opt& other) //strong exception-safety iff T::operator=() is strongly exception-safe
    {
        if (T* val = get())
        {
            if (const T* valOther = other.get())
                *val = *valOther; //throw X
            else
            {
                valid_ = false;
                val->~T();
            }
        }
        else if (const T* valOther = other.get())
        {
            new (&rawMem_) T(*valOther); //throw X
            valid_ = true;
        }
        return *this;
    }

    explicit operator bool() const { return valid_; } //thank you, C++11!!!

    const T* get() const { return valid_ ? reinterpret_cast<const T*>(&rawMem_) : nullptr; }
    T*       get()       { return valid_ ? reinterpret_cast<      T*>(&rawMem_) : nullptr; }

    const T& operator*() const { return *get(); }
    /**/  T& operator*()       { return *get(); }

    const T* operator->() const { return get(); }
    /**/  T* operator->()       { return get(); }

private:
    std::aligned_storage_t<sizeof(T), alignof(T)> rawMem_; //don't require T to be default-constructible!
    bool valid_ = false;
};


template <class T> inline
bool operator==(const Opt<T>& lhs, const Opt<T>& rhs)
{
    if (static_cast<bool>(lhs) != static_cast<bool>(rhs))
        return false;
    if (!lhs)
        return true;
    return *lhs == *rhs;
}
template <class T> inline
bool operator!=(const Opt<T>& lhs, const Opt<T>& rhs) { return !(lhs == rhs); }

}

#endif //OPTIONAL_H_2857428578342203589
bgstack15