From 17acd742e372a94667645fbd2c0df1fd680db7cc Mon Sep 17 00:00:00 2001 From: Jessica James Date: Sat, 29 Sep 2018 11:01:19 -0500 Subject: [PATCH] Added: `object`, `parser`, `config` TODO: add tests for `parser`, `config` --- src/common/CMakeLists.txt | 2 +- src/common/config.cpp | 147 +++++ src/common/object.cpp | 102 +++ src/common/parser/parser.cpp | 42 ++ src/common/parser/parser_manager.cpp | 121 ++++ src/common/serialize.cpp | 69 ++ src/include/config.hpp | 68 ++ src/include/impl/parser_manager.hpp | 77 +++ src/include/object.hpp | 436 +++++++++++++ src/include/parser.hpp | 58 ++ src/include/serialize.hpp | 41 ++ src/include/util.hpp | 215 ++++++ src/test/CMakeLists.txt | 2 +- src/test/object.cpp | 932 +++++++++++++++++++++++++++ src/test/thread_pool.cpp | 20 +- src/test/util.cpp | 32 + 16 files changed, 2351 insertions(+), 13 deletions(-) create mode 100644 src/common/config.cpp create mode 100644 src/common/object.cpp create mode 100644 src/common/parser/parser.cpp create mode 100644 src/common/parser/parser_manager.cpp create mode 100644 src/common/serialize.cpp create mode 100644 src/include/config.hpp create mode 100644 src/include/impl/parser_manager.hpp create mode 100644 src/include/object.hpp create mode 100644 src/include/parser.hpp create mode 100644 src/include/serialize.hpp create mode 100644 src/include/util.hpp create mode 100644 src/test/object.cpp create mode 100644 src/test/util.cpp diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 37573d3..19b64e7 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -3,6 +3,6 @@ cmake_minimum_required(VERSION 3.8) # Setup source files include_directories(../include) set(SOURCE_FILES - timer/timer.cpp timer/timer_manager.cpp thread_pool.cpp timer/timer_context.cpp timer/cancel_token.cpp timer/synchronized_timer.cpp) + timer/timer.cpp timer/timer_manager.cpp thread_pool.cpp timer/timer_context.cpp timer/cancel_token.cpp timer/synchronized_timer.cpp object.cpp parser/parser.cpp parser/parser_manager.cpp config.cpp serialize.cpp) add_library(jessilib ${SOURCE_FILES}) diff --git a/src/common/config.cpp b/src/common/config.cpp new file mode 100644 index 0000000..b899190 --- /dev/null +++ b/src/common/config.cpp @@ -0,0 +1,147 @@ +/** + * Copyright (C) 2018 Jessica James. + * + * 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. + * + * THE 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. + * + * Written by Jessica James + */ + +#include "config.hpp" +#include +#include +#include "assert.hpp" +#include "serialize.hpp" + +namespace jessilib { + +/** Error types */ + +config::file_error::file_error() + : std::runtime_error{ std::strerror(errno) } { + // Empty ctor body +} + +config::file_error::file_error(const std::filesystem::path& in_filename) + : std::runtime_error{ "Error openening file \"" + std::string{ in_filename } + "\". Error: " + std::strerror(errno) } { + // Empty ctor body +}; + +/** config */ + +object config::data() const { + std::shared_lock guard{ m_mutex }; + return m_data; +} + +std::filesystem::path config::filename() const { + std::shared_lock guard{ m_mutex }; + return m_filename; +} + +std::string config::format() const { + std::shared_lock guard{ m_mutex }; + return m_format; +} + +/** Modifiers */ +void config::set_data(const object& in_data) { + std::lock_guard guard{ m_mutex }; + m_data = in_data; +} + +/** File I/O */ +void config::load(const std::filesystem::path& in_filename, const std::string& in_format) { + jessilib_assert(!in_filename.empty()); + std::lock_guard guard{ m_mutex }; + + // Determine format + m_filename = in_filename; + m_format = get_format(m_filename, in_format); + + // Load + m_data = read_object(m_filename, m_format); +} + +void config::reload() { + std::lock_guard guard{ m_mutex }; + + if (jessilib_debug_assert(!m_filename.empty()) + && jessilib_debug_assert(!m_format.empty())) { + // Load data from disk + m_data = read_object(m_filename, m_format); + } +} + +void config::write() const { + std::shared_lock guard{ m_mutex }; + + if (jessilib_debug_assert(!m_filename.empty()) + && jessilib_debug_assert(!m_format.empty())) { + // Write data to disk + write_object(m_data, m_filename, m_format); + } +} + +void config::write(const std::filesystem::path& in_filename , const std::string& in_format) { + jessilib_assert(!in_filename.empty()); + std::lock_guard guard{ m_mutex }; + + // Setup + m_filename = in_filename; + m_format = get_format(m_filename, in_format); + + // Write + write_object(m_data, m_filename, m_format); +} + +/** Static File I/O */ +object config::read_object(const std::filesystem::path& in_filename, const std::string& in_format) { + // Open up file for reading + std::ifstream file{ in_filename, std::ios::in | std::ios::binary }; + if (!file) { + // Failed to open the file; throw file_error + throw file_error( in_filename ); + } + + // Deserialize1 + return deserialize_object(file, in_format); +} + +void config::write_object(const object& in_object, const std::filesystem::path& in_filename, const std::string& in_format) { + // Open up file for writing + std::ofstream file{ in_filename, std::ios::out | std::ios::binary }; + if (!file) { + // Failed to open the file; throw file_error + throw file_error( in_filename ); + } + + // Deserialize1 + return serialize_object(file, in_object, in_format); +} + +std::string get_format(const std::filesystem::path& in_filename, const std::string& in_format) { + if (!in_format.empty()) { + // Use specified format + return in_format; + } + + // Try to determine format from filename + std::string extension = in_filename.extension(); + if jessilib_assert(!extension.empty()) { + return extension.substr(1); + } + + return extension; +} + +} // namespace jessilib diff --git a/src/common/object.cpp b/src/common/object.cpp new file mode 100644 index 0000000..a07a7c4 --- /dev/null +++ b/src/common/object.cpp @@ -0,0 +1,102 @@ +/** + * Copyright (C) 2018 Jessica James. + * + * 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. + * + * THE 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. + * + * Written by Jessica James + */ + +#include "object.hpp" + +namespace jessilib { + +object::object(const object& in_object) { + m_value = in_object.m_value; +} + +object::object(object&& in_object) { + m_value = std::move(in_object.m_value); +} + +object::object(const char* in_str) + : m_value{ std::string{ in_str } } { + // Empty ctor body +} + +object::object(const std::string_view& in_str) + : m_value{ std::string{ in_str.begin(), in_str.end() } } { + // Empty ctor body +} + +/** Accessors */ + +bool object::null() const { + // There's something funny about variants holding nullptr_t + return std::holds_alternative(m_value); +} + +size_t object::size() const { + // If we're null, we don't have any members + if (null()) { + return 0; + } + + // Try array + { + const array_t* array = std::get_if(&m_value); + if (array != nullptr) { + return array->size(); + } + } + + // Try map + { + const map_t* map = std::get_if(&m_value); + if (map != nullptr) { + return map->size(); + } + } + + // We're a single value + return 1; +} + +const object& object::operator[](const std::string& in_key) const { + auto result = std::get_if(&m_value); + if (result != nullptr) { + auto itr = result->find(in_key); + if (itr != result->end()) { + return itr->second; + } + } + + static const object s_null_object; + return s_null_object; +} + +object& object::operator[](const std::string& in_key) { + if (null()) { + return m_value.emplace()[in_key]; + } + + auto result = std::get_if(&m_value); + if (result != nullptr) { + return result->operator[](in_key); + } + + static thread_local object s_null_object; + s_null_object.m_value.emplace(); + return s_null_object; +} + +} // namespace jessilib \ No newline at end of file diff --git a/src/common/parser/parser.cpp b/src/common/parser/parser.cpp new file mode 100644 index 0000000..1f85947 --- /dev/null +++ b/src/common/parser/parser.cpp @@ -0,0 +1,42 @@ +/** + * Copyright (C) 2018 Jessica James. + * + * 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. + * + * THE 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. + * + * Written by Jessica James + */ + +#include "parser.hpp" +#include + +namespace jessilib { + +object parser::deserialize(std::istream& in_stream) { + std::vector data; + + // Read entire stream into data + char buffer[1024]; + while(!in_stream.eof()) { + in_stream.read(buffer, sizeof(buffer)); + data.insert(data.end(), buffer, buffer + in_stream.gcount()); + } + + // Pass data to deserialize + return deserialize(std::string_view{ &data.front(), data.size() }); +} + +void parser::serialize(std::ostream& in_stream, const object& in_object) { + in_stream << serialize(in_object); +} + +} // namespace jessilib diff --git a/src/common/parser/parser_manager.cpp b/src/common/parser/parser_manager.cpp new file mode 100644 index 0000000..d606025 --- /dev/null +++ b/src/common/parser/parser_manager.cpp @@ -0,0 +1,121 @@ +/** + * Copyright (C) 2018 Jessica James. + * + * 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. + * + * THE 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. + * + * Written by Jessica James + */ + +#include "impl/parser_manager.hpp" +#include "parser.hpp" +#include "assert.hpp" + +namespace jessilib { +namespace impl { + +parser_manager::registration::registration(id in_id) + : m_id{ in_id } { + // Empty ctor body +} + +parser_manager::registration::registration(const std::string& in_format) + : m_format{ in_format } { + // Empty ctor body +} + +parser_manager::registration::registration(id in_id, const std::string& in_format) + : m_id{ in_id }, + m_format{ in_format } { + // Empty ctor body +} + +bool parser_manager::registration::operator<(const registration& rhs) const { + if (m_id != 0 && rhs.m_id) { + // Both registrations have an ID; this is a search by ID + return m_id < rhs.m_id; + } + + // One of the registrations lacked an ID; this must be a search by format + return m_format < rhs.m_format; +} + +parser_manager::id parser_manager::register_parser(std::shared_ptr in_parser, const std::string& in_format, bool in_force) { + std::lock_guard guard{ m_mutex }; + + // Check for any existing parser + auto itr = m_parsers.find(in_format); + if (itr != m_parsers.end()) { + // A parser already exists; return or replace + if (!in_force) { + return bad_id; + } + + // Remove existing parser and registration + m_parsers.erase(itr); + m_registrations.erase(registration{ in_format }); + } + + // Register our new parser + id parser_id = ++m_last_id; + m_parsers.emplace(in_format, in_parser); + m_registrations.emplace(parser_id, in_format); + + // Our parser is registered; return the parser ID we just registered + return parser_id; +} + +void parser_manager::unregister_parser(id in_id) { + std::lock_guard guard{ m_mutex }; + + // Search for registration + auto itr = m_registrations.find(registration{ in_id }); + if (itr == m_registrations.end()) { + // Parser already unregistered; do nothing + return; + } + + // Unregister + registration removed_registration = *itr; + m_registrations.erase(itr); + m_parsers.erase(removed_registration.m_format); +} + +std::shared_ptr parser_manager::find_parser(const std::string& in_format) { + std::shared_lock guard{ m_mutex }; + + // Search for the parser by key + auto itr = m_parsers.find(in_format); + if (itr != m_parsers.end()) { + // Found our parser; return + return itr->second; + } + + // No parser exists for the given format + return nullptr; +} + +void parser_manager::clear() { + std::lock_guard guard{ m_mutex }; + + m_parsers.clear(); + m_registrations.clear(); +} + +/** Singleton */ +parser_manager& parser_manager::instance() { + static parser_manager s_parser_manager{}; + return s_parser_manager; +} + +} // namespace jessilib +} // namespace jessilib diff --git a/src/common/serialize.cpp b/src/common/serialize.cpp new file mode 100644 index 0000000..8489968 --- /dev/null +++ b/src/common/serialize.cpp @@ -0,0 +1,69 @@ +/** + * Copyright (C) 2018 Jessica James. + * + * 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. + * + * THE 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. + * + * Written by Jessica James + */ + +#include "serialize.hpp" +#include "impl/parser_manager.hpp" +#include "parser.hpp" + +namespace jessilib { + +/** Exception types */ + +format_not_available::format_not_available(const std::string& in_format) + : std::runtime_error{ "Format \"" + in_format + "\"not recognized" } { + // Empty ctor body +}; + +/** Helpers */ + +std::shared_ptr get_parser(const std::string& in_format) { + auto parser = impl::parser_manager::instance().find_parser(in_format); + if (parser == nullptr) { + throw format_not_available{ in_format }; + } + + return parser; +} + +/** Deserialization */ +object deserialize_object(const std::string& in_data, const std::string& in_format) { + return deserialize_object(std::string_view{ &in_data.front(), in_data.size() }, in_format); +} + +object deserialize_object(const std::vector& in_data, const std::string& in_format) { + return deserialize_object(std::string_view{ &in_data.front(), in_data.size() }, in_format); +} + +object deserialize_object(std::string_view in_data, const std::string& in_format) { + return get_parser(in_format)->deserialize(in_data); +} + +object deserialize_object(std::istream& in_stream, const std::string& in_format) { + return get_parser(in_format)->deserialize(in_stream); +} + +/** Serialization */ +std::string serialize_object(const object& in_object, const std::string& in_format) { + return get_parser(in_format)->serialize(in_object); +} + +void serialize_object(std::ostream& in_stream, const object& in_object, const std::string& in_format) { + get_parser(in_format)->serialize(in_stream, in_object); +} + +} // namespace jessilib \ No newline at end of file diff --git a/src/include/config.hpp b/src/include/config.hpp new file mode 100644 index 0000000..eea7682 --- /dev/null +++ b/src/include/config.hpp @@ -0,0 +1,68 @@ +/** + * Copyright (C) 2018 Jessica James. + * + * 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. + * + * THE 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. + * + * Written by Jessica James + */ + +#pragma once + +#include +#include +#include +#include +#include +#include "object.hpp" + +namespace jessilib { + +class config { +public: + /** Types */ + + class file_error : public std::runtime_error { + public: + file_error(); + file_error(const std::filesystem::path& in_filename); + }; + + /** Accessors */ + object data() const; + std::filesystem::path filename() const; + std::string format() const; + + /** Modifiers */ + void set_data(const object& in_data); + + /** File I/O */ + void load(const std::filesystem::path& in_filename, const std::string& in_format = {}); + void reload(); + void write() const; + void write(const std::filesystem::path& in_filename , const std::string& in_format = {}); + + /** Static File I/O */ + static object read_object(const std::filesystem::path& in_filename, const std::string& in_format = {}); + static void write_object(const object& in_object, const std::filesystem::path& in_filename, const std::string& in_format = {}); + + /** Static helpers */ + static std::string get_format(const std::filesystem::path& in_filename, const std::string& in_format = {}); + +private: + mutable std::shared_mutex m_mutex; + object m_data; + std::string m_format; + std::filesystem::path m_filename; +}; + +} // namespace jessilib \ No newline at end of file diff --git a/src/include/impl/parser_manager.hpp b/src/include/impl/parser_manager.hpp new file mode 100644 index 0000000..7883a13 --- /dev/null +++ b/src/include/impl/parser_manager.hpp @@ -0,0 +1,77 @@ +/** + * Copyright (C) 2018 Jessica James. + * + * 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. + * + * THE 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. + * + * Written by Jessica James + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace jessilib { + +class parser; + +namespace impl { + +class parser_manager { +public: + /** Types */ + using id = size_t; + + /** Constants */ + static constexpr id bad_id = 0; + + /** Methods */ + id register_parser(std::shared_ptr in_parser, const std::string& in_format, bool in_force = false); + void unregister_parser(id in_id); + std::shared_ptr find_parser(const std::string& in_format); + void clear(); + + /** Singleton */ + static parser_manager& instance(); + +private: + class registration { + public: + registration() = default; + registration(const registration&) = default; + registration(registration&&) = default; + + explicit registration(id in_id); + explicit registration(const std::string& in_format); + registration(id in_id, const std::string& in_format); + + registration& operator=(const registration&) = default; + registration& operator=(registration&&) = default; + + bool operator<(const registration& rhs) const; + + id m_id{}; + std::string m_format; + }; + + std::shared_mutex m_mutex; + id m_last_id{}; + std::set m_registrations; // This set and map could be condensed into a bimap + std::unordered_map> m_parsers; +}; // parser_manager + +} // namespace impl +} // namespace jessilib \ No newline at end of file diff --git a/src/include/object.hpp b/src/include/object.hpp new file mode 100644 index 0000000..7932484 --- /dev/null +++ b/src/include/object.hpp @@ -0,0 +1,436 @@ +/** + * Copyright (C) 2018 Jessica James. + * + * 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. + * + * THE 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. + * + * Written by Jessica James + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include "util.hpp" + +namespace jessilib { + +class object { +public: + using array_t = std::vector; + using map_t = std::map; + + /** is_backing */ + + template + struct is_backing { + static constexpr bool value = false; + }; + + template + struct is_backing::value>::type> { + static constexpr bool value = true; + using type = bool; + }; + + template + struct is_backing::value && !std::is_same::value>::type> { + static constexpr bool value = true; + using type = intmax_t; + }; + + template + struct is_backing::value>::type> { + static constexpr bool value = true; + using type = long double; + }; + + template + struct is_backing::value>::type> { + static constexpr bool value = true; + using type = std::string; + }; + + template + struct is_backing::value>::type> { + static constexpr bool value = true; + using type = array_t; + }; + + template + struct is_backing::value>::type> { + static constexpr bool value = true; + using type = map_t; + }; + + // Standard constructors + object() = default; + object(const object& in_config); + object(object&& in_config); + ~object() = default; + + // Value constructors + template::type>::value + && !is_sequence_container::type>::value>::type* = nullptr> + object(T&& in_value) + : m_value{ typename is_backing::type>::type{ std::forward(in_value) } } { + // Empty ctor body + } + + template::type>::value + && !std::is_same::type, std::vector>::value>::type* = nullptr> + object(T&& in_value) + : m_value{ array_t{ in_value.begin(), in_value.end() } } { + // Empty ctor body + } + + // std::vector + template::type, std::vector>::value>::type* = nullptr> + object(T&& in_value) + : m_value{ array_t{} } { + auto& array = std::get(m_value); + array.reserve(in_value.size()); + + for (const auto& item : in_value) { + array.emplace_back(bool{item}); + } + } + + object(const char* in_str); + object(const std::string_view& in_str); + + // Comparison operators + bool operator==(const object& rhs) const { + return m_value == rhs.m_value; + } + + bool operator!=(const object& rhs) const { + return m_value != rhs.m_value; + } + + bool operator<(const object& rhs) const { + return m_value < rhs.m_value; + } + + bool operator>(const object& rhs) const { + return m_value > rhs.m_value; + } + + bool operator<=(const object& rhs) const { + return m_value <= rhs.m_value; + } + + bool operator>=(const object& rhs) const { + return m_value >= rhs.m_value; + } + + // Assignment operators + object& operator=(const object& in_config) = default; + object& operator=(object&& in_config) = default; + + // Non-copy/move assignment operator; forwards to set() + template::type, object>::value>::type* = nullptr> + object& operator=(T&& in) { + set(std::forward(in)); + return *this; + } + + const object& operator[](const std::string& in_key) const; + object& operator[](const std::string& in_key); + + /** Accessors */ + + bool null() const; + size_t size() const; + + template + bool has() const { + using backing_t = typename is_backing::type; + return std::holds_alternative(m_value); + } + + /** arithmetic types (numbers, bool) */ + + template::value>::type* = nullptr> + T get(T in_default_value = {}) const { + using backing_t = typename is_backing::type; + + const backing_t* result = std::get_if(&m_value); + if (result != nullptr) { + return static_cast(*result); + } + + return in_default_value; + } + + /** strings */ + + // TODO: support other basic_string types + template::value && std::is_convertible::type, T>::value>::type* = nullptr> + T get(DefaultT&& in_default_value = {}) const { + const std::string* result = std::get_if(&m_value); + if (result != nullptr) { + return *result; + } + + return std::forward(in_default_value); + } + + // TODO: support other basic_string_view types + template::value && std::is_same::type, std::string_view>::value>::type* = nullptr> + T get(DefaultT&& in_default_value) const { + const std::string* result = std::get_if(&m_value); + if (result != nullptr) { + return *result; + } + + return { in_default_value.begin(), in_default_value.end() }; + } + + /** arrays */ + + // reference getter (array_t) + template::value>::type* = nullptr> + const T& get(const T& in_default_value) const { + const array_t* result = std::get_if(&m_value); + if (result != nullptr) { + return *result; + } + + return in_default_value; + } + + // copy getter (array_t) + template::value>::type* = nullptr> + T get(T&& in_default_value = {}) const { + const array_t* result = std::get_if(&m_value); + if (result != nullptr) { + return *result; + } + + return std::move(in_default_value); + } + + // conversion getter (non-array_t) + template::value && !std::is_same::value && std::is_same::type, T>::value>::type* = nullptr> + T get(DefaultT&& in_default_value = {}) const { + using backing_t = typename is_sequence_container::type; + + const array_t* array = std::get_if(&m_value); + if (array != nullptr) { + T result; + // Expand capacity to fit values (if possible) + if constexpr (is_vector::value) { + result.reserve(array->size()); + } + + // Populate result from array + if constexpr (is_forward_list::value) { + // forward_list + if constexpr (std::is_same::value) { + // forward_list + result.insert_after(result.begin(), array->begin(), array->end()); + } + else { + for (auto itr = array->rend(); itr != array->rbegin(); ++itr) { + if (itr->has()) { + result.push_front(itr->get()); + } + } + } + } + else if constexpr (is_set::value || is_multiset::value || is_unordered_set::value || is_unordered_multiset::value) { + // set, unordered_set, multiset, unordered_multiset + if constexpr (std::is_same::value) { + // + result.insert(array->begin(), array->end()); + } + else { + for (auto& object : *array) { + if (object.has()) { + result.insert(object.get()); + } + } + } + } + else { + // vector, list, etc + for (auto& object : *array) { + if constexpr (std::is_same::value) { + // + result.push_back(object); + } + else if (object.has()) { + result.push_back(object.get()); + } + } + } + + return result; + } + + return std::forward(in_default_value); + } + + /** maps */ + + // TODO: implement in a way that does not require exposing map_t + + // reference getter (map_t) + template::value>::type* = nullptr> + const T& get(const T& in_default_value) const { + const map_t* result = std::get_if(&m_value); + if (result != nullptr) { + return *result; + } + + return in_default_value; + } + + // copy getter (map_t) + template::value>::type* = nullptr> + T get(T&& in_default_value = {}) const { + const map_t* result = std::get_if(&m_value); + if (result != nullptr) { + return *result; + } + + return std::move(in_default_value); + } + + // TODO: conversion getter (non-map_t, i.e: unordered_map) + + /** set */ + + // arithmetic types + template::value>::type* = nullptr> + void set(T in_value) { + using backing_t = typename is_backing::type; + + m_value.emplace(in_value); + } + + // string + template::type, std::string>::value>::type* = nullptr> + void set(T&& in_value) { + m_value.emplace(std::forward(in_value)); + } + + // string_view + template::value>::type* = nullptr> + void set(const T& in_value) { + m_value.emplace(in_value.begin(), in_value.end()); + } + + // array_t + template::type, array_t>::value>::type* = nullptr> + void set(T&& in_value) { + m_value.emplace(std::forward(in_value)); + } + + // is_sequence_container + template::type>::value + && !std::is_same::type, array_t>::value + && !std::is_same::type, std::vector>::value>::type* = nullptr> + void set(T&& in_value) { + m_value.emplace(in_value.begin(), in_value.end()); + } + + // std::vector + template::type, std::vector>::value>::type* = nullptr> + void set(T&& in_value) { + auto& array = m_value.emplace(); + array.reserve(in_value.size()); + + for (const auto& item : in_value) { + array.emplace_back(bool{item}); + } + } + + // object + template::type, object>::value>::type* = nullptr> + void set(T&& in_value) { + operator=(std::forward(in_value)); + } + + size_t hash() const { + return std::visit([this](auto&& value) -> size_t { + using T = typename std::decay::type; + + if constexpr (std::is_same::value) { + return 0; + } + else if constexpr (std::is_same::value) { + size_t result{}; + for (auto& obj : value) { + result += obj.hash(); + } + + return result; + } + else if constexpr (std::is_same::value) { + size_t result{}; + for (auto& pair : value) { + result += std::hash{}(pair.first); + } + + return result; + } + else { + return std::hash{}(std::forward(value)); + } + }, m_value); + } + +private: + std::variant m_value; +}; // object + +} // namespace jessilib + + +namespace std { + +template<> +struct hash { + using argument_type = jessilib::object; + using result_type = size_t; + + result_type operator()(const argument_type& in_object) const noexcept { + return in_object.hash(); + } +}; + +} // namepsace std diff --git a/src/include/parser.hpp b/src/include/parser.hpp new file mode 100644 index 0000000..a32b8ca --- /dev/null +++ b/src/include/parser.hpp @@ -0,0 +1,58 @@ +/** + * Copyright (C) 2018 Jessica James. + * + * 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. + * + * THE 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. + * + * Written by Jessica James + */ + +#pragma once + +#include +#include "object.hpp" +#include "impl/parser_manager.hpp" + +namespace jessilib { + +class parser { +public: + /** Interface methods */ + + /** + * Deserializes an object directly from a stream + * May throw: invalid_argument + * + * @param in_stream Stream to deserialize object from + * @return A valid (possibly null) object + */ + virtual object deserialize(std::istream& in_stream); + virtual object deserialize(std::string_view in_data) = 0; + virtual void serialize(std::ostream& in_stream, const object& in_object); + virtual std::string serialize(const object& in_object) = 0; +}; // parser + +template +class parser_registration { +public: + parser_registration(std::string in_format, bool in_force = false) { + m_id = impl::parser_manager::instance().register_parser(std::make_shared(), in_format, in_force); + } + + ~parser_registration() { + impl::parser_manager::instance().unregister_parser(m_id); + } + + impl::parser_manager::id m_id; +}; // parser_registration + +} // namespace jessilib \ No newline at end of file diff --git a/src/include/serialize.hpp b/src/include/serialize.hpp new file mode 100644 index 0000000..b5b2e0b --- /dev/null +++ b/src/include/serialize.hpp @@ -0,0 +1,41 @@ +/** + * Copyright (C) 2018 Jessica James. + * + * 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. + * + * THE 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. + * + * Written by Jessica James + */ + +#pragma once + +#include +#include "object.hpp" + +namespace jessilib { + +class format_not_available : public std::runtime_error { +public: + format_not_available(const std::string& in_format); +}; + +/** Deserialization */ +object deserialize_object(const std::string& in_data, const std::string& in_format); +object deserialize_object(const std::vector& in_data, const std::string& in_format); +object deserialize_object(std::string_view in_data, const std::string& in_format); +object deserialize_object(std::istream& in_stream, const std::string& in_format); + +/** Serialization */ +std::string serialize_object(const object& in_object, const std::string& in_format); +void serialize_object(std::ostream& in_stream, const object& in_object, const std::string& in_format); + +} // namespace jessilib \ No newline at end of file diff --git a/src/include/util.hpp b/src/include/util.hpp new file mode 100644 index 0000000..294e367 --- /dev/null +++ b/src/include/util.hpp @@ -0,0 +1,215 @@ +/** + * Copyright (C) 2018 Jessica James. + * + * 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. + * + * THE 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. + * + * Written by Jessica James + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** Macros */ + +#define JESSILIB_FILENAME \ + (::jessilib::impl::filename_from_string(__FILE__)) + +namespace jessilib { + +/** is_vector */ + +template +struct is_vector : std::false_type {}; + +template +struct is_vector> { + using type = T; + static constexpr bool value{ true }; + constexpr operator bool() const noexcept { return true; } + constexpr bool operator()() const noexcept { return true; } +}; + +/** is_list */ + +template +struct is_list : std::false_type {}; + +template +struct is_list> { + using type = T; + static constexpr bool value{ true }; + constexpr operator bool() const noexcept { return true; } + constexpr bool operator()() const noexcept { return true; } +}; + +/** is_forward_list */ + +template +struct is_forward_list : std::false_type {}; + +template +struct is_forward_list> { + using type = T; + static constexpr bool value{ true }; + constexpr operator bool() const noexcept { return true; } + constexpr bool operator()() const noexcept { return true; } +}; + +/** is_set */ + +template +struct is_set : std::false_type {}; + +template +struct is_set> { + using type = T; + static constexpr bool value{ true }; + constexpr operator bool() const noexcept { return true; } + constexpr bool operator()() const noexcept { return true; } +}; + +/** is_multiset */ + +template +struct is_multiset : std::false_type {}; + +template +struct is_multiset> { + using type = T; + static constexpr bool value{ true }; + constexpr operator bool() const noexcept { return true; } + constexpr bool operator()() const noexcept { return true; } +}; + +/** is_unordered_set */ + +template +struct is_unordered_set : std::false_type {}; + +template +struct is_unordered_set> { + using type = T; + static constexpr bool value{ true }; + constexpr operator bool() const noexcept { return true; } + constexpr bool operator()() const noexcept { return true; } +}; + +/** is_unordered_set */ + +template +struct is_unordered_multiset : std::false_type {}; + +template +struct is_unordered_multiset> { + using type = T; + static constexpr bool value{ true }; + constexpr operator bool() const noexcept { return true; } + constexpr bool operator()() const noexcept { return true; } +}; + +/** is_sequence_container */ + +template +struct is_sequence_container : std::false_type {}; + +template +struct is_sequence_container> { + using type = T; + static constexpr bool value{ true }; + constexpr operator bool() const noexcept { return true; } + constexpr bool operator()() const noexcept { return true; } +}; + +template +struct is_sequence_container> { + using type = T; + static constexpr bool value{ true }; + constexpr operator bool() const noexcept { return true; } + constexpr bool operator()() const noexcept { return true; } +}; + +template +struct is_sequence_container> { + using type = T; + static constexpr bool value{ true }; + constexpr operator bool() const noexcept { return true; } + constexpr bool operator()() const noexcept { return true; } +}; + +template +struct is_sequence_container> { + using type = T; + static constexpr bool value{ true }; + constexpr operator bool() const noexcept { return true; } + constexpr bool operator()() const noexcept { return true; } +}; + +template +struct is_sequence_container> { + using type = T; + static constexpr bool value{ true }; + constexpr operator bool() const noexcept { return true; } + constexpr bool operator()() const noexcept { return true; } +}; + +template +struct is_sequence_container> { + using type = T; + static constexpr bool value{ true }; + constexpr operator bool() const noexcept { return true; } + constexpr bool operator()() const noexcept { return true; } +}; + +template +struct is_sequence_container> { + using type = T; + static constexpr bool value{ true }; + constexpr operator bool() const noexcept { return true; } + constexpr bool operator()() const noexcept { return true; } +}; + +/** Implementation details */ + + +namespace impl { + +template +constexpr const char* filename_from_string(const char (&in_filename)[in_filename_length]) { + const char* filename_itr = in_filename; + const char* filename_end = filename_itr + in_filename_length; + const char* result = filename_itr; + + while (filename_itr != filename_end) { + if (*filename_itr == '/' || *filename_itr == '\\') { + ++filename_itr; + result = filename_itr; + } + else { + ++filename_itr; + } + } + + return result; +} + +} // namespace impl +} // namespace jessilib diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 18cbd18..a22e495 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.8) # Setup source files include_directories(../include) set(SOURCE_FILES - timer.cpp thread_pool.cpp) + timer.cpp thread_pool.cpp util.cpp object.cpp) # Setup gtest add_subdirectory(googletest/googletest) diff --git a/src/test/object.cpp b/src/test/object.cpp new file mode 100644 index 0000000..3d3749b --- /dev/null +++ b/src/test/object.cpp @@ -0,0 +1,932 @@ +/** + * Copyright (C) 2018 Jessica James. + * + * 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. + * + * THE 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. + * + * Written by Jessica James + */ + +#include +#include "test.hpp" +#include "object.hpp" + +using namespace jessilib; +using namespace std::literals; + +void object_compilation_test() { + object config {}; + + // Map accessor + config["bob"]["builder"].get(""s); + config["bob"]["builder"] = "whatever"; +} + +using signed_char_t = signed char; +using unsigned_char_t = unsigned char; +using long_long_t = long long; +using long_double_t = long double; + +/** basic tests; these test function calls against null objects and heavily test for compilation errors */ + +TEST(ObjectTest, basic) { + object obj; + + EXPECT_TRUE(obj.null()); + EXPECT_EQ(obj.size(), 0U); +} + +/** basic_has tests */ + +TEST(ObjectTest, basic_has) { + object obj; + + EXPECT_FALSE(obj.has()); + EXPECT_FALSE(obj.has()); + EXPECT_FALSE(obj.has()); + EXPECT_FALSE(obj.has()); + EXPECT_FALSE(obj.has()); + EXPECT_FALSE(obj.has()); + EXPECT_FALSE(obj.has()); + EXPECT_FALSE(obj.has()); + EXPECT_FALSE(obj.has()); + EXPECT_FALSE(obj.has()); + EXPECT_FALSE(obj.has()); + EXPECT_FALSE(obj.has()); + EXPECT_FALSE(obj.has()); + EXPECT_FALSE(obj.has()); +} + +TEST(ObjectTest, basic_has_vector) { + object obj; + + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); +} + +TEST(ObjectTest, basic_has_list) { + object obj; + + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); +} + +TEST(ObjectTest, basic_has_forward_list) { + object obj; + + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); +} + +TEST(ObjectTest, basic_has_set) { + object obj; + + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); +} + +TEST(ObjectTest, basic_has_unordered_set) { + object obj; + + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); + EXPECT_FALSE(obj.has>()); +} + +/** basic_get tests */ + +TEST(ObjectTest, basic_get) { + object obj; + + EXPECT_EQ(obj.get(), bool{}); + EXPECT_EQ(obj.get(), signed_char_t{}); + EXPECT_EQ(obj.get(), unsigned_char_t{}); + EXPECT_EQ(obj.get(), short{}); + EXPECT_EQ(obj.get(), int{}); + EXPECT_EQ(obj.get(), long{}); + EXPECT_EQ(obj.get(), long_long_t{}); + EXPECT_EQ(obj.get(), intmax_t{}); + EXPECT_EQ(obj.get(), float{}); + EXPECT_EQ(obj.get(), double{}); + EXPECT_EQ(obj.get(), long_double_t{}); + EXPECT_EQ(obj.get(), std::string{}); + EXPECT_TRUE(obj.get().empty()); + EXPECT_TRUE(obj.get().empty()); +} + +TEST(ObjectTest, basic_get_vector) { + object obj; + + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); +} + +TEST(ObjectTest, basic_get_list) { + object obj; + + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); +} + +TEST(ObjectTest, basic_get_forward_list) { + object obj; + + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); +} + +TEST(ObjectTest, basic_get_set) { + object obj; + + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); +} + +TEST(ObjectTest, basic_get_multiset) { + object obj; + + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); +} + +TEST(ObjectTest, basic_get_unordered_set) { + object obj; + + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); +} + +TEST(ObjectTest, basic_get_unordered_multiset) { + object obj; + + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); + EXPECT_TRUE(obj.get>().empty()); +} + +/** basic_value_constructor */ + +#define OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(in_type) \ + { object test_object__{ in_type {} }; \ + EXPECT_TRUE(test_object__ .has< in_type >()); \ + EXPECT_EQ(test_object__.get< in_type >(), in_type {} ); } + +// asdf + +TEST(ObjectTest, basic_value_constructor) { + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(bool); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(signed_char_t); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(unsigned_char_t); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(short); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(int); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(long); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(long_long_t); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(intmax_t); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(float); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(double); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(long_double_t); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::string); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(object::array_t); + + // const char* + { + object obj{ "" }; + EXPECT_TRUE(obj.has()); + EXPECT_EQ(obj.get(), std::string{}); + } + + // std::string_view + { + object obj{ ""sv }; + EXPECT_TRUE(obj.has()); + EXPECT_EQ(obj.get(), std::string{}); + } +} + +TEST(ObjectTest, basic_value_constructor_vector) { + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::vector); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::vector); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::vector); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::vector); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::vector); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::vector); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::vector); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::vector); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::vector); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::vector); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::vector); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::vector); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::vector); +} + +TEST(ObjectTest, basic_value_constructor_list) { + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::list); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::list); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::list); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::list); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::list); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::list); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::list); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::list); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::list); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::list); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::list); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::list); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::list); +} + +TEST(ObjectTest, basic_value_constructor_forward_list) { + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::forward_list); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::forward_list); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::forward_list); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::forward_list); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::forward_list); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::forward_list); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::forward_list); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::forward_list); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::forward_list); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::forward_list); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::forward_list); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::forward_list); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::forward_list); +} + +TEST(ObjectTest, basic_value_constructor_set) { + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::set); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::set); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::set); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::set); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::set); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::set); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::set); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::set); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::set); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::set); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::set); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::set); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::set); +} + +TEST(ObjectTest, basic_value_constructor_multiset) { + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::multiset); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::multiset); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::multiset); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::multiset); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::multiset); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::multiset); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::multiset); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::multiset); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::multiset); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::multiset); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::multiset); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::multiset); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::multiset); +} + +TEST(ObjectTest, basic_value_constructor_unordered_set) { + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::unordered_set); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::unordered_set); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::unordered_set); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::unordered_set); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::unordered_set); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::unordered_set); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::unordered_set); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::unordered_set); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::unordered_set); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::unordered_set); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::unordered_set); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::unordered_set); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::unordered_set); +} + +TEST(ObjectTest, basic_value_constructor_unordered_multiset) { + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::unordered_multiset); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::unordered_multiset); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::unordered_multiset); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::unordered_multiset); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::unordered_multiset); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::unordered_multiset); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::unordered_multiset); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::unordered_multiset); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::unordered_multiset); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::unordered_multiset); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::unordered_multiset); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::unordered_multiset); + OBJECT_BASIC_VALUE_CONSTRUCTOR_TEST(std::unordered_set); +} + +/** basic_set */ + +#define OBJECT_BASIC_SET_TEST(in_object, in_type) \ + in_object .set< in_type >( in_type {} ); \ + EXPECT_TRUE(in_object .has< in_type >()); \ + EXPECT_EQ(in_object .get< in_type >(), in_type {} ) + +TEST(ObjectTest, basic_set) { + object obj; + + OBJECT_BASIC_SET_TEST(obj, bool); + OBJECT_BASIC_SET_TEST(obj, signed_char_t); + OBJECT_BASIC_SET_TEST(obj, unsigned_char_t); + OBJECT_BASIC_SET_TEST(obj, short); + OBJECT_BASIC_SET_TEST(obj, int); + OBJECT_BASIC_SET_TEST(obj, long); + OBJECT_BASIC_SET_TEST(obj, long_long_t); + OBJECT_BASIC_SET_TEST(obj, intmax_t); + OBJECT_BASIC_SET_TEST(obj, float); + OBJECT_BASIC_SET_TEST(obj, double); + OBJECT_BASIC_SET_TEST(obj, long_double_t); + OBJECT_BASIC_SET_TEST(obj, std::string); + OBJECT_BASIC_SET_TEST(obj, object::array_t); + + // const char* + obj.set(""); + EXPECT_TRUE(obj.has()); + EXPECT_EQ(obj.get(), std::string{}); + + // std::string_view + obj.set(""sv); + EXPECT_TRUE(obj.has()); + EXPECT_EQ(obj.get(), std::string{}); +} + +TEST(ObjectTest, basic_set_vector) { + object obj; + + OBJECT_BASIC_SET_TEST(obj, std::vector); + OBJECT_BASIC_SET_TEST(obj, std::vector); + OBJECT_BASIC_SET_TEST(obj, std::vector); + OBJECT_BASIC_SET_TEST(obj, std::vector); + OBJECT_BASIC_SET_TEST(obj, std::vector); + OBJECT_BASIC_SET_TEST(obj, std::vector); + OBJECT_BASIC_SET_TEST(obj, std::vector); + OBJECT_BASIC_SET_TEST(obj, std::vector); + OBJECT_BASIC_SET_TEST(obj, std::vector); + OBJECT_BASIC_SET_TEST(obj, std::vector); + OBJECT_BASIC_SET_TEST(obj, std::vector); + OBJECT_BASIC_SET_TEST(obj, std::vector); + OBJECT_BASIC_SET_TEST(obj, std::vector); +} + +TEST(ObjectTest, basic_set_list) { + object obj; + + OBJECT_BASIC_SET_TEST(obj, std::list); + OBJECT_BASIC_SET_TEST(obj, std::list); + OBJECT_BASIC_SET_TEST(obj, std::list); + OBJECT_BASIC_SET_TEST(obj, std::list); + OBJECT_BASIC_SET_TEST(obj, std::list); + OBJECT_BASIC_SET_TEST(obj, std::list); + OBJECT_BASIC_SET_TEST(obj, std::list); + OBJECT_BASIC_SET_TEST(obj, std::list); + OBJECT_BASIC_SET_TEST(obj, std::list); + OBJECT_BASIC_SET_TEST(obj, std::list); + OBJECT_BASIC_SET_TEST(obj, std::list); + OBJECT_BASIC_SET_TEST(obj, std::list); + OBJECT_BASIC_SET_TEST(obj, std::list); +} + +TEST(ObjectTest, basic_set_forward_list) { + object obj; + + OBJECT_BASIC_SET_TEST(obj, std::forward_list); + OBJECT_BASIC_SET_TEST(obj, std::forward_list); + OBJECT_BASIC_SET_TEST(obj, std::forward_list); + OBJECT_BASIC_SET_TEST(obj, std::forward_list); + OBJECT_BASIC_SET_TEST(obj, std::forward_list); + OBJECT_BASIC_SET_TEST(obj, std::forward_list); + OBJECT_BASIC_SET_TEST(obj, std::forward_list); + OBJECT_BASIC_SET_TEST(obj, std::forward_list); + OBJECT_BASIC_SET_TEST(obj, std::forward_list); + OBJECT_BASIC_SET_TEST(obj, std::forward_list); + OBJECT_BASIC_SET_TEST(obj, std::forward_list); + OBJECT_BASIC_SET_TEST(obj, std::forward_list); + OBJECT_BASIC_SET_TEST(obj, std::forward_list); +} + +TEST(ObjectTest, basic_set_set) { + object obj; + + OBJECT_BASIC_SET_TEST(obj, std::set); + OBJECT_BASIC_SET_TEST(obj, std::set); + OBJECT_BASIC_SET_TEST(obj, std::set); + OBJECT_BASIC_SET_TEST(obj, std::set); + OBJECT_BASIC_SET_TEST(obj, std::set); + OBJECT_BASIC_SET_TEST(obj, std::set); + OBJECT_BASIC_SET_TEST(obj, std::set); + OBJECT_BASIC_SET_TEST(obj, std::set); + OBJECT_BASIC_SET_TEST(obj, std::set); + OBJECT_BASIC_SET_TEST(obj, std::set); + OBJECT_BASIC_SET_TEST(obj, std::set); + OBJECT_BASIC_SET_TEST(obj, std::set); + OBJECT_BASIC_SET_TEST(obj, std::set); +} + +TEST(ObjectTest, basic_set_multiset) { + object obj; + + OBJECT_BASIC_SET_TEST(obj, std::multiset); + OBJECT_BASIC_SET_TEST(obj, std::multiset); + OBJECT_BASIC_SET_TEST(obj, std::multiset); + OBJECT_BASIC_SET_TEST(obj, std::multiset); + OBJECT_BASIC_SET_TEST(obj, std::multiset); + OBJECT_BASIC_SET_TEST(obj, std::multiset); + OBJECT_BASIC_SET_TEST(obj, std::multiset); + OBJECT_BASIC_SET_TEST(obj, std::multiset); + OBJECT_BASIC_SET_TEST(obj, std::multiset); + OBJECT_BASIC_SET_TEST(obj, std::multiset); + OBJECT_BASIC_SET_TEST(obj, std::multiset); + OBJECT_BASIC_SET_TEST(obj, std::multiset); + OBJECT_BASIC_SET_TEST(obj, std::multiset); +} + +TEST(ObjectTest, basic_set_unordered_set) { + object obj; + + OBJECT_BASIC_SET_TEST(obj, std::unordered_set); + OBJECT_BASIC_SET_TEST(obj, std::unordered_set); + OBJECT_BASIC_SET_TEST(obj, std::unordered_set); + OBJECT_BASIC_SET_TEST(obj, std::unordered_set); + OBJECT_BASIC_SET_TEST(obj, std::unordered_set); + OBJECT_BASIC_SET_TEST(obj, std::unordered_set); + OBJECT_BASIC_SET_TEST(obj, std::unordered_set); + OBJECT_BASIC_SET_TEST(obj, std::unordered_set); + OBJECT_BASIC_SET_TEST(obj, std::unordered_set); + OBJECT_BASIC_SET_TEST(obj, std::unordered_set); + OBJECT_BASIC_SET_TEST(obj, std::unordered_set); + OBJECT_BASIC_SET_TEST(obj, std::unordered_set); + OBJECT_BASIC_SET_TEST(obj, std::unordered_set); +} + +TEST(ObjectTest, basic_set_unordered_multiset) { + object obj; + + OBJECT_BASIC_SET_TEST(obj, std::unordered_multiset); + OBJECT_BASIC_SET_TEST(obj, std::unordered_multiset); + OBJECT_BASIC_SET_TEST(obj, std::unordered_multiset); + OBJECT_BASIC_SET_TEST(obj, std::unordered_multiset); + OBJECT_BASIC_SET_TEST(obj, std::unordered_multiset); + OBJECT_BASIC_SET_TEST(obj, std::unordered_multiset); + OBJECT_BASIC_SET_TEST(obj, std::unordered_multiset); + OBJECT_BASIC_SET_TEST(obj, std::unordered_multiset); + OBJECT_BASIC_SET_TEST(obj, std::unordered_multiset); + OBJECT_BASIC_SET_TEST(obj, std::unordered_multiset); + OBJECT_BASIC_SET_TEST(obj, std::unordered_multiset); + OBJECT_BASIC_SET_TEST(obj, std::unordered_multiset); + OBJECT_BASIC_SET_TEST(obj, std::unordered_set); +} + +/** basic_assignment_operator */ + +#define OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(in_object, in_type) \ + in_object = in_type {}; \ + EXPECT_TRUE(in_object .has< in_type >()); \ + EXPECT_EQ(in_object .get< in_type >(), in_type {} ) + +TEST(ObjectTest, basic_assignment_operator) { + object obj; + + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, bool); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, signed_char_t); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, unsigned_char_t); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, short); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, int); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, long); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, long_long_t); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, intmax_t); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, float); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, double); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, long_double_t); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::string); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, object::array_t); + + // const char* + obj = ""; + EXPECT_TRUE(obj.has()); + EXPECT_EQ(obj.get(), std::string{}); + + // std::string_view + obj = ""sv; + EXPECT_TRUE(obj.has()); + EXPECT_EQ(obj.get(), std::string{}); +} + +TEST(ObjectTest, basic_assignment_operator_vector) { + object obj; + + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::vector); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::vector); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::vector); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::vector); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::vector); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::vector); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::vector); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::vector); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::vector); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::vector); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::vector); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::vector); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::vector); +} + +TEST(ObjectTest, basic_assignment_operator_list) { + object obj; + + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::list); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::list); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::list); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::list); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::list); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::list); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::list); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::list); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::list); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::list); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::list); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::list); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::list); +} + +TEST(ObjectTest, basic_assignment_operator_forward_list) { + object obj; + + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::forward_list); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::forward_list); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::forward_list); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::forward_list); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::forward_list); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::forward_list); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::forward_list); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::forward_list); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::forward_list); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::forward_list); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::forward_list); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::forward_list); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::forward_list); +} + +TEST(ObjectTest, basic_assignment_operator_set) { + object obj; + + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::set); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::set); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::set); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::set); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::set); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::set); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::set); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::set); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::set); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::set); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::set); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::set); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::set); +} + +TEST(ObjectTest, basic_assignment_operator_multiset) { + object obj; + + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::multiset); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::multiset); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::multiset); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::multiset); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::multiset); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::multiset); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::multiset); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::multiset); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::multiset); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::multiset); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::multiset); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::multiset); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::multiset); +} + +TEST(ObjectTest, basic_assignment_operator_unordered_set) { + object obj; + + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::unordered_set); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::unordered_set); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::unordered_set); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::unordered_set); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::unordered_set); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::unordered_set); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::unordered_set); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::unordered_set); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::unordered_set); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::unordered_set); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::unordered_set); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::unordered_set); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::unordered_set); +} + +TEST(ObjectTest, basic_assignment_operator_unordered_multiset) { + object obj; + + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::unordered_multiset); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::unordered_multiset); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::unordered_multiset); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::unordered_multiset); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::unordered_multiset); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::unordered_multiset); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::unordered_multiset); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::unordered_multiset); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::unordered_multiset); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::unordered_multiset); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::unordered_multiset); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::unordered_multiset); + OBJECT_BASIC_ASSIGNMENT_OPERATOR_TEST(obj, std::unordered_set); +} + +/** basic_access_operator */ + +TEST(ObjectTest, basic_access_operator) { + object obj; + + obj["test"] = 1234; + EXPECT_EQ(obj["test"].get(), 1234); + EXPECT_EQ(obj["test2"].get(), 0); + + obj["test"] = 4567; + EXPECT_EQ(obj["test"].get(), 4567); + EXPECT_EQ(obj["test2"].get(), 0); + + obj["test2"] = 1234; + EXPECT_EQ(obj["test"].get(), 4567); + EXPECT_EQ(obj["test2"].get(), 1234); +} + +/** end basic tests */ + +TEST(ObjectTest, set_bool) { + object obj; + + obj.set(true); + + EXPECT_TRUE(obj.has()); + EXPECT_TRUE(obj.get()); + EXPECT_FALSE(obj.has()); + EXPECT_EQ(obj.get(), 0); + + obj.set(false); + + EXPECT_TRUE(obj.has()); + EXPECT_FALSE(obj.get()); + EXPECT_FALSE(obj.has()); + EXPECT_EQ(obj.get(), 0); +} + +TEST(ObjectTest, set_int) { + object obj; + + obj.set(1337); + + EXPECT_TRUE(obj.has()); + EXPECT_EQ(obj.get(), 1337); + EXPECT_FALSE(obj.has()); + EXPECT_FALSE(obj.get()); + + obj.set(7331); + + EXPECT_TRUE(obj.has()); + EXPECT_EQ(obj.get(), 7331); + EXPECT_FALSE(obj.has()); + EXPECT_FALSE(obj.get()); + + obj.set(0); + + EXPECT_TRUE(obj.has()); + EXPECT_EQ(obj.get(), 0); + EXPECT_FALSE(obj.has()); + EXPECT_FALSE(obj.get()); +} + +TEST(ObjectTest, set_float) { + object obj; + + obj.set(13.37); + + EXPECT_TRUE(obj.has()); + EXPECT_EQ(obj.get(), 13.37); + EXPECT_FALSE(obj.has()); + EXPECT_FALSE(obj.get()); + + obj.set(73.31); + + EXPECT_TRUE(obj.has()); + EXPECT_EQ(obj.get(), 73.31); + EXPECT_FALSE(obj.has()); + EXPECT_FALSE(obj.get()); + + obj.set(0.0); + + EXPECT_TRUE(obj.has()); + EXPECT_EQ(obj.get(), 0.0); + EXPECT_FALSE(obj.has()); + EXPECT_FALSE(obj.get()); +} + +TEST(ObjectTest, set_string) { + object obj; + + obj.set("Jessica"); + + EXPECT_TRUE(obj.has()); + EXPECT_EQ(obj.get(), "Jessica"); + EXPECT_FALSE(obj.has()); + EXPECT_FALSE(obj.get()); + + obj.set("was"s); + + EXPECT_TRUE(obj.has()); + EXPECT_EQ(obj.get(), "was"); + EXPECT_FALSE(obj.has()); + EXPECT_FALSE(obj.get()); + + obj.set("here"sv); + + EXPECT_TRUE(obj.has()); + EXPECT_EQ(obj.get(), "here"); + EXPECT_FALSE(obj.has()); + EXPECT_FALSE(obj.get()); + + obj.set(""); + + EXPECT_TRUE(obj.has()); + EXPECT_EQ(obj.get(), ""); + EXPECT_FALSE(obj.has()); + EXPECT_FALSE(obj.get()); +} + +TEST(ObjectTest, set_vector) { + object obj; + + obj.set(std::vector{ true }); + + EXPECT_TRUE(obj.has>()); + EXPECT_EQ(obj.get>(), std::vector{ true }); + EXPECT_TRUE(obj.has>()); + EXPECT_EQ(obj.get>().size(), 1U); + EXPECT_TRUE(obj.has>()); + EXPECT_EQ(obj.get>().size(), 0U); + EXPECT_FALSE(obj.has()); + EXPECT_FALSE(obj.get()); + + obj.set(std::vector{}); + + EXPECT_TRUE(obj.has>()); + EXPECT_EQ(obj.get>().size(), 0U); + EXPECT_TRUE(obj.has>()); + EXPECT_EQ(obj.get>().size(), 0U); + EXPECT_TRUE(obj.has>()); + EXPECT_EQ(obj.get>().size(), 0U); + EXPECT_FALSE(obj.has()); + EXPECT_FALSE(obj.get()); +} + +TEST(ObjectTest, set_object) { + object obj1, obj2; + + obj1.set(true); + obj2.set(obj1); + + EXPECT_EQ(obj1, obj2); +} diff --git a/src/test/thread_pool.cpp b/src/test/thread_pool.cpp index 7ed0f26..ceede8a 100644 --- a/src/test/thread_pool.cpp +++ b/src/test/thread_pool.cpp @@ -53,19 +53,17 @@ TEST(ThreadPoolTest, initialSizeDefined) { } TEST(ThreadPoolTest, push) { - repeat (total_iterations) { - std::atomic iterations{0}; - thread_pool pool; - - repeat (total_iterations) { - pool.push([&iterations, &pool]() { - ++iterations; - }); - } + std::atomic iterations{0}; + thread_pool pool; - pool.join(); - EXPECT_EQ(iterations, total_iterations); + repeat (total_iterations) { + pool.push([&iterations, &pool]() { + ++iterations; + }); } + + pool.join(); + EXPECT_EQ(iterations, total_iterations); } TEST(ThreadPoolTest, deadlockSingleThread) { diff --git a/src/test/util.cpp b/src/test/util.cpp new file mode 100644 index 0000000..3933c2b --- /dev/null +++ b/src/test/util.cpp @@ -0,0 +1,32 @@ +/** + * Copyright (C) 2018 Jessica James. + * + * 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. + * + * THE 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. + * + * Written by Jessica James + */ + +#include "util.hpp" +#include "test.hpp" +#include "object.hpp" + +using namespace jessilib; +using namespace std::literals; + +TEST(UtilTest, filename) { + constexpr const char* filename = JESSILIB_FILENAME; + EXPECT_STREQ(filename, "util.cpp"); +} + +// Non-virtual using variant: 48 +// \ No newline at end of file