diff options
Diffstat (limited to 'subprojects/exess/bindings/cpp')
-rw-r--r-- | subprojects/exess/bindings/cpp/include/exess/exess.hpp | 707 | ||||
-rw-r--r-- | subprojects/exess/bindings/cpp/meson.build | 54 | ||||
-rw-r--r-- | subprojects/exess/bindings/cpp/test/.clang-tidy | 17 | ||||
-rw-r--r-- | subprojects/exess/bindings/cpp/test/meson.build | 8 | ||||
-rw-r--r-- | subprojects/exess/bindings/cpp/test/test_exess_hpp.cpp | 221 |
5 files changed, 1007 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 diff --git a/subprojects/exess/bindings/cpp/meson.build b/subprojects/exess/bindings/cpp/meson.build new file mode 100644 index 00000000..61e3616e --- /dev/null +++ b/subprojects/exess/bindings/cpp/meson.build @@ -0,0 +1,54 @@ +add_languages(['cpp']) +cpp = meson.get_compiler('cpp') + +# Set ultra strict warnings for developers, if requested +if get_option('strict') + if cpp.get_id() == 'clang' + cpp_suppressions = [ + '-Wno-c++14-extensions', + '-Wno-c++98-compat', + '-Wno-c++98-compat-pedantic', + '-Wno-documentation-unknown-command', + '-Wno-nullability-extension', + '-Wno-padded', + ] + + elif cpp.get_id() == 'gcc' + cpp_suppressions = [ + '-Wno-inline', + '-Wno-padded', + '-Wno-switch-default', + '-Wno-unused-const-variable', + '-Wno-useless-cast', + ] + + elif cpp.get_id() == 'msvc' + cpp_suppressions = [ + '/wd4028', # formal parameter different from declaration + '/wd4204', # nonstandard extension: non-constant aggregate initializer + '/wd4706', # assignment within conditional expression + '/wd4710', # function not inlined + '/wd4711', # function selected for automatic inline expansion + '/wd4820', # padding added after data member + '/wd5045', # will insert Spectre mitigation + ] + endif + +else + if cpp.get_id() == 'clang' + cpp_suppressions = [ + '-Wno-nullability-extension', + ] + endif +endif + +exess_cpp_args = cc.get_supported_arguments(cpp_suppressions) + +cpp_headers = ['include/exess/exess.hpp'] +cpp_header_files = files(cpp_headers) + +exess_hpp_dep = declare_dependency( + dependencies: exess_dep, + include_directories: include_directories('include')) + +subdir('test') diff --git a/subprojects/exess/bindings/cpp/test/.clang-tidy b/subprojects/exess/bindings/cpp/test/.clang-tidy new file mode 100644 index 00000000..a0307839 --- /dev/null +++ b/subprojects/exess/bindings/cpp/test/.clang-tidy @@ -0,0 +1,17 @@ +Checks: > + *, + -*-magic-numbers, + -*-uppercase-literal-suffix, + -clang-analyzer-nullability.NullableDereferenced, + -cppcoreguidelines-pro-bounds-array-to-pointer-decay, + -cppcoreguidelines-pro-type-reinterpret-cast, + -fuchsia-default-arguments-calls, + -fuchsia-overloaded-operator, + -hicpp-no-array-decay, + -llvmlibc-*, + -misc-non-private-member-variables-in-classes, + -modernize-unary-static-assert, + -modernize-use-trailing-return-type, +WarningsAsErrors: '*' +HeaderFilterRegex: '.*' +FormatStyle: file diff --git a/subprojects/exess/bindings/cpp/test/meson.build b/subprojects/exess/bindings/cpp/test/meson.build new file mode 100644 index 00000000..e04c21c9 --- /dev/null +++ b/subprojects/exess/bindings/cpp/test/meson.build @@ -0,0 +1,8 @@ +test('exess_hpp', + executable('test_exess_hpp', + 'test_exess_hpp.cpp', + cpp_args: exess_cpp_args + prog_args, + dependencies: [exess_hpp_dep], + include_directories: include_directories('../../../test', + '../../../src')), + suite: 'cpp') diff --git a/subprojects/exess/bindings/cpp/test/test_exess_hpp.cpp b/subprojects/exess/bindings/cpp/test/test_exess_hpp.cpp new file mode 100644 index 00000000..e940f4b1 --- /dev/null +++ b/subprojects/exess/bindings/cpp/test/test_exess_hpp.cpp @@ -0,0 +1,221 @@ +/* + 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. +*/ + +#undef NDEBUG + +#include "float_test_data.h" + +#include "exess/exess.h" +#include "exess/exess.hpp" + +#include <cassert> +#include <cstdint> +#include <cstdlib> +#include <iostream> +#include <sstream> +#include <stdexcept> +#include <string> + +namespace exess { +namespace { + +void +test_status() +{ + std::ostringstream ss; + ss << Status::no_space; + + std::cerr << ss.str(); + assert(ss.str() == "Insufficient space"); +} + +void +test_read() +{ + bool a_bool = false; + assert(!read(&a_bool, "true").status); + assert(a_bool == true); + + double a_double = 0.0; + assert(!read(&a_double, "4.2E16").status); + assert(double_matches(a_double, 42000000000000000.0)); + + float a_float = 0.0f; + assert(!read(&a_float, "4.2E7").status); + assert(float_matches(a_float, 42000000.0f)); + + int64_t a_long = 0; + assert(!read(&a_long, "-4200000000").status); + assert(a_long == -4200000000); + + int32_t a_int = 0; + assert(!read(&a_int, "-42000").status); + assert(a_int == -42000); + + int16_t a_short = 0; + assert(!read(&a_short, "-420").status); + assert(a_short == -420); + + int8_t a_byte = 0; + assert(!read(&a_byte, "-42").status); + assert(a_byte == -42); + + uint64_t a_ulong = 0u; + assert(!read(&a_ulong, "4200000000").status); + assert(a_ulong == 4200000000); + + uint32_t a_uint = 0u; + assert(!read(&a_uint, "42000").status); + assert(a_uint == 42000); + + uint16_t a_ushort = 0u; + assert(!read(&a_ushort, "420").status); + assert(a_ushort == 420); + + uint8_t a_ubyte = 0u; + assert(!read(&a_ubyte, "42").status); + assert(a_ubyte == 42); +} + +void +test_to_string() +{ + assert(to_string(true) == "true"); + assert(to_string(42000000000000000.0) == "4.2E16"); + assert(to_string(42000000.0f) == "4.2E7"); + assert(to_string(int64_t(-4200000000)) == "-4200000000"); + assert(to_string(int32_t(-42000)) == "-42000"); + assert(to_string(int16_t(-420)) == "-420"); + assert(to_string(int8_t(-42)) == "-42"); + assert(to_string(uint64_t(4200000000u)) == "4200000000"); + assert(to_string(uint32_t(42000u)) == "42000"); + assert(to_string(uint16_t(420u)) == "420"); + assert(to_string(uint8_t(42u)) == "42"); +} + +void +test_max_length() +{ + static_assert(max_length<bool>() == EXESS_MAX_BOOLEAN_LENGTH, ""); + static_assert(max_length<double>() == EXESS_MAX_DOUBLE_LENGTH, ""); + static_assert(max_length<float>() == EXESS_MAX_FLOAT_LENGTH, ""); + static_assert(max_length<int64_t>() == EXESS_MAX_LONG_LENGTH, ""); + static_assert(max_length<int32_t>() == EXESS_MAX_INT_LENGTH, ""); + static_assert(max_length<int16_t>() == EXESS_MAX_SHORT_LENGTH, ""); + static_assert(max_length<int8_t>() == EXESS_MAX_BYTE_LENGTH, ""); + static_assert(max_length<uint64_t>() == EXESS_MAX_ULONG_LENGTH, ""); + static_assert(max_length<uint32_t>() == EXESS_MAX_UINT_LENGTH, ""); + static_assert(max_length<uint16_t>() == EXESS_MAX_USHORT_LENGTH, ""); + static_assert(max_length<uint8_t>() == EXESS_MAX_UBYTE_LENGTH, ""); + static_assert(max_length<Duration>() == EXESS_MAX_DURATION_LENGTH, ""); + static_assert(max_length<DateTime>() == EXESS_MAX_DATETIME_LENGTH, ""); + static_assert(max_length<Date>() == EXESS_MAX_DATE_LENGTH, ""); + static_assert(max_length<Time>() == EXESS_MAX_TIME_LENGTH, ""); +} + +template<class T> +void +check_get_throws(const Variant& variant) +{ + bool caught = false; + + try { + get<T>(variant); + } catch (const std::runtime_error&) { + caught = true; + } + + assert(caught); +} + +void +test_variant() +{ + const auto a_nothing = Variant{Status::success}; + const auto a_bool = Variant{true}; + const auto a_decimal = make_decimal(1.2); + const auto a_double = Variant{3.4}; + const auto a_float = Variant{5.6f}; + const auto a_integer = make_integer(7); + const auto a_non_positive_integer = make_non_positive_integer(-8); + const auto a_negative_integer = make_negative_integer(-9); + const auto a_long = Variant{int64_t(10)}; + const auto a_int = Variant{int32_t(11)}; + const auto a_short = Variant{int16_t(12)}; + const auto a_byte = Variant{int8_t(13)}; + const auto a_non_negative_integer = make_non_negative_integer(14u); + const auto a_ulong = Variant{uint64_t(15u)}; + const auto a_uint = Variant{uint32_t(16u)}; + const auto a_ushort = Variant{uint16_t(17u)}; + const auto a_ubyte = Variant{uint8_t(18u)}; + const auto a_positive_integer = make_positive_integer(19u); + + try { + assert(get<Status>(a_nothing) == Status::success); + assert(get<bool>(a_bool) == true); + assert(double_matches(get<double>(a_decimal), 1.2)); + assert(double_matches(get<double>(a_double), 3.4)); + assert(float_matches(get<float>(a_float), 5.6f)); + assert(get<int64_t>(a_integer) == 7); + assert(get<int64_t>(a_non_positive_integer) == -8); + assert(get<int64_t>(a_negative_integer) == -9); + assert(get<int64_t>(a_long) == 10); + assert(get<int32_t>(a_int) == 11); + assert(get<int16_t>(a_short) == 12); + assert(get<int8_t>(a_byte) == 13); + assert(get<uint64_t>(a_non_negative_integer) == 14u); + assert(get<uint64_t>(a_ulong) == 15u); + assert(get<uint32_t>(a_uint) == 16u); + assert(get<uint16_t>(a_ushort) == 17u); + assert(get<uint8_t>(a_ubyte) == 18u); + assert(get<uint64_t>(a_positive_integer) == 19u); + } catch (const std::runtime_error&) { + abort(); + } + + check_get_throws<int>(a_nothing); + check_get_throws<int>(a_bool); + check_get_throws<int>(a_double); + check_get_throws<int>(a_float); + check_get_throws<int>(a_integer); + check_get_throws<int>(a_non_positive_integer); + check_get_throws<int>(a_negative_integer); + check_get_throws<int>(a_long); + check_get_throws<bool>(a_int); + check_get_throws<int>(a_short); + check_get_throws<int>(a_byte); + check_get_throws<int>(a_non_negative_integer); + check_get_throws<int>(a_ulong); + check_get_throws<int>(a_uint); + check_get_throws<int>(a_ushort); + check_get_throws<int>(a_ubyte); + check_get_throws<int>(a_positive_integer); +} + +} // namespace +} // namespace exess + +int +main() +{ + exess::test_status(); + exess::test_read(); + exess::test_to_string(); + exess::test_max_length(); + exess::test_variant(); + + return 0; +} |