commit bb839d27ed695152ff7d58607d38adf9a3978b8e Author: bergmann Date: Fri Jun 21 16:56:23 2019 +0200 * Initial commit * Moved and refactored code from existing cpputils library diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..567609b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..a89889c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "cmake/modules"] + path = cmake/modules + url = b3rgmann@git.bergmann89.de:cpp/CmakeModules.git diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..63abecf --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,53 @@ +# Initialize CMake ################################################################################ + +CMake_Minimum_Required ( VERSION 3.12.0 FATAL_ERROR ) + +# Set CMAKE_BUILD_TYPE +If ( NOT CMAKE_BUILD_TYPE ) + Set ( CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build!" FORCE ) +EndIf ( NOT CMAKE_BUILD_TYPE ) +Set_Property ( CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS Debug Release RelWithDebInfo MinSizeRel ) + +# Set CMAKE_MODULE_PATH +If ( EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/" ) + Set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/" ) +EndIf ( ) +If ( EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/" ) + Set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules" ) +EndIf ( ) + +# Project ######################################################################################### + +Include ( "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cppcore-var.cmake" ) +Project ( cppcore + DESCRIPTION "A simple library" + VERSION "${CPPCORE_VERSION}" ) +Include ( CTest ) +Include ( GNUInstallDirs ) + +# Subdirectories +Add_SubDirectory ( ${CMAKE_CURRENT_SOURCE_DIR}/src ) +Add_SubDirectory ( ${CMAKE_CURRENT_SOURCE_DIR}/test ) + +# Install +Include ( CMakePackageConfigHelpers ) +Write_Basic_Package_Version_File ( "${CMAKE_CURRENT_BINARY_DIR}/cmake/cppcore-config-version.cmake" + VERSION ${CPPCORE_VERSION} + COMPATIBILITY AnyNewerVersion ) +Configure_File ( "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cppcore-config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/cmake/cppcore-config.cmake" + @ONLY ) + +Set ( ConfigPackageLocation "${CPPCORE_INSTALL_DIR_SHARE}/cmake" ) +Install ( EXPORT cppcore + NAMESPACE cppcore:: + DESTINATION ${ConfigPackageLocation} ) +Install ( FILES + "${CMAKE_CURRENT_BINARY_DIR}/cmake/cppcore-config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/cmake/cppcore-config-version.cmake" + DESTINATION + ${ConfigPackageLocation} + COMPONENT + Devel ) diff --git a/cmake/cppcore-config.cmake b/cmake/cppcore-config.cmake new file mode 100644 index 0000000..52f697e --- /dev/null +++ b/cmake/cppcore-config.cmake @@ -0,0 +1,4 @@ +# cppcore-config.cmake - package configuration file + +Get_Filename_Component ( CURRENT_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH ) +Include ( ${CURRENT_DIR}/cppcore.cmake ) diff --git a/cmake/cppcore-var.cmake b/cmake/cppcore-var.cmake new file mode 100644 index 0000000..70b75e6 --- /dev/null +++ b/cmake/cppcore-var.cmake @@ -0,0 +1,20 @@ +# Version +Set ( CPPCORE_VERSION_MAJOR 1 ) +Set ( CPPCORE_VERSION_MINOR 0 ) +Set ( CPPCORE_VERSION_PATCH 0 ) +Set ( CPPCORE_VERSION_BUILD 0 ) +Set ( CPPCORE_VERSION_SHORT "${CPPCORE_VERSION_MAJOR}.${CPPCORE_VERSION_MINOR}" ) +Set ( CPPCORE_VERSION "${CPPCORE_VERSION_SHORT}.${CPPCORE_VERSION_PATCH}.${CPPCORE_VERSION_BUILD}" ) +Set ( CPPCORE_NAME "cppcore-${CPPCORE_VERSION_SHORT}" ) +Set ( CPPCORE_OUTPUTNAME "helloworld" ) + +# Install directories +Set ( CPPCORE_INSTALL_DIR_INCLUDE "include/${CPPCORE_NAME}" ) +Set ( CPPCORE_INSTALL_DIR_LIB "lib" ) +Set ( CPPCORE_INSTALL_DIR_SHARE "share/${CPPCORE_NAME}" ) + +# C Standard +Set ( CMAKE_C_STANDARD 11 ) +Set ( CMAKE_CXX_STANDARD 17 ) +Set ( CMAKE_C_STANDARD_REQUIRED ON ) +Set ( CMAKE_CXX_STANDARD_REQUIRED ON ) diff --git a/cmake/modules b/cmake/modules new file mode 160000 index 0000000..1a32531 --- /dev/null +++ b/cmake/modules @@ -0,0 +1 @@ +Subproject commit 1a32531aef2deeebd5637b1873bc4e976628801c diff --git a/include/cppcore.h b/include/cppcore.h new file mode 100644 index 0000000..458ddfa --- /dev/null +++ b/include/cppcore.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include +#include +#include diff --git a/include/cppcore/conversion.h b/include/cppcore/conversion.h new file mode 100644 index 0000000..3c7b7b7 --- /dev/null +++ b/include/cppcore/conversion.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include +#include +#include diff --git a/include/cppcore/conversion/byteorder.h b/include/cppcore/conversion/byteorder.h new file mode 100644 index 0000000..c180397 --- /dev/null +++ b/include/cppcore/conversion/byteorder.h @@ -0,0 +1,107 @@ +#pragma once + +#if defined(__linux__) + #include +#elif defined(__FreeBSD__) || defined(__NetBSD__) + #include +#elif defined(__OpenBSD__) + #include + #define be16toh(x) betoh16(x) + #define be32toh(x) betoh32(x) + #define be64toh(x) betoh64(x) +#endif + +namespace cppcore +{ + + namespace __impl + { + + /** + * @brief Helper class to convert byte order. + */ + template + struct byteorder_convert_helper; + + /** + * @brief Trait class for byteorder conversions. + */ + struct byteorder_traits + { + template + using convert_helper = byteorder_convert_helper; + }; + + } + + /** + * @brief Convert the byte order of a any type from host byte order to network byte order. + * + * @tparam T Type to convert. + * @tparam T_traits Trait class to store needed types and helpers. + * + * @param[in] value Value to convert. + * + * @return Converted value. + */ + template + constexpr decltype(auto) hton(const T& value); + + /** + * @brief Convert the byte order of a any type from network byte order to host byte order. + * + * @tparam T Type to convert. + * @tparam T_traits Trait class to store needed types and helpers. + * + * @param[in] value Value to convert. + * + * @return Converted value. + */ + template + constexpr decltype(auto) ntoh(const T& value); + + /** + * @brief Class to convert byte order of any type. + * + * @tparam T_traits Trait class to store needed types and helpers. + */ + template + struct byteorder_t + { + using traits_type = T_traits; + + /** + * @brief Convert the byte order of a any type from host byte order to network byte order. + * + * @tparam T Type to convert. + * + * @param[in] value Value to convert. + * + * @return Converted value. + */ + template + static constexpr decltype(auto) hton(const T& value); + + /** + * @brief Convert the byte order of a any type from network byte order to host byte order. + * + * @tparam T Type to convert. + * + * @param[in] value Value to convert. + * + * @return Converted value. + */ + template + static constexpr decltype(auto) ntoh(const T& value); + }; + + /** + * @brief Byte order helper class with default traits. + */ + struct byteorder + : public byteorder_t<__impl::byteorder_traits> + { }; + +} + +#include "byteorder.inl" diff --git a/include/cppcore/conversion/byteorder.inl b/include/cppcore/conversion/byteorder.inl new file mode 100644 index 0000000..9885977 --- /dev/null +++ b/include/cppcore/conversion/byteorder.inl @@ -0,0 +1,87 @@ +#pragma once + +#include "byteorder.h" + +namespace cppcore +{ + + /* static */ + + template + constexpr decltype(auto) hton(const T& value) + { return byteorder_t::template hton(value); } + + template + constexpr decltype(auto) ntoh(const T& value) + { return byteorder_t::template ntoh(value); } + + /* byteorder */ + + template + template + constexpr decltype(auto) byteorder_t + ::hton(const T& value) + { return traits_type::template convert_helper::hton(value); } + + template + template + constexpr decltype(auto) byteorder_t + ::ntoh(const T& value) + { return traits_type::template convert_helper::ntoh(value); } + + namespace __impl + { + /** + * @brief Helper class to convert type of size with 1 byte. + */ + template + struct byteorder_convert_helper + { + static inline T hton(const T& t) + { return t; } + + static inline T ntoh(const T& t) + { return t; } + }; + + /** + * @brief Helper class to convert type of size with 2 byte. + */ + template + struct byteorder_convert_helper + { + static inline T hton(const T& t) + { return reinterpret_cast(htobe16(reinterpret_cast(t))); } + + static inline T ntoh(const T& t) + { return reinterpret_cast(be16toh(reinterpret_cast(t))); } + }; + + /** + * @brief Helper class to convert type of size with 4 byte. + */ + template + struct byteorder_convert_helper + { + static inline T hton(const T& t) + { return reinterpret_cast(htobe32(reinterpret_cast(t))); } + + static inline T ntoh(const T& t) + { return reinterpret_cast(be32toh(reinterpret_cast(t))); } + }; + + /** + * @brief Helper class to convert type of size with 8 byte. + */ + template + struct byteorder_convert_helper + { + static inline T hton(const T& t) + { return reinterpret_cast(htobe64(reinterpret_cast(t))); } + + static inline T ntoh(const T& t) + { return reinterpret_cast(be64toh(reinterpret_cast(t))); } + }; + } + +} diff --git a/include/cppcore/conversion/enum.h b/include/cppcore/conversion/enum.h new file mode 100644 index 0000000..ee082b5 --- /dev/null +++ b/include/cppcore/conversion/enum.h @@ -0,0 +1,166 @@ +#pragma once + +#include +#include + +#include +#include +#include +// #include + +#define cppcore_define_enum_value_traits(enum, ...) \ + namespace cppcore { \ + namespace __impl { \ + template<> \ + struct enum_value_traits \ + { \ + using enum_type = enum; \ + using enum_value_pair_type = std::pair; \ + using enum_value_vector_type = std::vector; \ + \ + static decltype(auto) get_enum_values() \ + { \ + static const enum_value_vector_type value({ \ + __VA_ARGS__ \ + }); \ + return value; \ + } \ + }; } } + +namespace cppcore +{ + + namespace __impl + { + + /** + * @brief Traits class to strore enum to string pairs and the needed data types. + * + * @tparam T_enum Enum type. + */ + template + struct enum_value_traits + { + using enum_type = T_enum; + using enum_value_pair_type = std::pair; + using enum_value_vector_type = std::vector; + + /** + * @brief Returns a vector with all enum to string value paris. + */ + static decltype(auto) get_enum_values(); + }; + + /** + * @brief Traits class for enum conersions to store the needed types and data. + * + * @tparam T_enum Enum type. + * @tparam T_traits Traits that stores a list with all enum to string values pairs. + */ + template< + typename T_enum, + typename T_traits = enum_value_traits> + struct enum_conversion_traits + { + public: + using enum_type = T_enum; + using traits_type = enum_value_traits; + using enum_to_string_map_type = std::map; + using string_to_enum_map_type = std::map; + + /** + * @brief Returns a map with enum to string values. + */ + static inline decltype(auto) get_enum_to_string_map(); + + /** + * @brief Returns a map with string to enum values. + */ + static inline decltype(auto) get_string_to_enum_map(); + + private: + /** + * @brief Helper method to create the enum to string map. + */ + static inline decltype(auto) create_enum_to_string_map(); + + /** + * @brief Helper method to create the string to enum map. + */ + static inline decltype(auto) create_string_to_enum_map(); + }; + + } + + /** + * @brief Class to convert enum values to and from string. + * + * @tparam T_enum Enum type. + * @tparam T_traits Traits type that stores the enum to value and the value to enum maps. + */ + template< + typename T_enum, + typename T_traits = __impl::enum_conversion_traits> + struct enum_conversion + { + using enum_type = T_enum; + using base_type = typename std::underlying_type::type; + using traits_type = T_traits; + + /** + * @brief Convert the given enum value to a string. + * + * @param[in] value Enum value to convert to string. + * @param[in] add_numeric_value Append the numeric value to the converted enum value. + * + * @return Enum converted to string. + */ + static inline std::string to_string( + enum_type value, + bool add_numeric_value = false); + + /** + * @brief Convert the given string to the enum value. + * + * @param[in] str String to convert to enum value. + * @param[out] value Value to store converted + * @param[in] accept_numeric_value Accept numeric values. + * + * @retval true If the conversion was successfull. + * @retval false If the string could not be converted. + */ + static inline bool try_to_enum( + const std::string& str, + enum_type& value, + bool accept_numeric_value = true); + + /** + * @brief Convert the given string to the enum value. Throws an exception if the conversion failed. + * + * @param[in] str String to convert to enum value. + * @param[in] accept_numeric_value Accept numeric values. + * + * @return Enum value. + */ + static inline enum_type to_enum( + const std::string& str, + bool accept_numeric_value = true); + + /** + * @brief Convert the given string to the enum value. Returns the passed default value if the conversion failed. + * + * @param[in] str String to convert to enum value. + * @param[in] default_value Default value to pass if the conversion failed. + * @param[in] accept_numeric_value Accept numeric values. + * + * @return Enum value. + */ + static inline enum_type to_enum_default( + const std::string& str, + enum_type default_value, + bool accept_numeric_value = true); + }; + +} + +#include "enum.inl" diff --git a/include/cppcore/conversion/enum.inl b/include/cppcore/conversion/enum.inl new file mode 100644 index 0000000..6922639 --- /dev/null +++ b/include/cppcore/conversion/enum.inl @@ -0,0 +1,117 @@ +#pragma once + +#include "enum.h" + +namespace cppcore +{ + + namespace __impl + { + + /* enum_conversion_traits */ + + template + decltype(auto) enum_conversion_traits + ::get_enum_to_string_map() + { + static const auto value = create_enum_to_string_map(); + return value; + } + + template + decltype(auto) enum_conversion_traits + ::get_string_to_enum_map() + { + static const auto value = create_string_to_enum_map(); + return value; + } + + template + decltype(auto) enum_conversion_traits + ::create_enum_to_string_map() + { + auto& values = traits_type::get_enum_values(); + enum_to_string_map_type ret; + for (auto& p : values) + { + ret.emplace(p.first, p.second); + } + return ret; + } + + template + decltype(auto) enum_conversion_traits + ::create_string_to_enum_map() + { + auto& values = traits_type::get_enum_values(); + string_to_enum_map_type ret; + for (auto& p : values) + { + ret.emplace(p.second, p.first); + } + return ret; + } + + } + + /* enum_conversion */ + + template + std::string enum_conversion + ::to_string(enum_type value, bool add_numeric_value) + { + std::string ret; + auto& map = traits_type::get_enum_to_string_map(); + auto it = map.find(value); + if (it != map.end()) + { + ret = it->second; + if (add_numeric_value) + ret += '(' + std::to_string(static_cast(value)) + ')'; // TODO + } + else + ret = std::to_string(static_cast(value)); // TODO + return ret; + } + + template + bool enum_conversion + ::try_to_enum(const std::string& str, enum_type& value, bool accept_numeric_value) + { + static const auto& map = traits_type::get_string_to_enum_map(); + auto it = map.find(str); + if (it != map.end()) + { + value = it->second; + return true; + } + if (!accept_numeric_value) + return false; + char *e = nullptr; + const char *c = str.c_str(); + value = static_cast(std::strtoull(c, &e, 0)); + return (c != e); + } + + template + T_enum enum_conversion + ::to_enum(const std::string& str, bool accept_numeric_value) + { + using namespace ::std; + enum_type value; + if (!try_to_enum(str, value, accept_numeric_value)) + throw convert_exception("unable to convert '"s + str + "' to '"s + type_helper::name() + "'"s); + return value; + } + + template + T_enum enum_conversion + ::to_enum_default(const std::string& str, enum_type default_value, bool accept_numeric_value) + { + enum_type value; + return try_to_enum(str, value, accept_numeric_value) + ? value + : default_value; + } + +} diff --git a/include/cppcore/conversion/string.h b/include/cppcore/conversion/string.h new file mode 100644 index 0000000..fc6b380 --- /dev/null +++ b/include/cppcore/conversion/string.h @@ -0,0 +1,223 @@ +#pragma once + +#include +#include + +namespace cppcore +{ + + namespace __impl + { + + /** + * @brief Predicate class to convert any type to a string using a stream. + */ + template + struct op_to_stream; + + /** + * @brief Predicate class to convert any type to a string. + */ + template + struct op_to_string; + + /** + * @brief Predicate class to convert a string to any type. + */ + template + struct op_from_string; + + /** + * @brief Traits class to store needed data types. + */ + struct string_conversion_traits + { + template + using to_stream_predicate_type = op_to_stream; + + template + using to_string_predicate_type = op_to_string; + + template + using from_string_predicate_type = op_from_string; + }; + + } + + /** + * @brief Convert the passed value to string. + * + * @tparam T Type of the passed value. + * @tparam T_traits Traits type to use for conversion. + * + * @param[in] value Value to convert to string. + * + * @return Converted value. + */ + template + inline std::string to_string(const T& value); + + /** + * @brief Write the passed value as string representation to a stream. + * + * @tparam T Type of the passed value. + * @tparam T_traits Traits type to use for conversion. + * + * @param[in] value Value to convert to write to stream. + */ + template + inline void to_string(std::ostream& os, const T& value); + + /** + * @brief Try to convert the given string to the passed value. + * + * @tparam T Type of the value to convert the stirng to. + * @tparam T_traits Traits type to use for conversion. + * + * @param[in] s String to convert to value. + * @param[out] value Parameter to store vconverted value in. + * + * @retval true If the conversion was successful. + * @retval false If the conversion failed. + */ + template + inline bool try_from_string(const std::string& s, T& value); + + /** + * @brief Try to convert the given string to the passed type. Throws exception if conversion was failed. + * + * @tparam T Type of the value to convert the stirng to. + * @tparam T_traits Traits type to use for conversion. + * + * @param[in] s String to convert to value. + * + * @return Converted value. + */ + template + inline T from_string(const std::string& s); + + /** + * @brief Try to convert the given string to the passed type. Returns the passed default value if conversion was failed. + * + * @tparam T Type of the value to convert the stirng to. + * @tparam T_traits Traits type to use for conversion. + * + * @param[in] s String to convert to value. + * @param[in] default_value Default value to return if the conversion failed. + * + * @return Converted value. + */ + template + inline T from_string_default(const std::string& s, const T& default_value); + + /** + * @brief Split string at the passed seperator and calls the passed predicate for each element. + * + * @tparam T_predicate Type of predicate to execute for each element. + * + * @param s String to split. + * @param seperator Seperator to split string at. + * @param predicate Predicate to execute for each element. + * + * @return Value returned from the predicate. + */ + template + inline bool string_split(const std::string& s, char seperator, const T_predicate& predicate); + + /** + * @brief Class to handle string conversions. + * + * @tparam T_traits Traits to use for converting. + */ + template + struct string_conversion_t + { + using traits_type = T_traits; + + /** + * @brief Convert the passed value to string. + * + * @tparam T Type of the passed value. + * + * @param[in] value Value to convert to string. + * + * @return Converted value. + */ + template + static inline std::string to_string(const T& value); + + /** + * @brief Write the passed value as string representation to a stream. + * + * @tparam T Type of the passed value. + * + * @param[in] value Value to convert to write to stream. + */ + template + static inline void to_string(std::ostream& os, const T& value); + + /** + * @brief Try to convert the given string to the passed value. + * + * @tparam T Type of the value to convert the stirng to. + * + * @param[in] s String to convert to value. + * @param[out] value Parameter to store vconverted value in. + * + * @retval true If the conversion was successful. + * @retval false If the conversion failed. + */ + template + static inline bool try_from_string(const std::string& s, T& value); + + /** + * @brief Try to convert the given string to the passed type. Throws exception if conversion was failed. + * + * @tparam T Type of the value to convert the stirng to. + * + * @param[in] s String to convert to value. + * + * @return Converted value. + */ + template + static inline T from_string(const std::string& s); + + /** + * @brief Try to convert the given string to the passed type. Returns the passed default value if conversion was failed. + * + * @tparam T Type of the value to convert the stirng to. + * + * @param[in] s String to convert to value. + * @param[in] default_value Default value to return if the conversion failed. + * + * @return Converted value. + */ + template + static inline T from_string_default(const std::string& s, const T& default_value); + }; + + /** + * @brief String conversion class with default traits. + */ + struct string_conversion + : public string_conversion_t<__impl::string_conversion_traits> + { }; + +} + + +namespace std +{ + + /** + * @brief Operator overload to write value to stream that supports the to_string method, with stream parameter. + */ + template + inline auto operator<<(basic_ostream& os, X&& x) + -> decltype( + std::forward(x).to_string(std::declval&>()), + std::declval&>()); + +} + +#include "string.inl" diff --git a/include/cppcore/conversion/string.inl b/include/cppcore/conversion/string.inl new file mode 100644 index 0000000..8c3f739 --- /dev/null +++ b/include/cppcore/conversion/string.inl @@ -0,0 +1,422 @@ +#pragma once + +#include +#include + +#include "enum.h" +#include "string.h" + +#ifdef _GLIBCXX_VECTOR +#define CPPCORE_HAS_VECTOR +#endif + +#ifdef _GLIBCXX_LIST +#define CPPCORE_HAS_LIST +#endif + +namespace cppcore +{ + + /* global */ + + template + inline std::string to_string(const T& value) + { return string_conversion_t::template to_string(value); } + + template + inline void to_string(std::ostream& os, const T& value) + { string_conversion_t::template to_string(os, value); } + + template + inline bool try_from_string(const std::string& s, T& value) + { return string_conversion_t::template try_from_string(s, value); } + + template + inline T from_string(const std::string& s) + { return string_conversion_t::template from_string(s); } + + template + inline T from_string_default(const std::string& s, const T& default_value) + { return string_conversion_t::template from_string_default(s, default_value); } + + template + inline bool string_split(const std::string& str, char seperator, T_predicate&& predicate) + { + auto* i = str.c_str(); + auto* e = str.c_str() + str.size(); + auto* s = i; + while (i <= e) + { + if ( s != e + && ( *i == seperator + || *i == '\0')) + { + std::string tmp(s, static_cast(i - s)); + if (!predicate(tmp)) + return false; + s = i + 1; + } + ++i; + } + return true; + } + + /* string_conversion */ + + template + template + inline std::string string_conversion_t + ::to_string(const T& value) + { + using predicate_type = typename traits_type::template to_string_predicate_type; + return predicate_type { } (value); + } + + template + template + inline void string_conversion_t + ::to_string(std::ostream& os, const T& value) + { + using predicate_type = typename traits_type::template to_stream_predicate_type; + predicate_type { } (os, value); + } + + template + template + inline bool string_conversion_t + ::try_from_string(const std::string& s, T& value) + { + using predicate_type = typename traits_type::template from_string_predicate_type; + return predicate_type { } (s, value); + } + + template + template + inline T string_conversion_t + ::from_string(const std::string& s) + { + using namespace ::std; + T ret; + if (!try_from_string(s, ret)) + throw convert_exception("unable to convert '"s + s + "' to '"s + type_helper::name() + "'"s); + return ret; + } + + template + template + inline T string_conversion_t + ::from_string_default(const std::string& s, const T& default_value) + { + T ret; + return try_from_string(s, ret) + ? ret + : default_value; + } + + namespace __impl + { + + /* op_to_string */ + + template + struct op_to_string + { + inline std::string operator()(const T& v) const + { return std::to_string(v); } + }; + + template + struct op_to_string>> + { + inline std::string operator()(const T& v) const + { return enum_conversion::to_string(v); } + }; + + template + struct op_to_string>> + { + inline std::string operator()(const T& v) const + { + std::stringstream os; + op_to_stream { } (os, v); + return os.str(); + } + }; + + template<> + struct op_to_string + { + inline std::string operator()(const std::string& v) const + { return v; } + }; + + template + struct op_to_string, char>>> + { + using value_type = T[N]; + + inline std::string operator()(const value_type& v) const + { return std::string(v, N-1); } + }; + + template + struct op_to_string().to_string(std::declval()), void())> + { + inline std::string operator()(const T& v) const + { + std::ostringstream os; + v.to_string(os); + return os.str(); + } + }; + + template + struct op_to_string().to_string(), void())> + { + inline std::string operator()(const T& v) const + { return v.to_string(); } + }; + + #ifdef CPPCORE_HAS_VECTOR + template + struct op_to_string, void> + { + inline std::string operator()(const std::vector& v) const + { + std::ostringstream os; + op_to_stream> { } (os, v); + return os.str(); + } + }; + #endif + + #ifdef CPPCORE_HAS_LIST + template + struct op_to_string, void> + { + inline std::string operator()(const std::list& v) const + { + std::ostringstream os; + op_to_stream> { } (os, v); + return os.str(); + } + }; + #endif + + /* op_to_stream */ + + template + struct op_to_stream + { + inline void operator()(std::ostream& os, const T& v) const + { os << v; } + }; + + template + struct op_to_stream>> + { + inline void operator()(std::ostream& os, const T& v) const + { os << enum_conversion::to_string(v); } + }; + + template + struct op_to_stream().to_string(std::declval()), void())> + { + inline void operator()(std::ostream& os, const T& v) const + { v.to_string(os); } + }; + + template + struct op_to_stream().to_string(), void())> + { + inline void operator()(std::ostream& os, const T& v) const + { os << v.to_string(); } + }; + + #ifdef CPPCORE_HAS_VECTOR + template + struct op_to_stream, void> + { + inline void operator()(std::ostream& os, const std::vector& v) const + { + bool first = true; + for (auto& x : v) + { + if (first) first = false; + else os << ','; + to_string(os, x); + } + } + }; + #endif + + #ifdef CPPCORE_HAS_LIST + template + struct op_to_stream, void> + { + inline void operator()(std::ostream& os, const std::list& v) const + { + bool first = true; + for (auto& x : v) + { + if (first) first = false; + else os << ','; + to_string(os, x); + } + } + }; + #endif + + /* op_from_string */ + + template + struct op_from_string + { + inline bool operator()(const std::string& s, T& value) const + { + std::istringstream ss(s); + ss >> value; + return static_cast(ss); + } + }; + + template<> + struct op_from_string + { + inline bool operator()(const std::string& s, std::string& value) const + { + value = s; + return true; + } + }; + + template<> + struct op_from_string + { + inline bool operator()(const std::string& s, const char*& value) const + { + value = s.c_str(); + return true; + } + }; + + template<> + struct op_from_string + { + inline bool operator()(const std::string& s, bool& value) const + { + const std::string TRUE_STR("true"); + int i; + value = s == "T" + || s == "t" + || std::equal( + s.begin(), s.end(), + TRUE_STR.begin(), TRUE_STR.end(), + [](char a, char b) { + return tolower(a) == tolower(b); + }) + || ( cppcore::try_from_string(s, i) + && i != 0); + return true; + } + }; + + template + struct op_from_string>> + { + inline bool operator()(const std::string& s, T& v) const + { return enum_conversion::try_to_enum(s, v); } + }; + + template + struct op_from_string().from_string(std::declval()), void())> + { + inline bool operator()(const std::string& s, T& v) const + { return v.from_string(s); } + }; + + template + struct op_from_string(), std::declval()), void())> + { + inline bool operator()(const std::string& s, T& v) const + { return T::from_string(s, v); } + }; + + template + struct op_from_string>> + { + inline bool operator()(const std::string& s, T& v) const + { + char *e = nullptr; + const char *c = s.c_str(); + v = static_cast(std::strtoull(c, &e, 0)); + return (c != e); + } + }; + + template + struct op_from_string>> + { + inline bool operator()(const std::string& s, T& value) const + { + char *e = nullptr; + const char *c = s.c_str(); + value = static_cast(std::strtold(c, &e)); + return (c != e); + } + }; + + #ifdef CPPCORE_HAS_VECTOR + template + struct op_from_string, void> + { + inline bool operator()(const std::string& str, std::vector& value) const + { + std::vector tmp; + auto ret = string_split(str, ',', [&](const std::string& s) { + tmp.emplace_back(); + return try_from_string(s, tmp.back()); + }); + if (ret) + value = std::move(tmp); + return ret; + } + }; + #endif + + #ifdef CPPCORE_HAS_LIST + template + struct op_from_string, void> + { + inline bool operator()(const std::string& str, std::list& value) const + { + std::list tmp; + auto ret = string_split(str, ',', [&tmp](auto& s) { + tmp.emplace_back(); + return try_from_string(s, tmp.back()); + }); + if (ret) + value = std::move(tmp); + return ret; + } + }; + #endif + } + +} + + +namespace std +{ + + template + inline auto operator<<(basic_ostream& os, X&& x) + -> decltype( + std::forward(x).to_string(std::declval&>()), + std::declval&>()) + { + std::forward(x).to_string(os); + return os; + } + +} diff --git a/include/cppcore/conversion/time.h b/include/cppcore/conversion/time.h new file mode 100644 index 0000000..79be2ce --- /dev/null +++ b/include/cppcore/conversion/time.h @@ -0,0 +1,65 @@ +#pragma once + +#include +#include + +namespace cppcore +{ + + namespace __impl + { + + /** + * @brief Helper class to convert time values. + */ + template + struct op_convert_time; + + } + + /** + * @brief Cast a chrono duration to a timeval. + * + * @param[in] d Chrono duration to cast. + * + * @return Casted timeval. + */ + template + inline auto duration_cast(const std::chrono::duration& d) + -> std::enable_if_t::value, ::timeval>; + + + /** + * @brief Cast a chrono duration to a timespec. + * + * @param[in] d Chrono duration to cast. + * + * @return Casted timespec. + */ + template + inline auto duration_cast(const std::chrono::duration& d) + -> std::enable_if_t::value, ::timespec>; + + /** + * @brief Cast a timeval to a chrono duration. + * + * @param[in] tv Timeval to cast to chrono duration. + * + * @return Casted chrono duration. + */ + template + Duration duration_cast(const ::timeval& tv); + + /** + * @brief Cast a timespec to a chrono duration. + * + * @param[in] ts Timespec to cast to chrono duration. + * + * @return Casted chrono duration. + */ + template + Duration duration_cast(const ::timespec& ts); + +} + +#include "time.inl" diff --git a/include/cppcore/conversion/time.inl b/include/cppcore/conversion/time.inl new file mode 100644 index 0000000..da480fc --- /dev/null +++ b/include/cppcore/conversion/time.inl @@ -0,0 +1,83 @@ +#pragma once + +#include "time.h" + +namespace cppcore +{ + + namespace __impl + { + + /* op_convert_time */ + + template + struct op_convert_time, ::timeval> + { + static ::timeval cast(const std::chrono::duration& d) + { + ::timeval tv; + const std::chrono::seconds sec = std::chrono::duration_cast(d); + tv.tv_sec = sec.count(); + tv.tv_usec = std::chrono::duration_cast(d - sec).count(); + return tv; + } + + }; + + template + struct op_convert_time<::timeval, std::chrono::duration> + { + static std::chrono::duration cast(const ::timeval& tv) + { + return std::chrono::duration_cast>( + std::chrono::seconds(tv.tv_sec) + std::chrono::microseconds(tv.tv_usec)); + } + }; + + template + struct op_convert_time, ::timespec> + { + static ::timespec cast(const std::chrono::duration& d) + { + ::timespec ts; + const std::chrono::seconds sec = std::chrono::duration_cast(d); + ts.tv_sec = sec.count(); + ts.tv_nsec = std::chrono::duration_cast(d - sec).count(); + return ts; + } + + }; + + template + struct op_convert_time<::timespec, std::chrono::duration> + { + static std::chrono::duration cast(const ::timespec& ts) + { + return std::chrono::duration_cast>( + std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec)); + } + }; + + } + + /* global */ + + template + inline auto duration_cast(const std::chrono::duration& d) + -> std::enable_if_t::value, ::timeval> + { return __impl::op_convert_time, ::timeval>::cast(d); } + + template + inline auto duration_cast(const std::chrono::duration& d) + -> std::enable_if_t::value, ::timespec> + { return __impl::op_convert_time, ::timespec>::cast(d); } + + template + Duration duration_cast(const ::timeval& tv) + { return __impl::op_convert_time<::timeval, Duration>::cast(tv); } + + template + Duration duration_cast(const ::timespec& ts) + { return __impl::op_convert_time<::timespec, Duration>::cast(ts); } + +} diff --git a/include/cppcore/defines.h b/include/cppcore/defines.h new file mode 100644 index 0000000..728a608 --- /dev/null +++ b/include/cppcore/defines.h @@ -0,0 +1,25 @@ +#pragma once + +#define CPPCORE_BYTE_ORDER_UNKNOWN 0 +#define CPPCORE_BYTE_ORDER_LITTLE 1 +#define CPPCORE_BYTE_ORDER_BIG 2 + +#ifdef __BYTE_ORDER__ + #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + #define CPPCORE_BYTE_ORDER CPPCORE_BYTE_ORDER_BIG + #elif __BYTE_ORDER__ == __ORDER_BIG_LITTLE__ + #define CPPCORE_BYTE_ORDER CPPCORE_BYTE_ORDER_LITTLE + #endif +#endif + +#ifdef __BYTE_ORDER + #if __BYTE_ORDER == __BIG_ENDIAN + #define CPPCORE_BYTE_ORDER CPPCORE_BYTE_ORDER_BIG + #elif __BYTE_ORDER == __LITTLE_ENDIAN + #define CPPCORE_BYTE_ORDER CPPCORE_BYTE_ORDER_LITTLE + #endif +#endif + +#ifndef CPPCORE_BYTE_ORDER + #define CPPCORE_BYTE_ORDER CPPCORE_BYTE_ORDER_UNKNOWN +#endif diff --git a/include/cppcore/misc.h b/include/cppcore/misc.h new file mode 100644 index 0000000..78f87c8 --- /dev/null +++ b/include/cppcore/misc.h @@ -0,0 +1,8 @@ +#pragma once + +#include +#include +#include +#include +#include +#include diff --git a/include/cppcore/misc/compare.h b/include/cppcore/misc/compare.h new file mode 100644 index 0000000..b5dfbd4 --- /dev/null +++ b/include/cppcore/misc/compare.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +namespace cppcore +{ + + /** + * @brief Predicate class to caompare to string ignoring the case and variant. + */ + struct op_less_invariant_string + { + /** + * @brief Compare to string. + * + * @param[in] lhs Left hand value. + * @param[in] rhs Right hand value. + * + * @retval true If lhs is less than rhs. + * @retval false If rhs is greater or equal lhs. + */ + inline bool operator()(const std::string& lhs, const std::string& rhs) const; + }; + +} + +#include "compare.inl" diff --git a/include/cppcore/misc/compare.inl b/include/cppcore/misc/compare.inl new file mode 100644 index 0000000..68a4d6c --- /dev/null +++ b/include/cppcore/misc/compare.inl @@ -0,0 +1,29 @@ +#pragma once + +#include "compare.h" + +namespace cppcore +{ + + /* op_less_invariant_string */ + + bool op_less_invariant_string + ::operator()(const std::string& lhs, const std::string& rhs) const + { + auto c1 = lhs.c_str(); + auto c2 = rhs.c_str(); + auto l1 = lhs.size(); + auto l2 = rhs.size(); + while (l1 > 0 && l2 > 0 && std::tolower(*c1) == std::tolower(*c2)) + { + ++c1; + ++c2; + --l1; + --l2; + } + return l1 > 0 && l2 > 0 + ? std::tolower(*c1) < std::tolower(*c2) + : l1 < l2; + } + +} diff --git a/include/cppcore/misc/exception.h b/include/cppcore/misc/exception.h new file mode 100644 index 0000000..e55075c --- /dev/null +++ b/include/cppcore/misc/exception.h @@ -0,0 +1,219 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "flags.h" + +namespace cppcore +{ + + /** + * @brief Basic exception with stack trace and some more small features. + */ + struct exception + : public std::exception + { + public: + enum class print_flag + { + print_trace, + resolve_address, + }; + using print_flags = shifted_flags; + + public: + static constexpr size_t max_stack_size = 15; + + /** + * @brief Get the default print flags. + */ + static inline const print_flags& default_print_flag(); + + /** + * @brief Print the exception to the passed stream; + * + * @param[in] os Stream to print exception to. + * @param[in] ex Exception to print to stream. + * + * @return The stream received as parameter (for further processing). + */ + inline friend std::ostream& operator <<(std::ostream& os, const exception& ex); + + private: + mutable bool _msg_cache_empty; //!< Indicates if the message cache was created or not. + mutable std::string _msg_cache; //!< Caches the message of the exception. + + public: + std::string message; //!< message of the exception + void* stack[max_stack_size]; //!< stack trace + int stack_size; //!< number of entries stored in stack + + public: + /** + * @brief Default constructor. + */ + inline exception(); + + /** + * @brief Constructor to create an exception. + */ + inline exception(std::string msg); + + /** + * @brief Destructor. + */ + virtual inline ~exception() = default; + + /** + * @brief Print the whole exception to the passed stream. + * + * @param[in] os Stream to print exception to. + * @param[in] flags Flags to use for printing. + */ + inline void print( + std::ostream& os, + const print_flags& flags = default_print_flag()) const; + + /** + * @brief Convert the exception to string. + * + * @param[in] flags Flags to use for printing. + * + * @return String representation of the exception. + */ + inline std::string print( + const print_flags& flags = default_print_flag()) const; + + /** + * @brief Convert the exception to a string. + */ + inline std::string to_string() const; + + /** + * @brief Get the message of the exception as c-string. + */ + inline const char* what() const throw() override; + + protected: + /** + * @brief Print the message of the exception to the passed stream. + * + * @param[in] os Stream to print message of the exception to. + */ + virtual inline void print_message(std::ostream& os) const; + }; + + + + /** + * @brief Error exception. Wrapps the errors from the operating system. + */ + struct error_exception + : public exception + { + public: + int error; //!< Error received from the operating system + + public: + /** + * @brief Constructor to create a error exception. + * + * @param[in] e Error code received from the operating system. + */ + inline error_exception(int e); + + /** + * @brief Constructor to create a error exception. + * + * @param[in] msg Message of the error exception. + * @param[in] e Error code received from the operating system. + */ + inline error_exception(const std::string& msg, int e); + }; + + + + /** + * @brief Range exception. + */ + struct range_exception + : public exception + { + public: + size_t min; //!< minimum valid value of the range + size_t max; //!< maximum valid value of the range + size_t index; //!< actual index that was used + + public: + /** + * @brief Constructor to create a range exception. + */ + inline range_exception(size_t mi, size_t ma, size_t idx, std::string msg = ""); + + protected: + /** + * @brief Print the message of the exception to the passed stream. + * + * @param[in] os Stream to print message of the exception to. + */ + inline void print_message(std::ostream& os) const override; + }; + + + + /** + * @brief Argument exception. + */ + struct argument_exception + : public exception + { + public: + std::string argument; //!< argument that caused the exception + + public: + /** + * @brief Constructor to create the argument exception. + * + * @param[in] arg Argument that caused the exception. + * @param[in] msg Message of the exception. + */ + inline argument_exception(std::string arg, std::string msg = ""); + + protected: + /** + * @brief Print the message of the exception to the passed stream. + * + * @param[in] os Stream to print message of the exception to. + */ + inline void print_message(std::ostream& os) const override; + }; + + + + /** + * @brief Invalid operation exception. + */ + struct invalid_operation_exception + : public exception + { + using exception::exception; + }; + + + + /** + * @brief Convert exception. + */ + struct convert_exception + : public exception + { + using exception::exception; + }; + +} + +#include "exception.inl" diff --git a/include/cppcore/misc/exception.inl b/include/cppcore/misc/exception.inl new file mode 100644 index 0000000..258a406 --- /dev/null +++ b/include/cppcore/misc/exception.inl @@ -0,0 +1,124 @@ +#pragma once + +#include "exception.h" + +namespace cppcore +{ + + /* exception */ + + const exception::print_flags& exception::default_print_flag() + { + static const print_flags value({ + print_flag::print_trace, + print_flag::resolve_address, + }); + return value; + } + + exception::exception() + : exception("") + { } + + exception::exception(std::string msg) + : message (msg) + , stack_size (0) + , _msg_cache_empty (true) + { + stack_size = backtrace(&stack[0], max_stack_size); + } + + void exception::print(std::ostream& os, const print_flags& flags) const + { + print_message(os); + if (!flags.is_set(print_flag::print_trace)) + return; + os << std::endl; + char** lines = nullptr; + if (flags.is_set(print_flag::resolve_address)) + lines = backtrace_symbols(stack, stack_size); + for (int i = 0; i < stack_size; ++i) + { + os << " [0x" << std::setw(2 * sizeof(void*)) << std::setfill('0') << std::hex << reinterpret_cast(stack[i]) << "]"; + if (lines && lines[i]) + os << " " << lines[i]; + os << std::endl; + } + } + + std::string exception::print(const print_flags& flags) const + { + std::ostringstream os; + print(os, flags); + return os.str(); + } + + std::string exception::to_string() const + { + if (_msg_cache_empty) + { + _msg_cache = print(); + _msg_cache_empty = false; + } + return _msg_cache.c_str(); + } + + const char* exception::what() const throw() + { return to_string().c_str(); } + + void exception::print_message(std::ostream& os) const + { + os << message; + } + + std::ostream& operator <<(std::ostream& os, const exception& ex) + { + ex.print(os); + return os; + } + + /* error_exception */ + + error_exception::error_exception(int e) + : exception (std::to_string(e) + " - " + strerror(e)) + , error (e) + { } + + error_exception::error_exception(const std::string& msg, int e) : + exception(msg + ": " + std::to_string(e) + " - " + strerror(e)), + error(e) + { } + + /* range_exception */ + + range_exception::range_exception(size_t mi, size_t ma, size_t idx, std::string msg) + : exception (msg) + , min (mi) + , max (ma) + , index (idx) + { } + + void range_exception::print_message(std::ostream& os) const + { + os << "index out of range (min=" << min << "; max=" << max << "; index=" << index << ")"; + if (!message.empty()) + os << " - " << message; + } + + /* argument_exception */ + + argument_exception::argument_exception(std::string arg, std::string msg) + : exception (msg) + , argument (arg) + { } + + void argument_exception::print_message(std::ostream& os) const + { + os << "invalid argument"; + if (!argument.empty()) + os << "(" << argument << ")"; + if (!message.empty()) + os << " - " << message; + } + +} diff --git a/include/cppcore/misc/flags.h b/include/cppcore/misc/flags.h new file mode 100644 index 0000000..b81309d --- /dev/null +++ b/include/cppcore/misc/flags.h @@ -0,0 +1,245 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace cppcore +{ + + namespace __impl + { + + /** + * @brief Helper class to convert enumeration values. + * + * This class does not convert the values of the enum. It will cast the enum directly to the flag. + */ + template + struct op_flag_convert_none + { + /** + * @brief Convert the given enum value to it's underlying flags value. + * + * The enum already represents the flag, so no convertion is needed. + */ + static inline T_base to_base(const T_enum& e); + + /** + * @brief Convert the given flag index to an normal enum value. + * + * To get the enum (which equal to the flag), we need to use the index as shift count. + */ + static inline T_enum from_index(const ssize_t& index); + }; + + /** + * @brief Helper class to convert enumeration values. + * + * This class will shift the enum to generate the suitable flag. + */ + template + struct op_flag_convert_shift + { + /** + * @brief Convert the given enum value to it's underlying flags value. + * + * The enum values are numbered continuously. To get the flag we need to use the enum as shift count. + */ + static inline T_base to_base(const T_enum& e); + + /** + * @brief Convert the given flag index to an normal enum value. + * + * The given enum is equal to the flag index, no conversion is needed. + */ + static inline T_enum from_index(const ssize_t& index); + }; + + } + + /** + * @brief Class to create a bitmask of an enum type. + */ + template< + class T_enum, + class T_base = typename std::underlying_type::type, + class T_op = __impl::op_flag_convert_none> + struct flags + { + public: + using enum_type = T_enum; + using base_type = T_base; + using op_type = T_op; + + public: + /** + * @brief Class to iterate through the flags. + */ + struct iterator + { + private: + static constexpr ssize_t bit_count = static_cast(8 * sizeof(base_type)); + + base_type _base; //!< Flags to iterate over. + ssize_t _pos; //!< Current position. + + public: + /** + * @brief Constructor. + * + * @param[in] base Flasg to iterate over. + * @param[in] is_end This is the end iterator. + */ + inline iterator(const base_type& base, bool is_end); + + /** + * @brief Equality compare operator. + */ + inline bool operator==(const iterator& other) const; + + /** + * @brief Negative equality compare operator. + */ + inline bool operator!=(const iterator& other) const; + + /** + * @brief Dereference the iterator. + */ + inline enum_type operator*() const; + + /** + * @brief Increment operator. + */ + inline iterator& operator++(); + + /** + * @brief Increment operator. + */ + inline iterator operator++(int); + + private: + /** + * @brief Move the iterator to it's next position. + */ + inline void next(); + }; + + public: + base_type value; //!< intenal representation of the flags + + public: /* constructors */ + + /** + * @brief Default constructor. + */ + inline flags(); + + /** + * @brief Constructor to create a flags object from the underlying data type. + */ + inline explicit flags(base_type v); + + /** + * @brief Constructor to create a flags object from the enum type. + */ + inline flags(enum_type e); + + /** + * @brief Copy constructor. + */ + inline flags(const flags& other); + + /** + * @brief Constructor to create flags object from a list of enum values. + */ + inline flags(std::initializer_list list); + + public: /* misc */ + + /** + * @brief Returns the begin iterator. + */ + inline auto begin() const; + + /** + * @brief Returns the end iterator. + */ + inline auto end() const; + + /** + * @brief Check if the given enum value is set in the flags object. + */ + inline bool is_set(enum_type e) const; + + /** + * @brief Set the passed enum value in the flags object. + */ + inline void set(enum_type e); + + /** + * @brief Clear the passed enum value in the flags object. + */ + inline void clear(enum_type e); + + /** + * @brief REset all flags. + */ + inline void reset(); + + public: /* operators */ + + /** + * @brief Get the value of underlying type of the flags object. + */ + inline base_type operator()() const; + + /** + * @brief Implicit conversion to the base type. + */ + inline operator base_type() const; + + /** + * @brief Implicit conversion to bool. + */ + inline explicit operator bool() const; + + /** + * @brief Check if a enum value is set in the flags object. + */ + inline bool operator[](enum_type e) const; + + public: /* static */ + + /** + * @brief Get a flags object with all flags cleared. + */ + static inline const flags& empty(); + + /** + * @brief Get a flags object with all flags set. + */ + static inline const flags& all(); + }; + + /** + * @brief Flag class that uses the enum as is. + */ + template< + class T_enum, + class T_base = typename std::underlying_type::type> + using simple_flags = flags>; + + /** + * @brief Flag class that uses the enum as index. + */ + template< + class T_enum, + class T_base = typename std::underlying_type::type> + using shifted_flags = flags>; + +} + +#include "flags.inl" diff --git a/include/cppcore/misc/flags.inl b/include/cppcore/misc/flags.inl new file mode 100644 index 0000000..923ed61 --- /dev/null +++ b/include/cppcore/misc/flags.inl @@ -0,0 +1,196 @@ +#pragma once + +#include "flags.h" + +namespace cppcore +{ + + namespace __impl + { + + /* op_flag_convert_none */ + + template + T_base op_flag_convert_none + ::to_base(const T_enum& e) + { return static_cast(e); } + + template + T_enum op_flag_convert_none + ::from_index(const ssize_t& index) + { return static_cast(1 << index); } + + /* op_flag_convert_shift */ + + template + T_base op_flag_convert_shift + ::to_base(const T_enum& e) + { return static_cast(1 << static_cast(e)); } + + template + T_enum op_flag_convert_shift + ::from_index(const ssize_t& index) + { return static_cast(index); } + + } + + /* flags::iterator */ + + template + flags::iterator + ::iterator(const base_type& base, bool is_end) + : _base(base) + , _pos (is_end ? bit_count : -1) + { next(); } + + template + bool flags::iterator + ::operator==(const iterator& other) const + { return _base == other._base && _pos == other._pos; } + + template + bool flags::iterator + ::operator!=(const iterator& other) const + { return _base != other._base || _pos != other._pos; } + + template + T_enum flags::iterator + ::operator*() const + { + assert(_pos < bit_count); + return op_type::from_index(_pos); + } + + template + typename flags::iterator& + flags::iterator + ::operator++() + { + next(); + return *this; + } + + template + typename flags::iterator + flags::iterator + ::operator++(int) + { + auto ret = *this; + next(); + return ret; + } + + template + void flags::iterator + ::next() + { + if (_pos >= bit_count) + return; + do + { + ++_pos; + } while(_pos < bit_count && !static_cast(_base & (1 << _pos))); + } + + /* flags */ + + template + flags + ::flags() + : value(0) + { } + + template + flags + ::flags(base_type v) + : value(v) + { } + + template + flags + ::flags(enum_type e) + : value(T_op::to_base(e)) + { } + + template + flags + ::flags(const flags& other) + : value(other.value) + { } + + template + flags + ::flags(std::initializer_list list) + : flags() + { + for (auto& e : list) + set(e); + } + + template + auto flags + ::begin() const + { return iterator(value, false); } + + template + auto flags + ::end() const + { return iterator(value, true); } + + template + bool flags + ::is_set(enum_type e) const + { return static_cast(value & op_type::to_base(e)); } + + template + void flags + ::set(enum_type e) + { value = static_cast(value | op_type::to_base(e)); } + + template + void flags + ::clear(enum_type e) + { value = static_cast(value & ~op_type::to_base(e)); } + + template + void flags + ::reset() + { value = 0; } + + template + T_base flags + ::operator()() const + { return value; } + + template + flags + ::operator base_type() const + { return static_cast(value); } + + template + flags + ::operator bool() const + { return static_cast(value); } + + template + bool flags + ::operator[](enum_type e) const + { return is_set(e); } + + template + const flags& flags + ::empty() + { + const flags value(0); + return value; + } + + template + const flags& flags + ::all() + { + const flags value(std::numeric_limits::max()); + return value; + } + +} diff --git a/include/cppcore/misc/indent.h b/include/cppcore/misc/indent.h new file mode 100644 index 0000000..273497a --- /dev/null +++ b/include/cppcore/misc/indent.h @@ -0,0 +1,70 @@ +#pragma once + +#include + +namespace cppcore +{ + + namespace __impl + { + constexpr long default_indent = 4; + + inline int indent_stream_index() + { + static const int value = std::ios::xalloc(); + return value; + } + + inline int indent_count_stream_index() + { + static const int value = std::ios::xalloc(); + return value; + } + + struct set_indent_impl + { int value; }; + } + + inline auto setindent(int value) + { return __impl::set_indent_impl { value }; } + + inline std::ostream& incindent(std::ostream& os) + { + ++os.iword(__impl::indent_stream_index()); + return os; + } + + inline std::ostream& decindent(std::ostream& os) + { + auto& indent = os.iword(__impl::indent_stream_index()); + if (--indent < 0) + indent = 0; + return os; + } + + inline std::ostream& indent(std::ostream& os) + { + auto i = os.iword(__impl::indent_stream_index()); + auto c = __impl::default_indent + os.iword(__impl::indent_count_stream_index()); + i *= c; + if (i >= 0) + { + os << std::endl; + while (i--) + os.put(' '); + } + return os; + } + +} + +namespace std +{ + template + inline basic_ostream& operator<< (basic_ostream& os, const cppcore::__impl::set_indent_impl& i) + { + using namespace ::cppcore; + os.iword(__impl::indent_count_stream_index()) = i.value - __impl::default_indent; + return os; + } +} diff --git a/include/cppcore/misc/stream.h b/include/cppcore/misc/stream.h new file mode 100644 index 0000000..9218148 --- /dev/null +++ b/include/cppcore/misc/stream.h @@ -0,0 +1,209 @@ +#pragma once + +#include +#include + +namespace cppcore +{ + + namespace __impl + { + + /** + * @brief Predicate class to read any type from the stream. + */ + template + struct op_stream_read; + + /** + * @brief Predicate class to write any type to the stream. + */ + template + struct op_stream_write; + + /** + * @brief Traits class to wrapp the needed types. + */ + struct stream_helper_traits + { + template + using write_stream_predicate_type = op_stream_write; + + template + using read_stream_predicate_type = op_stream_read; + }; + + } + + /** + * @brief Helper class to read/write data to/from stream. + * + * @tparam T_traits Traits class to use for the stream operations. + */ + template + struct stream_helper_t + { + using traits_type = T_traits; + + /** + * @brief Write a value to the stream. + * + * @tparam T Type of the value to write to the stream. + * + * @param[in] os Stream to write data to. + * @param[in] t Value to write to stream. + * + * @return Number of bytes written. + */ + template + static inline size_t write(std::ostream& os, const T& t); + + /** + * @brief Read a value from the stream. + * + * @tparam T Type of the value to read from the stream. + * + * @param[in] is Stream to read data from. + * @param[out] t Parameter to store read value in. + * + * @return Number of read bytes. + */ + template + static inline size_t read(std::istream& is, T& t); + + /** + * @brief Read a value from the stream. + * + * @tparam T Type of the value to read from the stream. + * + * @param[in] is Stream to read data from. + * + * @return Value read from the stream. + */ + template + static inline T read(std::istream& is); + }; + + /** + * @brief Stream helper class with default traits. + */ + struct stream_helper + : public stream_helper_t<__impl::stream_helper_traits> + { }; + + + + /** + * @brief Class to write values to the given stream. + */ + template + struct stream_writer_t + { + public: + using traits_type = T_traits; + using stream_helper_type = stream_helper_t; + + public: + std::ostream& stream; //!< stream to write data to + + public: + /** + * @brief Constructor. + */ + inline stream_writer_t(std::ostream& p_stream); + + /** + * @brief Write a value to the stream. + * + * @tparam T Type of the value to write to the stream. + * + * @param[in] t Value to write to stream. + */ + template + inline void write(const T& t); + }; + + /** + * @brief Stream write class with default traits. + */ + struct stream_writer + : public stream_writer_t<__impl::stream_helper_traits> + { }; + + + + /** + * @brief Class to read values from the given stream. + */ + template + struct stream_reader_t + { + public: + using traits_type = T_traits; + using stream_helper_type = stream_helper_t; + + public: + std::istream& stream; //!< stream to write data to + + public: + /** + * @brief Constructor. + */ + inline stream_reader_t(std::istream& p_stream); + + /** + * @brief Read a value from the stream. + * + * @tparam T Type of the value to read from the stream. + * + * @param[out] t Parameter to store read value in. + * + * @return Number of read bytes. + */ + template + inline size_t read(T& t); + + /** + * @brief Read a value from the stream. + * + * @tparam T Type of the value to read from the stream. + * + * @return Value read from the stream. + */ + template + inline T read(std::istream& is); + }; + + /** + * @brief Stream reader class with default traits. + */ + struct stream_reader + : public stream_reader_t<__impl::stream_helper_traits> + { }; + + + + /** + * @brief Helper class to save stream format. + * + * This class stores the format of the passed stream and restore it, if it is destroyed- + */ + struct stream_format_saver + : private std::ios + { + std::ostream& stream; + + /** + * @brief Constructor. + */ + inline stream_format_saver(std::ostream& s); + + /** + * @brief Destructor. + */ + inline ~stream_format_saver(); + }; + +} + +#include "stream.inl" diff --git a/include/cppcore/misc/stream.inl b/include/cppcore/misc/stream.inl new file mode 100644 index 0000000..233ccee --- /dev/null +++ b/include/cppcore/misc/stream.inl @@ -0,0 +1,176 @@ +#pragma once + +#include "stream.h" + +namespace cppcore +{ + + /* stream_helper_t */ + + template + template + size_t stream_helper_t + ::write(std::ostream& os, const T& t) + { return __impl::op_stream_write()(os, t); } + + template + template + size_t stream_helper_t + ::read(std::istream& is, T& t) + { return __impl::op_stream_read()(is, t); } + + template + template + T stream_helper_t + ::read(std::istream& is) + { + T t; + read(is, t); + return std::move(t); + } + + /* stream_writer_t */ + + template + stream_writer_t + ::stream_writer_t(std::ostream& p_stream) + : stream(p_stream) + { } + + template + template + void stream_writer_t + ::write(const T& t) + { stream_helper_type::template write(stream, t); } + + /* stream_reader_t */ + + template + stream_reader_t + ::stream_reader_t(std::istream& p_stream) + : stream(p_stream) + { } + + template + template + size_t stream_reader_t + ::read(T& t) + { return stream_helper_type::template read(stream, t); } + + template + template + T stream_reader_t + ::read(std::istream& is) + { return stream_helper_type::template read(stream); } + + /* stream_format_saver */ + + inline stream_format_saver + ::stream_format_saver(std::ostream& s) + : stream(s) + { copyfmt(stream); } + + inline stream_format_saver + ::~stream_format_saver() + { stream.copyfmt(*this); } + + namespace __impl + { + + /* op_stream_read */ + + template + struct op_stream_read + { + inline size_t operator()(std::istream& is, T& t) + { + if (!is) + throw exception("unable to read data from stream: invalid stream"); + if (is.read(reinterpret_cast(&t), sizeof(t)).gcount() != sizeof(t)) + throw exception("unable to read data from stream: EOF"); + if (!is) + throw exception("unable to read data from stream: stream error"); + return sizeof(t); + } + }; + + template<> + struct op_stream_read + { + inline size_t operator()(std::istream& is, std::string& t) + { + uint32_t sz; + op_stream_read()(is, sz); + if (!is) + throw exception("unable to read data from stream: invalid stream"); + + std::string tmp; + tmp.resize(sz); + if (is.read(const_cast(tmp.data()), sz).gcount() != sz) + throw exception("unable to read data from stream: EOF"); + if (!is) + throw exception("unable to read data from stream: stream error"); + t = std::move(tmp); + + return sizeof(sz) + sz; + } + }; + + template + struct op_stream_read().deserialize(std::declval()), void())> + { + inline size_t operator()(std::istream& is, T& t) + { + auto pos = is.tellg(); + t.deserialize(is); + return static_cast(is.tellg() - pos); + } + }; + + /* op_stream_write */ + + template + struct op_stream_write + { + inline size_t operator()(std::ostream& os, const T& t) const + { + if (!os) + throw exception("unable to write data to stream: invalid stream"); + os.write(reinterpret_cast(&t), sizeof(t)); + if (!os) + throw exception("unable to write data to stream: stream error"); + return sizeof(t); + } + }; + + template<> + struct op_stream_write + { + inline size_t operator()(std::ostream& os, const std::string& t) + { + if (t.size() > std::numeric_limits::max()) + throw exception("unable to write data to stream: string is to large"); + op_stream_write()(os, static_cast(t.size())); + if (!os) + throw exception("unable to write data to stream: invalid stream"); + os.write(t.data(), static_cast(t.size())); + if (!os) + throw exception("unable to write data to stream: stream error"); + return sizeof(uint32_t) + t.size(); + } + }; + + template + struct op_stream_write().serialize(std::declval()), void())> + { + inline size_t operator()(std::ostream& os, const T& t) + { + auto pos = os.tellp(); + t.serialize(os); + return static_cast(os.tellp() - pos); + } + }; + + } + +} diff --git a/include/cppcore/misc/type_helper.h b/include/cppcore/misc/type_helper.h new file mode 100644 index 0000000..d580639 --- /dev/null +++ b/include/cppcore/misc/type_helper.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include + +namespace cppcore +{ + + /** + * @brief Class to get meta infos for a specific type. + */ + template + struct type_helper + { + public: + /** + * @brief Get the name of the type. + */ + static inline std::string name(); + }; + + + + /** + * @brief Implements a unique counter for the given template parameters. + */ + template + struct unique_counter + { + /** + * @brief Get the next value of the counter. + */ + static inline size_t& next(); + }; + + + + /** + * @brief Get a unique ID for the passed template arguments. + * + * @tparam T_counter Counter to use to generate the unique ID. + * @tparam X Type to generate the ID for. + */ + template + inline size_t get_unique_id(); + +} + +#include "type_helper.inl" diff --git a/include/cppcore/misc/type_helper.inl b/include/cppcore/misc/type_helper.inl new file mode 100644 index 0000000..75dfa23 --- /dev/null +++ b/include/cppcore/misc/type_helper.inl @@ -0,0 +1,39 @@ +#pragma once + +#include "type_helper.h" + +namespace cppcore +{ + + template + std::string type_helper::name() + { + int status; + auto name = abi::__cxa_demangle(typeid(T).name(), 0, 0, &status); + return std::string(name ? name : typeid(T).name()); + } + + /** + * @brief Implements a unique counter for the given template parameters. + */ + template + size_t& unique_counter::next() + { + static size_t value { }; + return ++value; + } + + /** + * @brief Get a unique ID for the passed template arguments. + * + * @tparam T_counter Counter to use to generate the unique ID. + * @tparam X Type to generate the ID for. + */ + template + size_t get_unique_id() + { + static const auto v = unique_counter::next(); + return v; + } + +} diff --git a/include/cppcore/misc/utils.h b/include/cppcore/misc/utils.h new file mode 100644 index 0000000..0c5df36 --- /dev/null +++ b/include/cppcore/misc/utils.h @@ -0,0 +1,28 @@ +#pragma once + +namespace cppcore +{ + + /** + * @brief Count the bit set in the passed value using HACKMEM bitcount. + */ + inline size_t bit_count(uint32_t u); + + /** + * @breif Try to dynamic cast the passed value to the given type. + * + * @tparam T Type to cast. + * @tparam S Type to cast to. + * + * @param[in] t Value to cast. + * @param[out] s Parameter to store casted value in. + * + * @retval true If the cast was succesful. + * @retval false If the cast failed. + */ + template + inline bool try_cast(T* t, S*& s); + +} + +#include "utils.inl" diff --git a/include/cppcore/misc/utils.inl b/include/cppcore/misc/utils.inl new file mode 100644 index 0000000..378f43b --- /dev/null +++ b/include/cppcore/misc/utils.inl @@ -0,0 +1,23 @@ +#pragma once + +#include "utils.h" + +namespace cppcore +{ + + size_t bit_count(uint32_t u) + { + u = u + - ((u >> 1) & 033333333333) + - ((u >> 2) & 011111111111); + return static_cast(((u + (u >> 3)) & 030707070707) % 63); + } + + template + bool try_cast(T* t, S*& s) + { + s = dynamic_cast(t); + return static_cast(s); + } + +} diff --git a/include/cppcore/threading.h b/include/cppcore/threading.h new file mode 100644 index 0000000..1bf9ef9 --- /dev/null +++ b/include/cppcore/threading.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/include/cppcore/threading/cancellation_token.h b/include/cppcore/threading/cancellation_token.h new file mode 100644 index 0000000..6d63d23 --- /dev/null +++ b/include/cppcore/threading/cancellation_token.h @@ -0,0 +1,107 @@ +#pragma once + +#include +#include + +namespace cppcore +{ + + /** + * @brief Exception to throw to terminate the current operation. + */ + struct cancellation_exception + : public std::exception + { + inline const char* what() const throw() override; + }; + + /** + * @brief Excaption to throw to terminate the current operation with some additional data. + */ + template + struct data_cancellation_exception + : public cancellation_exception + { + T data; + + /** + * @brief Constructor. + */ + inline data_cancellation_exception(const T& d); + }; + + /** + * @brief Token to pass to an operation to check if an cancellation is requested. + */ + struct cancellation_token + { + private: + /** + * @brief Is set to true if the cancellation is requested. + */ + std::atomic _is_cancellation_requested; + + public: + /** + * @brief Constructor. + */ + inline cancellation_token(); + + /** + * @brief Destructor. + */ + virtual ~cancellation_token() = default; + + /** + * @brief Returns true if a cancellation was requested, false otherwise. + */ + inline bool is_cancellation_requested() const; + + /** + * @brief Throw a cancellation exception if cancellation was requested. + */ + virtual void throw_if_cancellation_requested() const = 0; + + protected: + /** + * @brief Request to cancel the current operation. + */ + inline void cancel(); + + private: + /** + * @brief Move constructor. + */ + cancellation_token(cancellation_token&&) = delete; + + /** + * @brief Copy constructor. + */ + cancellation_token(const cancellation_token&) = delete; + }; + + /** + * @brief Source of a cancellation token. Can be used to cancel the current operation. + */ + template + struct cancellation_source + : public cancellation_token + { + private: + T _value; //!< Value to pass to the cancellation exception. + + public: + /** + * @brief Throw a cancellation exception if cancellation was requested. + */ + inline void throw_if_cancellation_requested() const override; + + /** + * @brief Request to cancel the current operation. + */ + inline void cancel(const T& value); + }; + +} + +#include "cancellation_token.inl" diff --git a/include/cppcore/threading/cancellation_token.inl b/include/cppcore/threading/cancellation_token.inl new file mode 100644 index 0000000..5b4202b --- /dev/null +++ b/include/cppcore/threading/cancellation_token.inl @@ -0,0 +1,78 @@ +#pragma once + +#include "cancellation_token.h" + +namespace cppcore +{ + + /* cancellation_exception */ + + const char* cancellation_exception + ::what() const throw() + { return "task was canceled!"; } + + /* data_cancellation_exception */ + + template + data_cancellation_exception::data_cancellation_exception(const T& d) + : data(d) + { } + + /* data_cancellation_exception */ + + template<> + struct data_cancellation_exception + : public cancellation_exception + { }; + + /* cancellation_token */ + + cancellation_token + ::cancellation_token() + : _is_cancellation_requested(false) + { } + + bool cancellation_token + ::is_cancellation_requested() const + { return _is_cancellation_requested; } + + void cancellation_token + ::cancel() + { _is_cancellation_requested = true; } + + /* cancellation_source */ + + template + void cancellation_source + ::throw_if_cancellation_requested() const + { + if (is_cancellation_requested()) + throw data_cancellation_exception(_value); + } + + template + void cancellation_source + ::cancel(const T& value) + { + cancellation_token::cancel(); + _value = value; + } + + /* cancellation_source */ + + template<> + struct cancellation_source + : public cancellation_token + { + public: + inline void throw_if_cancellation_requested() const + { + if (is_cancellation_requested()) + throw cancellation_exception(); + } + + inline void cancel() + { cancellation_token::cancel(); } + }; + +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..350bf52 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,25 @@ +# Initialize ###################################################################################### + +Include ( cotire OPTIONAL RESULT_VARIABLE HAS_COTIRE ) +Include ( pedantic OPTIONAL RESULT_VARIABLE HAS_PEDANTIC ) +Include ( strip_symbols OPTIONAL RESULT_VARIABLE HAS_STRIP_SYMBOLS ) + +# Interface Library ############################################################################### + +Set ( CPPCORE_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../include ) +Add_Library ( cppcore INTERFACE ) +Target_Include_Directories ( cppcore + INTERFACE + $ + $ ) + +# Install ######################################################################################### + +# Header +Install ( FILES ${CPPCORE_INCLUDE_DIR}/cppcore.h + DESTINATION ${CPPCORE_INSTALL_DIR_INCLUDE} ) +Install ( DIRECTORY ${CPPCORE_INCLUDE_DIR}/cppcore + DESTINATION ${CPPCORE_INSTALL_DIR_INCLUDE} ) +Install ( TARGETS cppcore + EXPORT cppcore + DESTINATION ${CPPCORE_INSTALL_DIR_INCLUDE} ) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..ec69fff --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,44 @@ +# Initialize ###################################################################################### + +Include ( cotire OPTIONAL RESULT_VARIABLE HAS_COTIRE ) +Include ( pedantic OPTIONAL RESULT_VARIABLE HAS_PEDANTIC ) +Include ( cmake_tests OPTIONAL RESULT_VARIABLE HAS_CMAKE_TESTS ) + +# Test ############################################################################################ + +Find_Package ( GTest ) +If ( NOT "${GTest_FOUND}" ) + Return ( ) +EndIf ( ) + +File ( GLOB_RECURSE CPPCORE_TEST_HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.h ) +File ( GLOB_RECURSE CPPCORE_TEST_INLINE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.inl ) +File ( GLOB_RECURSE CPPCORE_TEST_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ) + +Add_Executable ( cppcore-test + EXCLUDE_FROM_ALL + ${CPPCORE_TEST_HEADER_FILES} + ${CPPCORE_TEST_INLINE_FILES} + ${CPPCORE_TEST_SOURCE_FILES} ) +Target_Link_Libraries ( cppcore-test + PUBLIC + cppcore + GTest::Main ) + +# optimization +If ( HAS_COTIRE ) + Cotire ( cppcore-test ) +EndIf ( ) + +# pedantic +If ( HAS_PEDANTIC ) + Pedantic_Apply_Flags_Target ( cppcore-test + ALL ) +EndIf ( ) + +# test +If ( HAS_CMAKE_TESTS ) + Add_CMake_Test ( NAME cppcore TARGET cppcore-test ) +Else ( ) + Add_Test ( NAME cppcore COMMAND cppcore-test ) +EndIf ( ) diff --git a/test/cppcore/conversion/byteorder_tests.cpp b/test/cppcore/conversion/byteorder_tests.cpp new file mode 100644 index 0000000..1b8e188 --- /dev/null +++ b/test/cppcore/conversion/byteorder_tests.cpp @@ -0,0 +1,39 @@ +#include +#include +#include + +using namespace ::cppcore; + +TEST(byteorder_tests, hton) +{ +#if CPPCORE_BYTE_ORDER == CPPCORE_BYTE_ORDER_LITTLE + EXPECT_EQ(hton(static_cast (0x12)), 0x12); + EXPECT_EQ(hton(static_cast(0x1234)), 0x3412); + EXPECT_EQ(hton(static_cast(0x12345678)), 0x78563412); + EXPECT_EQ(hton(static_cast(0x0123456789ABCDEF)), 0xEFCDAB8967452301); +#elif CPPCORE_BYTE_ORDER == CPPCORE_BYTE_ORDER_BIG + EXPECT_EQ(hton(static_cast (0x12)), 0x12); + EXPECT_EQ(hton(static_cast(0x1234)), 0x1234); + EXPECT_EQ(hton(static_cast(0x12345678)), 0x12345678); + EXPECT_EQ(hton(static_cast(0x0123456789ABCDEF)), 0x0123456789ABCDEF); +#else + #error "Please add test!" +#endif +} + +TEST(byteorder_tests, ntoh) +{ +#if CPPCORE_BYTE_ORDER == CPPCORE_BYTE_ORDER_LITTLE + EXPECT_EQ(ntoh(static_cast (0x12)), 0x12); + EXPECT_EQ(ntoh(static_cast(0x1234)), 0x3412); + EXPECT_EQ(ntoh(static_cast(0x12345678)), 0x78563412); + EXPECT_EQ(ntoh(static_cast(0x0123456789ABCDEF)), 0xEFCDAB8967452301); +#elif CPPCORE_BYTE_ORDER == CPPCORE_BYTE_ORDER_BIG + EXPECT_EQ(ntoh(static_cast (0x12)), 0x12); + EXPECT_EQ(ntoh(static_cast(0x1234)), 0x1234); + EXPECT_EQ(ntoh(static_cast(0x12345678)), 0x12345678); + EXPECT_EQ(ntoh(static_cast(0x0123456789ABCDEF)), 0x0123456789ABCDEF); +#else + #error "Please add test!" +#endif +} diff --git a/test/cppcore/conversion/enum_tests.cpp b/test/cppcore/conversion/enum_tests.cpp new file mode 100644 index 0000000..70531e0 --- /dev/null +++ b/test/cppcore/conversion/enum_tests.cpp @@ -0,0 +1,58 @@ +#include +#include +#include + +enum class TestEnum +{ + Test0, + Test1, + Test2, + Test3 +}; + +cppcore_define_enum_value_traits( + TestEnum, + { TestEnum::Test0, "Test0" }, + { TestEnum::Test1, "Test1" }, + { TestEnum::Test2, "Test2" }, + { TestEnum::Test3, "Test3" } +); + +using namespace ::cppcore; +using namespace ::testing; + +TEST(enum_tests, to_string) +{ + EXPECT_EQ(std::string("Test1"), to_string(TestEnum::Test1)); + EXPECT_EQ(std::string("Test0(0)"), enum_conversion::to_string(TestEnum::Test0, true)); + EXPECT_EQ(std::string("Test1"), enum_conversion::to_string(TestEnum::Test1, false)); + EXPECT_EQ(std::string("Test2(2)"), enum_conversion::to_string(TestEnum::Test2, true)); + EXPECT_EQ(std::string("Test3"), enum_conversion::to_string(TestEnum::Test3, false)); + EXPECT_EQ(std::string("123"), enum_conversion::to_string(static_cast(123), true)); +} + +TEST(enum_tests, from_string) +{ + TestEnum e; + EXPECT_FALSE(try_from_string("abc", e)); + EXPECT_TRUE (try_from_string("test1", e)); + EXPECT_EQ (TestEnum::Test1, e); + EXPECT_THROW(enum_conversion::to_enum("abc"), convert_exception); +} + +TEST(enum_tests, to_enum) +{ + TestEnum e; + EXPECT_TRUE (enum_conversion::try_to_enum("test0", e, false)); + EXPECT_EQ (TestEnum::Test0, e); + + EXPECT_TRUE (enum_conversion::try_to_enum("Test1", e, false)); + EXPECT_EQ (TestEnum::Test1, e); + + EXPECT_FALSE(enum_conversion::try_to_enum("asd", e, false)); + + EXPECT_FALSE(enum_conversion::try_to_enum("1", e, false)); + + EXPECT_TRUE (enum_conversion::try_to_enum("2", e, true)); + EXPECT_EQ (TestEnum::Test2, e); +} diff --git a/test/cppcore/conversion/string_tests.cpp b/test/cppcore/conversion/string_tests.cpp new file mode 100644 index 0000000..fb65b8e --- /dev/null +++ b/test/cppcore/conversion/string_tests.cpp @@ -0,0 +1,95 @@ +#include +#include +#include +#include + +struct TestClass1 +{ + int i; + + std::string to_string() const + { return std::to_string(i); } + + bool from_string(const std::string& s) + { return static_cast(std::istringstream(s) >> i); } +}; + +struct TestClass2 +{ + int i; + + void to_string(std::ostream& os) const + { os << i; } + + static bool from_string(const std::string& s, TestClass2& v) + { return static_cast(std::istringstream(s) >> v.i); } +}; + +using namespace ::testing; + +TEST(string_tests, to_string) +{ + EXPECT_EQ(std::string("4.5"), ::cppcore::to_string(4.5)); + EXPECT_EQ(std::string("test"), ::cppcore::to_string("test")); + EXPECT_EQ(std::string("fuu"), ::cppcore::to_string(std::string("fuu"))); + EXPECT_EQ(std::string("5"), ::cppcore::to_string(TestClass1 { 5 })); + EXPECT_EQ(std::string("6"), ::cppcore::to_string(TestClass2 { 6 })); +} + +TEST(string_tests, from_string) +{ + double d; + EXPECT_FALSE(::cppcore::try_from_string("abc", d)); + EXPECT_TRUE (::cppcore::try_from_string("4.5", d)); + EXPECT_TRUE (d >= 4.5 && d <= 4.5); + + int i; + EXPECT_FALSE(::cppcore::try_from_string("abc", i)); + EXPECT_TRUE (::cppcore::try_from_string("123", i)); + EXPECT_EQ (123, i); + + TestClass1 t1; + EXPECT_FALSE(::cppcore::try_from_string("abc", t1)); + EXPECT_TRUE (::cppcore::try_from_string("11", t1)); + EXPECT_EQ (11, t1.i); + + TestClass2 t2; + EXPECT_FALSE(::cppcore::try_from_string("abc", t2)); + EXPECT_TRUE (::cppcore::try_from_string("12", t2)); + EXPECT_EQ (12, t2.i); +} + +TEST(string_tests, shift_operator) +{ + std::ostringstream ss; + ss << TestClass2 { 6 }; + EXPECT_EQ(std::string("6"), ss.str()); +} + +TEST(string_tests, vector_to_string) +{ + EXPECT_EQ( + "1,2,3,4,5", + ::cppcore::to_string(std::vector { 1, 2, 3, 4, 5 })); +} + +TEST(string_tests, list_to_string) +{ + EXPECT_EQ( + "1,2,3,4,5", + ::cppcore::to_string(std::list { 1, 2, 3, 4, 5 })); +} + +TEST(string_tests, string_to_vector) +{ + EXPECT_EQ( + (std::vector { 1, 245, 3, 4, 5 }), + ::cppcore::from_string>("1, 245, 3, 4, 5")); +} + +TEST(string_tests, string_to_list) +{ + EXPECT_EQ( + (std::list { 123, 2, 3, 4, 5 }), + ::cppcore::from_string>("123, 2, 3, 4, 5")); +} diff --git a/test/cppcore/conversion/time_tests.cpp b/test/cppcore/conversion/time_tests.cpp new file mode 100644 index 0000000..c2ccc68 --- /dev/null +++ b/test/cppcore/conversion/time_tests.cpp @@ -0,0 +1,30 @@ +#include +#include + +using namespace ::cppcore; + +TEST(time_tests, duration_to_timespec) +{ + auto ts1 = duration_cast(std::chrono::milliseconds(123456)); + EXPECT_EQ(123, ts1.tv_sec); + EXPECT_EQ(456000000, ts1.tv_nsec); +} + +TEST(time_tests, duration_to_timeval) +{ + auto tv1 = duration_cast(std::chrono::milliseconds(123456)); + EXPECT_EQ(123, tv1.tv_sec); + EXPECT_EQ(456000, tv1.tv_usec); +} + +TEST(time_tests, timespec_to_duration) +{ + auto d1 = duration_cast(timespec{ 1, 234567890 }); + EXPECT_EQ(1234567, d1.count()); +} + +TEST(time_tests, timeval_to_duration) +{ + auto d1 = duration_cast(timeval{ 1, 234567 }); + EXPECT_EQ(1234567, d1.count()); +} diff --git a/test/cppcore/misc/flags_tests.cpp b/test/cppcore/misc/flags_tests.cpp new file mode 100644 index 0000000..448ee6f --- /dev/null +++ b/test/cppcore/misc/flags_tests.cpp @@ -0,0 +1,101 @@ +#include +#include + +using namespace ::cppcore; +using namespace ::testing; + +enum class TestFlag : uint8_t +{ + value0, + value1, + value2, + value3, + value4 +}; + +using Testflags = shifted_flags; + +TEST(flags_tests, ctor) +{ + Testflags f0; + EXPECT_EQ(0, f0()); + + Testflags f1({ TestFlag::value1, TestFlag::value3 }); + EXPECT_EQ(10, f1()); + + Testflags f2(f1); + EXPECT_EQ(10, f2()); + + Testflags f3(TestFlag::value3); + EXPECT_EQ(8, f3()); +} + +TEST(flags_tests, set) +{ + Testflags f; + EXPECT_FALSE(static_cast(f)); + f.set(TestFlag::value1); + EXPECT_TRUE(static_cast(f)); + EXPECT_TRUE(f[TestFlag::value1]); +} + +TEST(flags_tests, is_set) +{ + Testflags f({ TestFlag::value1, TestFlag::value3 }); + + EXPECT_FALSE(f[TestFlag::value0]); + EXPECT_TRUE (f[TestFlag::value1]); + EXPECT_FALSE(f[TestFlag::value2]); + EXPECT_TRUE (f[TestFlag::value3]); + EXPECT_FALSE(f[TestFlag::value4]); + + EXPECT_FALSE(f.is_set(TestFlag::value0)); + EXPECT_TRUE (f.is_set(TestFlag::value1)); + EXPECT_FALSE(f.is_set(TestFlag::value2)); + EXPECT_TRUE (f.is_set(TestFlag::value3)); + EXPECT_FALSE(f.is_set(TestFlag::value4)); +} + +TEST(flags_tests, clear) +{ + Testflags f({ TestFlag::value1, TestFlag::value3 }); + f.clear(TestFlag::value1); + EXPECT_FALSE(f.is_set(TestFlag::value1)); + EXPECT_TRUE (f.is_set(TestFlag::value3)); +} + +TEST(flags_tests, reset) +{ + Testflags f({ TestFlag::value1, TestFlag::value3 }); + f.reset(); + EXPECT_FALSE(static_cast(f)); +} + +TEST(flags_tests, iterator) +{ + Testflags f({ TestFlag::value0, TestFlag::value1, TestFlag::value3 }); + auto it = f.begin(); + auto end = f.end(); + + EXPECT_FALSE(it == end); + EXPECT_TRUE (it != end); + EXPECT_EQ (TestFlag::value0, *it); + ++it; + + EXPECT_FALSE(it == end); + EXPECT_TRUE (it != end); + EXPECT_EQ (TestFlag::value1, *it); + ++it; + + EXPECT_FALSE(it == end); + EXPECT_TRUE (it != end); + EXPECT_EQ (TestFlag::value3, *it); + ++it; + + EXPECT_TRUE (it == end); + EXPECT_FALSE(it != end); + ++it; + + EXPECT_TRUE (it == end); + EXPECT_FALSE(it != end); +} diff --git a/test/cppcore/misc/stream_tests.cpp b/test/cppcore/misc/stream_tests.cpp new file mode 100644 index 0000000..0b1dac4 --- /dev/null +++ b/test/cppcore/misc/stream_tests.cpp @@ -0,0 +1,84 @@ +#include +#include + +namespace stream_helper_tests +{ + struct SimpleStruct + { + uint32_t u32; + uint8_t u8; + } + __attribute__((__packed__)); + + struct ManagedStruct + { + std::string s; + + void serialize(std::ostream& os) const + { ::cppcore::stream_helper::write(os, s); } + + void deserialize(std::istream& is) + { ::cppcore::stream_helper::read(is, s); } + }; +} + +using namespace ::cppcore; +using namespace ::testing; +using namespace ::stream_helper_tests; + +TEST(stream_tests, write_scalar) +{ + std::ostringstream os; + stream_helper::write(os, static_cast(0x12345678)); + EXPECT_EQ(std::string("\x78\x56\x34\x12", 4), os.str()); +} + +TEST(stream_tests, write_string) +{ + std::ostringstream os; + stream_helper::write(os, std::string("test")); + EXPECT_EQ(std::string("\x04\x00\x00\x00test", 8), os.str()); +} + +TEST(stream_tests, write_simpleStruct) +{ + std::ostringstream os; + stream_helper::write(os, SimpleStruct { 0x78563412, 5 }); + EXPECT_EQ(std::string("\x12\x34\x56\x78\x05", 5), os.str()); +} + +TEST(stream_tests, write_managedStruct) +{ + std::ostringstream os; + stream_helper::write(os, ManagedStruct { "test" }); + EXPECT_EQ(std::string("\x04\x00\x00\x00test", 8), os.str()); +} + +TEST(stream_tests, read_scalar) +{ + std::istringstream is(std::string("\x78\x56\x34\x12", 4)); + auto ret = stream_helper::read(is); + EXPECT_EQ(0x12345678, ret); +} + +TEST(stream_tests, read_string) +{ + std::istringstream is(std::string("\x04\x00\x00\x00test", 8)); + auto ret = stream_helper::read(is); + EXPECT_EQ(std::string("test"), ret); +} + +TEST(stream_tests, read_simpleStruct) +{ + std::istringstream is(std::string("\x12\x34\x56\x78\x05", 5)); + auto ret = stream_helper::read(is); + EXPECT_EQ(0x78563412, ret.u32); + EXPECT_EQ(5, ret.u8); +} + +TEST(stream_tests, read_managedStruct) +{ + std::istringstream is(std::string("\x04\x00\x00\x00test", 8)); + auto ret = stream_helper::read(is); + EXPECT_EQ(std::string("test"), ret.s); +} diff --git a/test/cppcore/threading/cancellation_token_tests.cpp b/test/cppcore/threading/cancellation_token_tests.cpp new file mode 100644 index 0000000..4cb198e --- /dev/null +++ b/test/cppcore/threading/cancellation_token_tests.cpp @@ -0,0 +1,15 @@ +#include +#include + +using namespace ::cppcore; + +TEST(cancellation_token_tests, cancel) +{ + cancellation_source cancellation; + + cancellation.throw_if_cancellation_requested(); + + cancellation.cancel(5); + + EXPECT_THROW(cancellation.throw_if_cancellation_requested(), data_cancellation_exception); +}