aboutsummaryrefslogtreecommitdiff
path: root/src/unique.hpp
blob: c214dbc54c134d02c16550af8185d37f5c097b49 (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
#pragma once

// #################################################################################################

// A unique type generator
template <typename T, typename TAG, typename specialization = void>
class Unique
{
    private:
    T val; // no default initialization so that std::is_trivial_v<Unique<T>> == true

    public:
    using self = Unique<T, TAG, specialization>;
    using type = T;

    ~Unique() = default;
    constexpr explicit Unique(T b_)
    : val{ b_ }
    {
    }

    // rule of 5
    constexpr Unique() = default;
    constexpr Unique(Unique const&) = default;
    constexpr Unique(Unique&&) = default;
    constexpr Unique& operator=(Unique const&) = default;
    constexpr Unique& operator=(Unique&&) = default;

    // Forbid construction with any other class, especially with types that convert naturally to T.
    // For instance, this prevents construction with a float when T is an integer type.
    // Conversion will have to be explicit and the developer will be aware of it.
    // However we want to keep the same-type copy constructors (including with an inherited class), hence the enable_if stuff.
    template <typename AnyOtherClass, std::enable_if_t<!std::is_base_of_v<self, std::decay_t<AnyOtherClass>>, bool> = true>
    Unique(AnyOtherClass const&) = delete;
    template <typename AnyOtherClass, std::enable_if_t<!std::is_base_of_v<self, std::decay_t<AnyOtherClass>>, bool> = true>
    Unique(AnyOtherClass&&) = delete;

    // can't implicitly affect from base type
    Unique& operator=(T const&) = delete;
    constexpr Unique& operator=(T&&) = delete;

    // cast
    constexpr operator T const&() const noexcept { return val; }
    constexpr T const& value() const noexcept { return val; }

    // pre-increment
    auto& operator++() noexcept
    {
        ++val;
        return *this;
    }
    // post-increment
    auto operator++(int) noexcept
    {
        return Unique<T, TAG>{ std::exchange(val, val + 1) };
    }

    // pre-decrement
    auto& operator--() noexcept
    {
        --val;
        return *this;
    }
    // post-decrement
    auto operator--(int) noexcept
    {
        return Unique<T, TAG>{ std::exchange(val, val - 1) };
    }
};

template <typename T, typename TAG>
class Unique<T, TAG, std::enable_if_t<!std::is_scalar_v<T>>>
: public T
{
    public:
    using self = Unique<T, TAG, void>;
    using type = T;
    using T::T;
    explicit Unique(T const& b_)
    : T{ b_ }
    {
    }

    // rule of 5
    constexpr Unique() = default;
    constexpr Unique(Unique const&) = default;
    constexpr Unique(Unique&&) = default;
    constexpr Unique& operator=(Unique const&) = default;
    constexpr Unique& operator=(Unique&&) = default;

    // Forbid construction with any other class, especially with types that convert naturally to T.
    // For instance, this prevents construction with a float when T is an integer type.
    // Conversion will have to be explicit and the developer will be aware of it.
    // However we want to keep the same-type copy constructors (including with an inherited class), hence the enable_if stuff.
    template <typename AnyOtherClass, std::enable_if_t<!std::is_base_of_v<self, std::decay_t<AnyOtherClass>>, bool> = true>
    Unique(AnyOtherClass const&) = delete;
    template <typename AnyOtherClass, std::enable_if_t<!std::is_base_of_v<self, std::decay_t<AnyOtherClass>>, bool> = true>
    Unique(AnyOtherClass&&) = delete;

    // can't implicitly affect from base type
    Unique& operator=(T const&) = delete;
    constexpr Unique& operator=(T&&) = delete;
};

#define DECLARE_UNIQUE_TYPE(_name, _type) using _name = Unique<_type, class Unique_##_name##_Tag>