libpromeki main
PROfessional MEdia toolKIt
 
Loading...
Searching...
No Matches
variant.h
Go to the documentation of this file.
1
8#pragma once
9
10#include <variant>
11#include <cstdint>
13#include <promeki/core/util.h>
14#include <promeki/core/string.h>
17#include <promeki/core/size2d.h>
18#include <promeki/core/uuid.h>
21#include <promeki/core/list.h>
22#include <promeki/thirdparty/nlohmann/json.hpp>
23
25
61#define PROMEKI_VARIANT_TYPES \
62 X(TypeInvalid, std::monostate) \
63 X(TypeBool, bool) \
64 X(TypeU8, uint8_t) \
65 X(TypeS8, int8_t) \
66 X(TypeU16, uint16_t) \
67 X(TypeS16, int16_t) \
68 X(TypeU32, uint32_t) \
69 X(TypeS32, int32_t) \
70 X(TypeU64, uint64_t) \
71 X(TypeS64, int64_t) \
72 X(TypeFloat, float) \
73 X(TypeDouble, double) \
74 X(TypeString, String) \
75 X(TypeDateTime, DateTime) \
76 X(TypeTimeStamp, TimeStamp) \
77 X(TypeSize2D, Size2Du32) \
78 X(TypeUUID, UUID) \
79 X(TypeTimecode, Timecode) \
80 X(TypeRational, Rational<int>)
81
82namespace detail {
84 struct VariantEnd {};
85}
86
106template <typename... Types> class VariantImpl {
107 public:
108 #define X(name, type) name,
116 #undef X
117
118 #define X(name, type) PROMEKI_STRINGIFY(type),
124 static const char *typeName(Type id) {
125 static const char *items[] = { PROMEKI_VARIANT_TYPES };
126 PROMEKI_ASSERT(id < PROMEKI_ARRAY_SIZE(items));
127 return items[id];
128 }
129 #undef X
130
147 if(val.is_null()) return VariantImpl();
148 if(val.is_boolean()) return val.get<bool>();
149 if(val.is_number_integer()) {
150 if(val.is_number_unsigned()) return val.get<uint64_t>();
151 return val.get<int64_t>();
152 }
153 if(val.is_number_float()) return val.get<double>();
154 if(val.is_string()) return String(val.get<std::string>());
155 return String(val.dump());
156 }
157
159 VariantImpl() = default;
160
166 template <typename T> VariantImpl(const T& value) : v(value) { }
167
169 bool isValid() const {
170 return v.index() != 0;
171 }
172
178 template <typename T> void set(const T &value) { v = value; }
179
196 template <typename To> To get(Error *err = nullptr) const {
197 return std::visit([err](auto &&arg) -> To {
198 using From = std::decay_t<decltype(arg)>;
199 if(err != nullptr) *err = Error::Ok;
200 if constexpr (std::is_same_v<From, To>) {
201 return arg;
202
203 } else if constexpr (std::is_same_v<To, bool>) {
204 if constexpr (std::is_integral<From>::value ||
205 std::is_floating_point<From>::value) return arg ? true : false;
206 if constexpr (std::is_same_v<From, String>) return arg.template to<To>(err);
207
208 } else if constexpr (std::is_same_v<To, int8_t>) {
209 if constexpr (std::is_same_v<From, bool>) return !!arg;
210 if constexpr (std::is_integral<From>::value ||
211 std::is_floating_point<From>::value) return promekiConvert<To>(arg, err);
212 if constexpr (std::is_same_v<From, String>) return arg.template to<To>(err);
213
214 } else if constexpr (std::is_same_v<To, uint8_t>) {
215 if constexpr (std::is_same_v<From, bool>) return !!arg;
216 if constexpr (std::is_integral<From>::value ||
217 std::is_floating_point<From>::value) return promekiConvert<To>(arg, err);
218 if constexpr (std::is_same_v<From, String>) return arg.template to<To>(err);
219
220 } else if constexpr (std::is_same_v<To, int16_t>) {
221 if constexpr (std::is_same_v<From, bool>) return !!arg;
222 if constexpr (std::is_integral<From>::value ||
223 std::is_floating_point<From>::value) return promekiConvert<To>(arg, err);
224 if constexpr (std::is_same_v<From, String>) return arg.template to<To>(err);
225
226 } else if constexpr (std::is_same_v<To, uint16_t>) {
227 if constexpr (std::is_same_v<From, bool>) return !!arg;
228 if constexpr (std::is_integral<From>::value ||
229 std::is_floating_point<From>::value) return promekiConvert<To>(arg, err);
230 if constexpr (std::is_same_v<From, String>) return arg.template to<To>(err);
231
232 } else if constexpr (std::is_same_v<To, int32_t>) {
233 if constexpr (std::is_same_v<From, bool>) return !!arg;
234 if constexpr (std::is_integral<From>::value ||
235 std::is_floating_point<From>::value) return promekiConvert<To>(arg, err);
236 if constexpr (std::is_same_v<From, String>) return arg.template to<To>(err);
237
238 } else if constexpr (std::is_same_v<To, uint32_t>) {
239 if constexpr (std::is_same_v<From, bool>) return !!arg;
240 if constexpr (std::is_integral<From>::value ||
241 std::is_floating_point<From>::value) return promekiConvert<To>(arg, err);
242 if constexpr (std::is_same_v<From, String>) return arg.template to<To>(err);
243
244 } else if constexpr (std::is_same_v<To, int64_t>) {
245 if constexpr (std::is_same_v<From, bool>) return !!arg;
246 if constexpr (std::is_integral<From>::value ||
247 std::is_floating_point<From>::value) return promekiConvert<To>(arg, err);
248 if constexpr (std::is_same_v<From, String>) return arg.template to<To>(err);
249
250 } else if constexpr (std::is_same_v<To, uint64_t>) {
251 if constexpr (std::is_same_v<From, bool>) return !!arg;
252 if constexpr (std::is_integral<From>::value ||
253 std::is_floating_point<From>::value) return promekiConvert<To>(arg, err);
254 if constexpr (std::is_same_v<From, String>) return arg.template to<To>(err);
255
256 } else if constexpr (std::is_same_v<To, float>) {
257 if constexpr (std::is_same_v<From, bool>) return !!arg;
258 if constexpr (std::is_integral<From>::value ||
259 std::is_floating_point<From>::value) return promekiConvert<To>(arg, err);
260 if constexpr (std::is_same_v<From, String>) return arg.template to<To>(err);
261 if constexpr (std::is_same_v<From, Rational<int>>) return arg.toDouble();
262
263 } else if constexpr (std::is_same_v<To, double>) {
264 if constexpr (std::is_same_v<From, bool>) return !!arg;
265 if constexpr (std::is_integral<From>::value ||
266 std::is_floating_point<From>::value) return promekiConvert<To>(arg, err);
267 if constexpr (std::is_same_v<From, String>) return arg.template to<To>(err);
268 if constexpr (std::is_same_v<From, Rational<int>>) return arg.toDouble();
269
270 } else if constexpr (std::is_same_v<To, DateTime>) {
271 if constexpr (std::is_same_v<From, String>) return DateTime::fromString(
273
274 } else if constexpr (std::is_same_v<To, UUID>) {
275 if constexpr (std::is_same_v<From, String>) {
276 Error e;
277 UUID ret = UUID::fromString(arg, &e);
278 if(e.isError()) {
279 if(err != nullptr) *err = Error::Invalid;
280 return UUID();
281 }
282 return ret;
283 }
284
285 } else if constexpr (std::is_same_v<To, Timecode>) {
286 if constexpr (std::is_same_v<From, String>) {
287 std::pair<Timecode, Error> ret = Timecode::fromString(arg);
288 if(ret.second.isError()) {
289 if(err != nullptr) *err = Error::Invalid;
290 return Timecode();
291 }
292 return ret.first;
293 }
294
295
296 } else if constexpr (std::is_same_v<To, String>) {
297 if constexpr (std::is_same_v<From, bool>) return String::number(arg);
298 if constexpr (std::is_same_v<From, int8_t>) return String::number(arg);
299 if constexpr (std::is_same_v<From, uint8_t>) return String::number(arg);
300 if constexpr (std::is_same_v<From, int16_t>) return String::number(arg);
301 if constexpr (std::is_same_v<From, uint16_t>) return String::number(arg);
302 if constexpr (std::is_same_v<From, int32_t>) return String::number(arg);
303 if constexpr (std::is_same_v<From, uint32_t>) return String::number(arg);
304 if constexpr (std::is_same_v<From, int64_t>) return String::number(arg);
305 if constexpr (std::is_same_v<From, uint64_t>) return String::number(arg);
306 if constexpr (std::is_same_v<From, float>) return String::number(arg);
307 if constexpr (std::is_same_v<From, double>) return String::number(arg);
308 if constexpr (std::is_same_v<From, DateTime>) return arg.toString();
309 if constexpr (std::is_same_v<From, TimeStamp>) return arg.toString();
310 if constexpr (std::is_same_v<From, Size2Du32>) return arg.toString();
311 if constexpr (std::is_same_v<From, UUID>) return arg.toString();
312 if constexpr (std::is_same_v<From, Timecode>) return arg.toString().first;
313 if constexpr (std::is_same_v<From, Rational<int>>) return arg.toString();
314
315 }
316 if(err != nullptr) *err = Error::Invalid;
317 return To{};
318 }, v);
319 }
320
322 Type type() const {
323 return static_cast<Type>(v.index());
324 }
325
327 const char *typeName() const {
328 return typeName(type());
329 }
330
342 switch(type()) {
343 case TypeString:
344 case TypeDateTime:
345 case TypeTimeStamp:
346 case TypeSize2D:
347 case TypeUUID:
348 case TypeTimecode:
349 case TypeRational:
350 return get<std::string>();
351 break;
352 }
353 return *this;
354 }
355
356 private:
357 std::variant<Types...> v;
358};
359
360#define X(name, type) type,
363#undef X
364
367
369
static constexpr const char * DefaultFormat
Default strftime format string ("%F %T" = "YYYY-MM-DD HH:MM:SS").
Definition datetime.h:42
static DateTime fromString(const String &str, const char *fmt=DefaultFormat, Error *err=nullptr)
Parses a DateTime from a formatted string.
Definition datetime.h:59
Lightweight error code wrapper for the promeki library.
Definition error.h:39
@ Ok
No error.
Definition error.h:51
@ Invalid
Invalid value or argument (EINVAL).
Definition error.h:66
Dynamic array container wrapping std::vector.
Definition list.h:40
List()=default
Default constructor. Creates an empty list.
Encoding-aware string class with copy-on-write semantics.
Definition string.h:35
static String number(int8_t value, int base=10, int padding=0, char padchar=' ', bool addPrefix=false)
Converts a numeric value to its String representation.
Class for holding and manipulating timecode.
Definition timecode.h:45
static std::pair< Timecode, Error > fromString(const String &str)
Parses a Timecode from its string representation.
Universally Unique Identifier (UUID).
Definition uuid.h:34
static UUID fromString(const char *string, Error *err=nullptr)
Parses a UUID from a string representation.
Type-safe tagged union that can hold any of the types listed in PROMEKI_VARIANT_TYPES.
Definition variant.h:106
void set(const T &value)
Replaces the currently held value with value.
Definition variant.h:178
VariantImpl(const T &value)
Constructs a variant holding a copy of value.
Definition variant.h:166
static const char * typeName(Type id)
Returns the human-readable C++ type name for the given Type enumerator.
Definition variant.h:124
static VariantImpl fromJson(const nlohmann::json &val)
Constructs a VariantImpl from a JSON value, inferring the best native type.
Definition variant.h:146
const char * typeName() const
Returns the human-readable type name of the currently held value.
Definition variant.h:327
Type type() const
Returns the Type enumerator for the currently held value.
Definition variant.h:322
VariantImpl()=default
Default-constructs an invalid (empty) variant holding std::monostate.
To get(Error *err=nullptr) const
Converts the stored value to the requested type To.
Definition variant.h:196
VariantImpl toStandardType() const
Converts complex types to their String representation, leaving simple types unchanged.
Definition variant.h:341
Type
Enumerates every type the variant can hold.
Definition variant.h:115
bool isValid() const
Returns true if the variant holds a value other than std::monostate.
Definition variant.h:169
#define PROMEKI_VARIANT_TYPES
X-macro that defines all supported Variant types.
Definition variant.h:61
#define PROMEKI_NAMESPACE_BEGIN
Starts a promeki namespace block.
Definition namespace.h:14
#define PROMEKI_NAMESPACE_END
Ends a promeki namespace block.
Definition namespace.h:19
const T & value(const Result< T > &r)
Returns the value from a Result.
Definition result.h:56
Sentinel type used to absorb the trailing comma from X-macro expansion.
Definition variant.h:84