aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/exess/bindings/cpp/include
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2021-02-25 10:27:59 -0500
committerDavid Robillard <d@drobilla.net>2021-03-08 23:23:05 -0500
commitc4821c8e6bf1f81c6ea31e11ebc0fc1666e9337b (patch)
treea62995534f5f606ac2f8bae22d525532b824cb5e /subprojects/exess/bindings/cpp/include
parent6bcd18ae60482790b645a345f718e7099250f261 (diff)
downloadserd-c4821c8e6bf1f81c6ea31e11ebc0fc1666e9337b.tar.gz
serd-c4821c8e6bf1f81c6ea31e11ebc0fc1666e9337b.tar.bz2
serd-c4821c8e6bf1f81c6ea31e11ebc0fc1666e9337b.zip
Add exess from git@gitlab.com:drobilla/exess.git 4638b1f
Diffstat (limited to 'subprojects/exess/bindings/cpp/include')
-rw-r--r--subprojects/exess/bindings/cpp/include/exess/exess.hpp707
1 files changed, 707 insertions, 0 deletions
diff --git a/subprojects/exess/bindings/cpp/include/exess/exess.hpp b/subprojects/exess/bindings/cpp/include/exess/exess.hpp
new file mode 100644
index 00000000..7aa6fc5b
--- /dev/null
+++ b/subprojects/exess/bindings/cpp/include/exess/exess.hpp
@@ -0,0 +1,707 @@
+/*
+ Copyright 2021 David Robillard <d@drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#ifndef EXESS_EXESS_HPP
+#define EXESS_EXESS_HPP
+
+#include "exess/exess.h"
+
+#include <cstdint>
+#include <exception>
+#include <iostream>
+#include <string>
+#include <type_traits>
+#include <typeinfo>
+#include <vector>
+
+namespace exess {
+
+/**
+ @defgroup exesspp Exess C++ API
+ This is the C++ wrapper for the exess API.
+ @{
+*/
+
+constexpr const char* EXESS_NONNULL const xsd_uri =
+ "http://www.w3.org/2001/XMLSchema#";
+
+// FIXME: Reorganize
+using Datatype = ExessDatatype;
+using Result = ExessResult;
+
+using Duration = ExessDuration;
+using DateTime = ExessDateTime;
+using Time = ExessTime;
+using Date = ExessDate;
+
+/**
+ @defgroup exesspp_status Status
+ @copydoc exess_status
+ @{
+*/
+
+/// @copydoc ExessStatus
+enum class Status {
+ success, ///< @copydoc EXESS_SUCCESS
+ expected_end, ///< @copydoc EXESS_EXPECTED_END
+ expected_boolean, ///< @copydoc EXESS_EXPECTED_BOOLEAN
+ expected_integer, ///< @copydoc EXESS_EXPECTED_INTEGER
+ expected_duration, ///< @copydoc EXESS_EXPECTED_DURATION
+ expected_sign, ///< @copydoc EXESS_EXPECTED_SIGN
+ expected_digit, ///< @copydoc EXESS_EXPECTED_DIGIT
+ expected_colon, ///< @copydoc EXESS_EXPECTED_COLON
+ expected_dash, ///< @copydoc EXESS_EXPECTED_DASH
+ expected_time_sep, ///< @copydoc EXESS_EXPECTED_TIME_SEP
+ expected_time_tag, ///< @copydoc EXESS_EXPECTED_TIME_TAG
+ expected_date_tag, ///< @copydoc EXESS_EXPECTED_DATE_TAG
+ expected_hex, ///< @copydoc EXESS_EXPECTED_HEX
+ expected_base64, ///< @copydoc EXESS_EXPECTED_BASE64
+ bad_order, ///< @copydoc EXESS_BAD_ORDER
+ bad_value, ///< @copydoc EXESS_BAD_VALUE
+ out_of_range, ///< @copydoc EXESS_OUT_OF_RANGE
+ no_space, ///< @copydoc EXESS_NO_SPACE
+ would_reduce_precision, ///< @copydoc EXESS_WOULD_REDUCE_PRECISION
+ would_round, ///< @copydoc EXESS_WOULD_ROUND
+ would_truncate, ///< @copydoc EXESS_WOULD_TRUNCATE
+ unsupported, ///< @copydoc EXESS_UNSUPPORTED
+};
+
+/// @copydoc exess_strerror
+inline const char* EXESS_NONNULL
+strerror(const Status status)
+{
+ return exess_strerror(static_cast<ExessStatus>(status));
+}
+
+inline std::ostream&
+operator<<(std::ostream& stream, const Status status)
+{
+ return stream << strerror(status);
+}
+
+/**
+ @}
+
+ Datatype Traits
+*/
+
+namespace detail {
+
+template<class T>
+struct DatatypeTraits {};
+
+template<>
+struct DatatypeTraits<bool> {
+ static constexpr auto datatype = EXESS_BOOLEAN;
+ static constexpr auto read_function = exess_read_boolean;
+ static constexpr auto write_function = exess_write_boolean;
+};
+
+template<>
+struct DatatypeTraits<double> {
+ static constexpr auto datatype = EXESS_DOUBLE;
+ static constexpr auto read_function = exess_read_double;
+ static constexpr auto write_function = exess_write_double;
+};
+
+template<>
+struct DatatypeTraits<float> {
+ static constexpr auto datatype = EXESS_FLOAT;
+ static constexpr auto read_function = exess_read_float;
+ static constexpr auto write_function = exess_write_float;
+};
+
+template<>
+struct DatatypeTraits<int64_t> {
+ static constexpr auto datatype = EXESS_LONG;
+ static constexpr auto read_function = exess_read_long;
+ static constexpr auto write_function = exess_write_long;
+};
+
+template<>
+struct DatatypeTraits<int32_t> {
+ static constexpr auto datatype = EXESS_INT;
+ static constexpr auto read_function = exess_read_int;
+ static constexpr auto write_function = exess_write_int;
+};
+
+template<>
+struct DatatypeTraits<int16_t> {
+ static constexpr auto datatype = EXESS_SHORT;
+ static constexpr auto read_function = exess_read_short;
+ static constexpr auto write_function = exess_write_short;
+};
+
+template<>
+struct DatatypeTraits<int8_t> {
+ static constexpr auto datatype = EXESS_BYTE;
+ static constexpr auto read_function = exess_read_byte;
+ static constexpr auto write_function = exess_write_byte;
+};
+
+template<>
+struct DatatypeTraits<uint64_t> {
+ static constexpr auto datatype = EXESS_ULONG;
+ static constexpr auto read_function = exess_read_ulong;
+ static constexpr auto write_function = exess_write_ulong;
+};
+
+template<>
+struct DatatypeTraits<uint32_t> {
+ static constexpr auto datatype = EXESS_UINT;
+ static constexpr auto read_function = exess_read_uint;
+ static constexpr auto write_function = exess_write_uint;
+};
+
+template<>
+struct DatatypeTraits<uint16_t> {
+ static constexpr auto datatype = EXESS_USHORT;
+ static constexpr auto read_function = exess_read_ushort;
+ static constexpr auto write_function = exess_write_ushort;
+};
+
+template<>
+struct DatatypeTraits<uint8_t> {
+ static constexpr auto datatype = EXESS_UBYTE;
+ static constexpr auto read_function = exess_read_ubyte;
+ static constexpr auto write_function = exess_write_ubyte;
+};
+
+template<>
+struct DatatypeTraits<Duration> {
+ static constexpr auto datatype = EXESS_DURATION;
+ static constexpr auto read_function = exess_read_duration;
+ static constexpr auto write_function = exess_write_duration;
+};
+
+template<>
+struct DatatypeTraits<DateTime> {
+ static constexpr auto datatype = EXESS_DATETIME;
+ static constexpr auto read_function = exess_read_datetime;
+ static constexpr auto write_function = exess_write_datetime;
+};
+
+template<>
+struct DatatypeTraits<Time> {
+ static constexpr auto datatype = EXESS_TIME;
+ static constexpr auto read_function = exess_read_time;
+ static constexpr auto write_function = exess_write_time;
+};
+
+template<>
+struct DatatypeTraits<Date> {
+ static constexpr auto datatype = EXESS_DATE;
+ static constexpr auto read_function = exess_read_date;
+ static constexpr auto write_function = exess_write_date;
+};
+
+} // namespace detail
+
+/**
+ @defgroup exesspp_read_write Reading and Writing
+ @{
+*/
+
+/**
+ Read a value from a string.
+
+ @param out Set to the parsed value.
+ @param str String input.
+ @return The `count` of characters read, and a `status` code.
+*/
+template<class T>
+inline Result
+read(T* EXESS_NONNULL out, const char* EXESS_NONNULL str)
+{
+ return detail::DatatypeTraits<T>::read_function(out, str);
+}
+
+/**
+ Write a value to a canonical string.
+
+ @param value Value to write.
+ @param buf_size The size of `buf` in bytes.
+ @param buf Output buffer, or null to only measure.
+
+ @return The `count` of characters in the output, and `status`
+ #Status::success, or #Status::no_space if the buffer is too small.
+*/
+template<class T>
+inline Result
+write(const T& value, const size_t buf_size, char* EXESS_NONNULL buf)
+{
+ return detail::DatatypeTraits<T>::write_function(value, buf_size, buf);
+}
+
+/**
+ Return a value as a string.
+
+ This is a wrapper for write() that allocates a new string of the appropriate
+ length, writes the value to it, and returns it.
+
+ @param value The value to convert to a string.
+ @return The value as a string, or the empty string on error.
+*/
+template<class T>
+inline std::string
+to_string(const T& value)
+{
+ auto r = detail::DatatypeTraits<T>::write_function(value, 0, nullptr);
+ if (r.status) {
+ return {};
+ }
+
+ // In C++17, std::string::data() allows mutable access
+#if __cplusplus >= 201703L
+ std::string string(r.count, ' ');
+ r = detail::DatatypeTraits<T>::write_function(
+ value, r.count + 1, string.data());
+
+ // Before, we had to allocate somewhere else
+#else
+ std::vector<char> buf(r.count + 1, '\0');
+ r = detail::DatatypeTraits<T>::write_function(value, r.count + 1, buf.data());
+
+ std::string string(buf.data());
+#endif
+
+ return r.status ? "" : string;
+}
+
+/**
+ @}
+*/
+
+// TODO: hex, base64
+
+struct Variant : ExessVariant {
+ explicit Variant(const Status status) noexcept
+ : ExessVariant{}
+ {
+ datatype = EXESS_NOTHING;
+ value.as_status = static_cast<ExessStatus>(status);
+ }
+
+ explicit Variant(const bool v) noexcept
+ : ExessVariant{}
+ {
+ datatype = EXESS_BOOLEAN;
+ value.as_bool = v;
+ }
+
+ explicit Variant(const double v) noexcept
+ : ExessVariant{}
+ {
+ datatype = EXESS_DOUBLE;
+ value.as_double = v;
+ }
+
+ explicit Variant(const float v) noexcept
+ : ExessVariant{}
+ {
+ datatype = EXESS_FLOAT;
+ value.as_float = v;
+ }
+
+ explicit Variant(const int64_t v) noexcept
+ : ExessVariant{}
+ {
+ datatype = EXESS_LONG;
+ value.as_long = v;
+ }
+
+ explicit Variant(const int32_t v) noexcept
+ : ExessVariant{}
+ {
+ datatype = EXESS_INT;
+ value.as_int = v;
+ }
+
+ explicit Variant(const int16_t v) noexcept
+ : ExessVariant{}
+ {
+ datatype = EXESS_SHORT;
+ value.as_short = v;
+ }
+
+ explicit Variant(const int8_t v) noexcept
+ : ExessVariant{}
+ {
+ datatype = EXESS_BYTE;
+ value.as_byte = v;
+ }
+
+ explicit Variant(const uint64_t v) noexcept
+ : ExessVariant{}
+ {
+ datatype = EXESS_ULONG;
+ value.as_ulong = v;
+ }
+
+ explicit Variant(const uint32_t v) noexcept
+ : ExessVariant{}
+ {
+ datatype = EXESS_UINT;
+ value.as_uint = v;
+ }
+
+ explicit Variant(const uint16_t v) noexcept
+ : ExessVariant{}
+ {
+ datatype = EXESS_USHORT;
+ value.as_ushort = v;
+ }
+
+ explicit Variant(const uint8_t v) noexcept
+ : ExessVariant{}
+ {
+ datatype = EXESS_UBYTE;
+ value.as_ubyte = v;
+ }
+
+ explicit Variant(const Duration v) noexcept
+ : ExessVariant{}
+ {
+ datatype = EXESS_DURATION;
+ value.as_duration = v;
+ }
+
+ explicit Variant(const DateTime v) noexcept
+ : ExessVariant{}
+ {
+ datatype = EXESS_DATETIME;
+ value.as_datetime = v;
+ }
+
+ explicit Variant(const Time v) noexcept
+ : ExessVariant{}
+ {
+ datatype = EXESS_TIME;
+ value.as_time = v;
+ }
+
+ explicit Variant(const Date v) noexcept
+ : ExessVariant{}
+ {
+ datatype = EXESS_DATE;
+ value.as_date = v;
+ }
+};
+
+inline Variant
+make_decimal(const double value) noexcept
+{
+ Variant result{value};
+ result.datatype = EXESS_DECIMAL;
+ return result;
+}
+
+inline Variant
+make_integer(const int64_t value) noexcept
+{
+ Variant result{value};
+ result.datatype = EXESS_INTEGER;
+ return result;
+}
+
+inline Variant
+make_non_positive_integer(const int64_t value) noexcept
+{
+ if (value > 0) {
+ return Variant{Status::out_of_range};
+ }
+
+ Variant result{value};
+ result.datatype = EXESS_NON_POSITIVE_INTEGER;
+ return result;
+}
+
+inline Variant
+make_negative_integer(const int64_t value) noexcept
+{
+ if (value >= 0) {
+ return Variant{Status::out_of_range};
+ }
+
+ Variant result{value};
+ result.datatype = EXESS_NEGATIVE_INTEGER;
+ return result;
+}
+
+inline Variant
+make_non_negative_integer(const uint64_t value) noexcept
+{
+ Variant result{value};
+ result.datatype = EXESS_NON_NEGATIVE_INTEGER;
+ return result;
+}
+
+inline Variant
+make_positive_integer(const uint64_t value) noexcept
+{
+ if (value == 0) {
+ return Variant{Status::out_of_range};
+ }
+
+ Variant result{value};
+ result.datatype = EXESS_POSITIVE_INTEGER;
+ return result;
+}
+
+// enum class Datatype {
+// NOTHING, ///< Sentinel for unknown datatypes or errors
+// BOOLEAN, ///< xsd:boolean (see @ref exess_boolean)
+// DECIMAL, ///< xsd:decimal (see @ref exess_decimal)
+// DOUBLE, ///< xsd:double (see @ref exess_double)
+// FLOAT, ///< xsd:float (see @ref exess_float)
+// INTEGER, ///< xsd:integer (see @ref exess_long)
+// NON_POSITIVE_INTEGER, ///< xsd:nonPositiveInteger (see @ref exess_long)
+// NEGATIVE_INTEGER, ///< xsd:negativeInteger (see @ref exess_long)
+// LONG, ///< xsd:long (see @ref exess_long)
+// INT, ///< xsd:integer (see @ref exess_int)
+// SHORT, ///< xsd:short (see @ref exess_short)
+// BYTE, ///< xsd:byte (see @ref exess_byte)
+// NON_NEGATIVE_INTEGER, ///< xsd:nonNegativeInteger (see @ref exess_ulong)
+// ULONG, ///< xsd:unsignedLong (see @ref exess_ulong)
+// UINT, ///< xsd:unsignedInt (see @ref exess_uint)
+// USHORT, ///< xsd:unsignedShort (see @ref exess_ushort)
+// UBYTE, ///< xsd:unsignedByte (see @ref exess_ubyte)
+// POSITIVE_INTEGER, ///< xsd:positiveInteger (see @ref exess_ulong)
+// DURATION, ///< xsd:duration (see @ref exess_duration)
+// DATETIME, ///< xsd:dateTime (see @ref exess_datetime)
+// TIME, ///< xsd:time (see @ref exess_time)
+// DATE, ///< xsd:date (see @ref exess_date)
+// HEX, ///< xsd:hexBinary (see @ref exess_hex)
+// BASE64, ///< xsd:base64Binary (see @ref exess_base64)
+// }
+
+//
+
+/**
+ Return a pointer to the value of `variant` if it has type `T`.
+
+ This is safe to call on any variant, and will only return a pointer to the
+ value if the variant has the matching type and the value is valid to read.
+
+ @return A pointer to the value in `variant`, or null.
+*/
+template<class T>
+constexpr const T* EXESS_NULLABLE
+get_if(const Variant& variant) noexcept
+{
+ return (variant.datatype == detail::DatatypeTraits<T>::datatype)
+ ? reinterpret_cast<const T*>(&variant.value)
+ : nullptr;
+}
+
+/**
+ Return a pointer to the Status value in `variant` if it exists.
+*/
+template<>
+constexpr const Status* EXESS_NULLABLE
+get_if<Status>(const Variant& variant) noexcept
+{
+ return variant.datatype == EXESS_NOTHING
+ ? reinterpret_cast<const Status*>(&variant.value.as_status)
+ : nullptr;
+}
+
+/**
+ Return a pointer to the value of `variant` if it is a `double`.
+
+ This specialization works for both #EXESS_DECIMAL and #EXESS_DOUBLE values.
+*/
+template<>
+constexpr const double* EXESS_NULLABLE
+get_if<double>(const Variant& variant) noexcept
+{
+ return (variant.datatype == EXESS_DECIMAL || variant.datatype == EXESS_DOUBLE)
+ ? &variant.value.as_double
+ : nullptr;
+}
+
+/**
+ Return a pointer to the value of `variant` if it is an `int64_t` (long).
+
+ This specialization works for #EXESS_INTEGER, #EXESS_NON_POSITIVE_INTEGER,
+ #EXESS_NEGATIVE_INTEGER, and #EXESS_LONG values.
+*/
+template<>
+constexpr const int64_t* EXESS_NULLABLE
+get_if<int64_t>(const Variant& variant) noexcept
+{
+ return (variant.datatype >= EXESS_INTEGER && variant.datatype <= EXESS_LONG)
+ ? &variant.value.as_long
+ : nullptr;
+}
+
+/**
+ Return a pointer to the value of `variant` if it is a `uint64_t` (ulong).
+
+ This specialization works for #EXESS_NON_NEGATIVE_INTEGER, #EXESS_ULONG, and
+ #EXESS_POSITIVE_INTEGER values.
+*/
+template<>
+constexpr const uint64_t* EXESS_NULLABLE
+get_if<uint64_t>(const Variant& variant) noexcept
+{
+ return (variant.datatype == EXESS_NON_NEGATIVE_INTEGER ||
+ variant.datatype == EXESS_ULONG ||
+ variant.datatype == EXESS_POSITIVE_INTEGER)
+ ? &variant.value.as_ulong
+ : nullptr;
+}
+
+/**
+ Return the value of a variant with the given type.
+*/
+template<class T>
+const T&
+get(const Variant& variant)
+{
+ if (const T* const pointer = get_if<T>(variant)) {
+ return *pointer;
+ }
+
+ if (variant.datatype == EXESS_NOTHING) {
+ throw std::runtime_error{"Empty exess::Variant access"};
+ }
+
+ throw std::runtime_error{
+ std::string("Bad exess::Variant access: ") +
+ std::string(exess_datatype_uri(variant.datatype) + strlen(xsd_uri)) +
+ " from " + typeid(T).name()};
+}
+
+template<class T>
+constexpr size_t
+max_length()
+{
+ return 0u;
+}
+
+template<>
+constexpr size_t
+max_length<bool>()
+{
+ return EXESS_MAX_BOOLEAN_LENGTH;
+}
+
+template<>
+constexpr size_t
+max_length<double>()
+{
+ return EXESS_MAX_DOUBLE_LENGTH;
+}
+
+template<>
+constexpr size_t
+max_length<float>()
+{
+ return EXESS_MAX_FLOAT_LENGTH;
+}
+
+template<>
+constexpr size_t
+max_length<int64_t>()
+{
+ return EXESS_MAX_LONG_LENGTH;
+}
+
+template<>
+constexpr size_t
+max_length<int32_t>()
+{
+ return EXESS_MAX_INT_LENGTH;
+}
+
+template<>
+constexpr size_t
+max_length<int16_t>()
+{
+ return EXESS_MAX_SHORT_LENGTH;
+}
+
+template<>
+constexpr size_t
+max_length<int8_t>()
+{
+ return EXESS_MAX_BYTE_LENGTH;
+}
+
+template<>
+constexpr size_t
+max_length<uint64_t>()
+{
+ return EXESS_MAX_ULONG_LENGTH;
+}
+
+template<>
+constexpr size_t
+max_length<uint32_t>()
+{
+ return EXESS_MAX_UINT_LENGTH;
+}
+
+template<>
+constexpr size_t
+max_length<uint16_t>()
+{
+ return EXESS_MAX_USHORT_LENGTH;
+}
+
+template<>
+constexpr size_t
+max_length<uint8_t>()
+{
+ return EXESS_MAX_UBYTE_LENGTH;
+}
+
+template<>
+constexpr size_t
+max_length<Duration>()
+{
+ return EXESS_MAX_DURATION_LENGTH;
+}
+
+template<>
+constexpr size_t
+max_length<DateTime>()
+{
+ return EXESS_MAX_DATETIME_LENGTH;
+}
+
+template<>
+constexpr size_t
+max_length<Time>()
+{
+ return EXESS_MAX_TIME_LENGTH;
+}
+
+template<>
+constexpr size_t
+max_length<Date>()
+{
+ return EXESS_MAX_DATE_LENGTH;
+}
+
+/**
+ @}
+*/
+
+} // namespace exess
+
+#endif // EXESS_EXESS_HPP