summaryrefslogtreecommitdiff
path: root/asl/status.hpp
blob: c494759454e0114c0dd66fcebd0ec02309d477b7 (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
#pragma once

#include "asl/integers.hpp"
#include "asl/string_view.hpp"

namespace asl
{

// @Todo Make status with formatting

enum class status_code : uint8_t
{
    ok               = 0,
    unknown          = 1,
    internal         = 2,
    runtime          = 3,
    invalid_argument = 4,
};

class status
{
    void* m_payload{};

    static constexpr void* status_to_payload(status_code code)
    {
        return code == status_code::ok
            ? nullptr
            : bit_cast<void*>((static_cast<uintptr_t>(code) << 1) | 1);
    }

    static constexpr status_code payload_to_status(void* payload)
    {
        return static_cast<status_code>(bit_cast<uintptr_t>(payload) >> 1);
    }

    constexpr bool is_inline() const
    {
        return m_payload == nullptr || (bit_cast<uintptr_t>(m_payload) & 1) != 0;
    }

    constexpr status_code code_inline() const
    {
        ASL_ASSERT(is_inline());
        if (m_payload == nullptr)
        {
            return status_code::ok;
        }
        return payload_to_status(m_payload);
    }

    status_code code_internal() const;

    void unref();
    
public:
    constexpr status() = default;

    constexpr ~status()
    {
        if (!is_inline())
        {
            unref();
        }
    }
    
    explicit constexpr status(status_code code)
        : m_payload{status_to_payload(code)}
    {}

    status(status_code code, string_view msg);

    constexpr status(status&& other)
        : m_payload{exchange(other.m_payload, nullptr)}
    {}

    constexpr status& operator=(status&& other)
    {
        if (&other != this)
        {
            swap(m_payload, other.m_payload);
        }
        return *this;
    }

    // @Todo Copy constructor & assignment

    constexpr bool ok() const
    {
        return m_payload == nullptr || code() == status_code::ok;
    }

    // NOLINTNEXTLINE(*-explicit-conversions)
    constexpr operator bool() const { return ok(); }

    constexpr status_code code() const
    {
        return is_inline() ? code_inline() : code_internal();
    }
};

} // namespace asl