summaryrefslogtreecommitdiffstats
path: root/depends/libnbtplusplus/include/value.h
blob: a2db2957edb3786d7ea80fbf31815c00df18ecd7 (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
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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
/*
 * libnbt++ - A library for the Minecraft Named Binary Tag format.
 * Copyright (C) 2013, 2015  ljfa-ag
 *
 * This file is part of libnbt++.
 *
 * libnbt++ is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * libnbt++ is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with libnbt++.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef TAG_REF_PROXY_H_INCLUDED
#define TAG_REF_PROXY_H_INCLUDED

#include "tag.h"
#include <string>
#include <type_traits>

#include "nbt++_export.h"

namespace nbt
{

/**
 * @brief Contains an NBT value of fixed type
 *
 * This class is a convenience wrapper for @c std::unique_ptr<tag>.
 * A value can contain any kind of tag or no tag (nullptr) and provides
 * operations for handling tags of which the type is not known at compile time.
 * Assignment or the set method on a value with no tag will fill in the value.
 *
 * The rationale for the existance of this class is to provide a type-erasured
 * means of storing tags, especially when they are contained in tag_compound
 * or tag_list. The alternative would be directly using @c std::unique_ptr<tag>
 * and @c tag&, which is how it was done in libnbt++1. The main drawback is that
 * it becomes very cumbersome to deal with tags of unknown type.
 *
 * For example, in this case it would not be possible to allow a syntax like
 * <tt>compound["foo"] = 42</tt>. If the key "foo" does not exist beforehand,
 * the left hand side could not have any sensible value if it was of type
 * @c tag&.
 * Firstly, the compound tag would have to create a new tag_int there, but it
 * cannot know that the new tag is going to be assigned an integer.
 * Also, if the type was @c tag& and it allowed assignment of integers, that
 * would mean the tag base class has assignments and conversions like this.
 * Which means that all other tag classes would inherit them from the base
 * class, even though it does not make any sense to allow converting a
 * tag_compound into an integer. Attempts like this should be caught at
 * compile time.
 *
 * This is why all the syntactic sugar for tags is contained in the value class
 * while the tag class only contains common operations for all tag types.
 */
class NBT___EXPORT value
{
public:
    //Constructors
    value() noexcept {}
    explicit value(std::unique_ptr<tag>&& t) noexcept: tag_(std::move(t)) {}
    explicit value(tag&& t);

    //Moving
    value(value&&) noexcept = default;
    value& operator=(value&&) noexcept = default;

    //Copying
    explicit value(const value& rhs);
    value& operator=(const value& rhs);

    /**
     * @brief Assigns the given value to the tag if the type matches
     * @throw std::bad_cast if the type of @c t is not the same as the type
     * of this value
     */
    value& operator=(tag&& t);
    void set(tag&& t);

    //Conversion to tag
    /**
     * @brief Returns the contained tag
     *
     * If the value is uninitialized, the behavior is undefined.
     */
    operator tag&() { return get(); }
    operator const tag&() const { return get(); }
    tag& get() { return *tag_; }
    const tag& get() const { return *tag_; }

    /**
     * @brief Returns a reference to the contained tag as an instance of T
     * @throw std::bad_cast if the tag is not of type T
     */
    template<class T>
    T& as();
    template<class T>
    const T& as() const;

    //Assignment of primitives and string
    /**
     * @brief Assigns the given value to the tag if the type is compatible
     * @throw std::bad_cast if the value is not convertible to the tag type
     * via a widening conversion
     */
    value& operator=(int8_t val);
    value& operator=(int16_t val);
    value& operator=(int32_t val);
    value& operator=(int64_t val);
    value& operator=(float val);
    value& operator=(double val);

    /**
     * @brief Assigns the given string to the tag if it is a tag_string
     * @throw std::bad_cast if the contained tag is not a tag_string
     */
    value& operator=(const std::string& str);
    value& operator=(std::string&& str);

    //Conversions to primitives and string
    /**
     * @brief Returns the contained value if the type is compatible
     * @throw std::bad_cast if the tag type is not convertible to the desired
     * type via a widening conversion
     */
    explicit operator int8_t() const;
    explicit operator int16_t() const;
    explicit operator int32_t() const;
    explicit operator int64_t() const;
    explicit operator float() const;
    explicit operator double() const;

    /**
     * @brief Returns the contained string if the type is tag_string
     *
     * If the value is uninitialized, the behavior is undefined.
     * @throw std::bad_cast if the tag type is not tag_string
     */
    explicit operator const std::string&() const;

    ///Returns true if the value is not uninitialized
    explicit operator bool() const { return tag_ != nullptr; }

    /**
     * @brief In case of a tag_compound, accesses a tag by key with bounds checking
     *
     * If the value is uninitialized, the behavior is undefined.
     * @throw std::bad_cast if the tag type is not tag_compound
     * @throw std::out_of_range if given key does not exist
     * @sa tag_compound::at
     */
    value& at(const std::string& key);
    const value& at(const std::string& key) const;

    /**
     * @brief In case of a tag_compound, accesses a tag by key
     *
     * If the value is uninitialized, the behavior is undefined.
     * @throw std::bad_cast if the tag type is not tag_compound
     * @sa tag_compound::operator[]
     */
    value& operator[](const std::string& key);
    value& operator[](const char* key); //need this overload because of conflict with built-in operator[]

    /**
     * @brief In case of a tag_list, accesses a tag by index with bounds checking
     *
     * If the value is uninitialized, the behavior is undefined.
     * @throw std::bad_cast if the tag type is not tag_list
     * @throw std::out_of_range if the index is out of range
     * @sa tag_list::at
     */
    value& at(size_t i);
    const value& at(size_t i) const;

    /**
     * @brief In case of a tag_list, accesses a tag by index
     *
     * No bounds checking is performed. If the value is uninitialized, the
     * behavior is undefined.
     * @throw std::bad_cast if the tag type is not tag_list
     * @sa tag_list::operator[]
     */
    value& operator[](size_t i);
    const value& operator[](size_t i) const;

    ///Returns a reference to the underlying std::unique_ptr<tag>
    std::unique_ptr<tag>& get_ptr() { return tag_; }
    const std::unique_ptr<tag>& get_ptr() const { return tag_; }
    ///Resets the underlying std::unique_ptr<tag> to a different value
    void set_ptr(std::unique_ptr<tag>&& t) { tag_ = std::move(t); }

    ///@sa tag::get_type
    tag_type get_type() const;

    NBT___EXPORT friend bool operator==(const value& lhs, const value& rhs);
    NBT___EXPORT friend bool operator!=(const value& lhs, const value& rhs);

private:
    std::unique_ptr<tag> tag_;
};

template<class T>
T& value::as()
{
    return tag_->as<T>();
}

template<class T>
const T& value::as() const
{
    return tag_->as<T>();
}

}

#endif // TAG_REF_PROXY_H_INCLUDED