Browse Source

* Initial commit

* Moved and refactored code from existing cpputils library
master
bergmann 6 years ago
commit
bb839d27ed
43 changed files with 3510 additions and 0 deletions
  1. +1
    -0
      .gitignore
  2. +3
    -0
      .gitmodules
  3. +53
    -0
      CMakeLists.txt
  4. +4
    -0
      cmake/cppcore-config.cmake
  5. +20
    -0
      cmake/cppcore-var.cmake
  6. +1
    -0
      cmake/modules
  7. +6
    -0
      include/cppcore.h
  8. +6
    -0
      include/cppcore/conversion.h
  9. +107
    -0
      include/cppcore/conversion/byteorder.h
  10. +87
    -0
      include/cppcore/conversion/byteorder.inl
  11. +166
    -0
      include/cppcore/conversion/enum.h
  12. +117
    -0
      include/cppcore/conversion/enum.inl
  13. +223
    -0
      include/cppcore/conversion/string.h
  14. +422
    -0
      include/cppcore/conversion/string.inl
  15. +65
    -0
      include/cppcore/conversion/time.h
  16. +83
    -0
      include/cppcore/conversion/time.inl
  17. +25
    -0
      include/cppcore/defines.h
  18. +8
    -0
      include/cppcore/misc.h
  19. +27
    -0
      include/cppcore/misc/compare.h
  20. +29
    -0
      include/cppcore/misc/compare.inl
  21. +219
    -0
      include/cppcore/misc/exception.h
  22. +124
    -0
      include/cppcore/misc/exception.inl
  23. +245
    -0
      include/cppcore/misc/flags.h
  24. +196
    -0
      include/cppcore/misc/flags.inl
  25. +70
    -0
      include/cppcore/misc/indent.h
  26. +209
    -0
      include/cppcore/misc/stream.h
  27. +176
    -0
      include/cppcore/misc/stream.inl
  28. +49
    -0
      include/cppcore/misc/type_helper.h
  29. +39
    -0
      include/cppcore/misc/type_helper.inl
  30. +28
    -0
      include/cppcore/misc/utils.h
  31. +23
    -0
      include/cppcore/misc/utils.inl
  32. +3
    -0
      include/cppcore/threading.h
  33. +107
    -0
      include/cppcore/threading/cancellation_token.h
  34. +78
    -0
      include/cppcore/threading/cancellation_token.inl
  35. +25
    -0
      src/CMakeLists.txt
  36. +44
    -0
      test/CMakeLists.txt
  37. +39
    -0
      test/cppcore/conversion/byteorder_tests.cpp
  38. +58
    -0
      test/cppcore/conversion/enum_tests.cpp
  39. +95
    -0
      test/cppcore/conversion/string_tests.cpp
  40. +30
    -0
      test/cppcore/conversion/time_tests.cpp
  41. +101
    -0
      test/cppcore/misc/flags_tests.cpp
  42. +84
    -0
      test/cppcore/misc/stream_tests.cpp
  43. +15
    -0
      test/cppcore/threading/cancellation_token_tests.cpp

+ 1
- 0
.gitignore View File

@@ -0,0 +1 @@
build/

+ 3
- 0
.gitmodules View File

@@ -0,0 +1,3 @@
[submodule "cmake/modules"]
path = cmake/modules
url = b3rgmann@git.bergmann89.de:cpp/CmakeModules.git

+ 53
- 0
CMakeLists.txt View File

@@ -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 )

+ 4
- 0
cmake/cppcore-config.cmake View File

@@ -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 )

+ 20
- 0
cmake/cppcore-var.cmake View File

@@ -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 )

+ 1
- 0
cmake/modules

@@ -0,0 +1 @@
Subproject commit 1a32531aef2deeebd5637b1873bc4e976628801c

+ 6
- 0
include/cppcore.h View File

@@ -0,0 +1,6 @@
#pragma once

#include <cppcore/conversion.h>
#include <cppcore/define.h>
#include <cppcore/misc.h>
#include <cppcore/threading.h>

+ 6
- 0
include/cppcore/conversion.h View File

@@ -0,0 +1,6 @@
#pragma once

#include <cppcore/conversion/byteorder.h>
#include <cppcore/conversion/enum.h>
#include <cppcore/conversion/string.h>
#include <cppcore/conversion/time.h>

+ 107
- 0
include/cppcore/conversion/byteorder.h View File

@@ -0,0 +1,107 @@
#pragma once

#if defined(__linux__)
#include <endian.h>
#elif defined(__FreeBSD__) || defined(__NetBSD__)
#include <sys/endian.h>
#elif defined(__OpenBSD__)
#include <sys/types.h>
#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<class T, size_t N = sizeof(T)>
struct byteorder_convert_helper;

/**
* @brief Trait class for byteorder conversions.
*/
struct byteorder_traits
{
template<typename T>
using convert_helper = byteorder_convert_helper<T>;
};

}

/**
* @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<class T, typename T_traits = __impl::byteorder_traits>
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<class T, typename T_traits = __impl::byteorder_traits>
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<typename T_traits>
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<class T>
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<class T>
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"

+ 87
- 0
include/cppcore/conversion/byteorder.inl View File

@@ -0,0 +1,87 @@
#pragma once

#include "byteorder.h"

namespace cppcore
{

/* static */

template<class T, typename T_traits>
constexpr decltype(auto) hton(const T& value)
{ return byteorder_t<T_traits>::template hton<T>(value); }

template<class T, typename T_traits>
constexpr decltype(auto) ntoh(const T& value)
{ return byteorder_t<T_traits>::template ntoh<T>(value); }

/* byteorder */

template<typename T_traits>
template<class T>
constexpr decltype(auto) byteorder_t<T_traits>
::hton(const T& value)
{ return traits_type::template convert_helper<T>::hton(value); }

template<typename T_traits>
template<class T>
constexpr decltype(auto) byteorder_t<T_traits>
::ntoh(const T& value)
{ return traits_type::template convert_helper<T>::ntoh(value); }

namespace __impl
{
/**
* @brief Helper class to convert type of size with 1 byte.
*/
template<class T>
struct byteorder_convert_helper<T, 1>
{
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<class T>
struct byteorder_convert_helper<T, 2>
{
static inline T hton(const T& t)
{ return reinterpret_cast<T>(htobe16(reinterpret_cast<uint16_t>(t))); }

static inline T ntoh(const T& t)
{ return reinterpret_cast<T>(be16toh(reinterpret_cast<uint16_t>(t))); }
};

/**
* @brief Helper class to convert type of size with 4 byte.
*/
template<class T>
struct byteorder_convert_helper<T, 4>
{
static inline T hton(const T& t)
{ return reinterpret_cast<T>(htobe32(reinterpret_cast<uint32_t>(t))); }

static inline T ntoh(const T& t)
{ return reinterpret_cast<T>(be32toh(reinterpret_cast<uint32_t>(t))); }
};

/**
* @brief Helper class to convert type of size with 8 byte.
*/
template<class T>
struct byteorder_convert_helper<T, 8>
{
static inline T hton(const T& t)
{ return reinterpret_cast<T>(htobe64(reinterpret_cast<uint64_t>(t))); }

static inline T ntoh(const T& t)
{ return reinterpret_cast<T>(be64toh(reinterpret_cast<uint64_t>(t))); }
};
}

}

+ 166
- 0
include/cppcore/conversion/enum.h View File

@@ -0,0 +1,166 @@
#pragma once

#include <map>
#include <vector>

#include <cppcore/misc/compare.h>
#include <cppcore/misc/exception.h>
#include <cppcore/misc/type_helper.h>
// #include <cppcore/conversion/string.h>

#define cppcore_define_enum_value_traits(enum, ...) \
namespace cppcore { \
namespace __impl { \
template<> \
struct enum_value_traits<enum> \
{ \
using enum_type = enum; \
using enum_value_pair_type = std::pair<enum_type, std::string>; \
using enum_value_vector_type = std::vector<enum_value_pair_type>; \
\
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<typename T_enum>
struct enum_value_traits
{
using enum_type = T_enum;
using enum_value_pair_type = std::pair<enum_type, std::string>;
using enum_value_vector_type = std::vector<enum_value_pair_type>;

/**
* @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<T_enum>>
struct enum_conversion_traits
{
public:
using enum_type = T_enum;
using traits_type = enum_value_traits<enum_type>;
using enum_to_string_map_type = std::map<enum_type, std::string>;
using string_to_enum_map_type = std::map<std::string, enum_type, op_less_invariant_string>;

/**
* @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<T_enum>>
struct enum_conversion
{
using enum_type = T_enum;
using base_type = typename std::underlying_type<enum_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"

+ 117
- 0
include/cppcore/conversion/enum.inl View File

@@ -0,0 +1,117 @@
#pragma once

#include "enum.h"

namespace cppcore
{

namespace __impl
{

/* enum_conversion_traits */

template<typename T_enum, typename T_traits>
decltype(auto) enum_conversion_traits<T_enum, T_traits>
::get_enum_to_string_map()
{
static const auto value = create_enum_to_string_map();
return value;
}

template<typename T_enum, typename T_traits>
decltype(auto) enum_conversion_traits<T_enum, T_traits>
::get_string_to_enum_map()
{
static const auto value = create_string_to_enum_map();
return value;
}

template<typename T_enum, typename T_traits>
decltype(auto) enum_conversion_traits<T_enum, T_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<typename T_enum, typename T_traits>
decltype(auto) enum_conversion_traits<T_enum, T_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<typename T_enum, typename T_traits>
std::string enum_conversion<T_enum, T_traits>
::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<base_type>(value)) + ')'; // TODO
}
else
ret = std::to_string(static_cast<base_type>(value)); // TODO
return ret;
}

template<typename T_enum, typename T_traits>
bool enum_conversion<T_enum, T_traits>
::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<enum_type>(std::strtoull(c, &e, 0));
return (c != e);
}

template<typename T_enum, typename T_traits>
T_enum enum_conversion<T_enum, T_traits>
::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<enum_type>::name() + "'"s);
return value;
}

template<typename T_enum, typename T_traits>
T_enum enum_conversion<T_enum, T_traits>
::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;
}

}

+ 223
- 0
include/cppcore/conversion/string.h View File

@@ -0,0 +1,223 @@
#pragma once

#include <string>
#include <iostream>

namespace cppcore
{

namespace __impl
{

/**
* @brief Predicate class to convert any type to a string using a stream.
*/
template<typename T, typename Enable = void>
struct op_to_stream;

/**
* @brief Predicate class to convert any type to a string.
*/
template<typename T, typename Enable = void>
struct op_to_string;

/**
* @brief Predicate class to convert a string to any type.
*/
template<typename T, typename Enable = void>
struct op_from_string;

/**
* @brief Traits class to store needed data types.
*/
struct string_conversion_traits
{
template<typename T>
using to_stream_predicate_type = op_to_stream<T>;

template<typename T>
using to_string_predicate_type = op_to_string<T>;

template<typename T>
using from_string_predicate_type = op_from_string<T>;
};

}

/**
* @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<typename T, typename T_traits = __impl::string_conversion_traits>
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<typename T, typename T_traits = __impl::string_conversion_traits>
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<typename T, typename T_traits = __impl::string_conversion_traits>
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<typename T, typename T_traits = __impl::string_conversion_traits>
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<typename T, typename T_traits = __impl::string_conversion_traits>
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<typename T_predicate>
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<typename T_traits>
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<typename T>
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<typename T>
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<typename T>
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<typename T>
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<typename T>
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<typename T_char, typename T_traits, typename X>
inline auto operator<<(basic_ostream<T_char, T_traits>& os, X&& x)
-> decltype(
std::forward<X>(x).to_string(std::declval<basic_ostream<T_char, T_traits>&>()),
std::declval<basic_ostream<T_char, T_traits>&>());

}

#include "string.inl"

+ 422
- 0
include/cppcore/conversion/string.inl View File

@@ -0,0 +1,422 @@
#pragma once

#include <cppcore/misc/exception.h>
#include <cppcore/misc/type_helper.h>

#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<typename T, typename T_traits>
inline std::string to_string(const T& value)
{ return string_conversion_t<T_traits>::template to_string<T>(value); }

template<typename T, typename T_traits>
inline void to_string(std::ostream& os, const T& value)
{ string_conversion_t<T_traits>::template to_string<T>(os, value); }

template<typename T, typename T_traits>
inline bool try_from_string(const std::string& s, T& value)
{ return string_conversion_t<T_traits>::template try_from_string<T>(s, value); }

template<typename T, typename T_traits>
inline T from_string(const std::string& s)
{ return string_conversion_t<T_traits>::template from_string<T>(s); }

template<typename T, typename T_traits>
inline T from_string_default(const std::string& s, const T& default_value)
{ return string_conversion_t<T_traits>::template from_string_default<T>(s, default_value); }

template<typename T_predicate>
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<size_t>(i - s));
if (!predicate(tmp))
return false;
s = i + 1;
}
++i;
}
return true;
}

/* string_conversion */

template<typename T_traits>
template<typename T>
inline std::string string_conversion_t<T_traits>
::to_string(const T& value)
{
using predicate_type = typename traits_type::template to_string_predicate_type<T>;
return predicate_type { } (value);
}

template<typename T_traits>
template<typename T>
inline void string_conversion_t<T_traits>
::to_string(std::ostream& os, const T& value)
{
using predicate_type = typename traits_type::template to_stream_predicate_type<T>;
predicate_type { } (os, value);
}

template<typename T_traits>
template<typename T>
inline bool string_conversion_t<T_traits>
::try_from_string(const std::string& s, T& value)
{
using predicate_type = typename traits_type::template from_string_predicate_type<T>;
return predicate_type { } (s, value);
}

template<typename T_traits>
template<typename T>
inline T string_conversion_t<T_traits>
::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<T>::name() + "'"s);
return ret;
}

template<typename T_traits>
template<typename T>
inline T string_conversion_t<T_traits>
::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<typename T, typename Enable>
struct op_to_string
{
inline std::string operator()(const T& v) const
{ return std::to_string(v); }
};

template<typename T>
struct op_to_string<T, std::enable_if_t<std::is_enum_v<T>>>
{
inline std::string operator()(const T& v) const
{ return enum_conversion<T>::to_string(v); }
};

template<typename T>
struct op_to_string<T, std::enable_if_t<std::is_floating_point_v<T>>>
{
inline std::string operator()(const T& v) const
{
std::stringstream os;
op_to_stream<T> { } (os, v);
return os.str();
}
};

template<>
struct op_to_string<std::string, void>
{
inline std::string operator()(const std::string& v) const
{ return v; }
};

template<typename T, size_t N>
struct op_to_string<T[N], std::enable_if_t<std::is_same_v<std::decay_t<T>, char>>>
{
using value_type = T[N];

inline std::string operator()(const value_type& v) const
{ return std::string(v, N-1); }
};

template<typename T>
struct op_to_string<T, decltype(std::declval<T>().to_string(std::declval<std::ostream&>()), void())>
{
inline std::string operator()(const T& v) const
{
std::ostringstream os;
v.to_string(os);
return os.str();
}
};

template<typename T>
struct op_to_string<T, decltype(std::declval<T>().to_string(), void())>
{
inline std::string operator()(const T& v) const
{ return v.to_string(); }
};

#ifdef CPPCORE_HAS_VECTOR
template<typename T>
struct op_to_string<std::vector<T>, void>
{
inline std::string operator()(const std::vector<T>& v) const
{
std::ostringstream os;
op_to_stream<std::vector<T>> { } (os, v);
return os.str();
}
};
#endif

#ifdef CPPCORE_HAS_LIST
template<typename T>
struct op_to_string<std::list<T>, void>
{
inline std::string operator()(const std::list<T>& v) const
{
std::ostringstream os;
op_to_stream<std::list<T>> { } (os, v);
return os.str();
}
};
#endif

/* op_to_stream */

template<typename T, typename Enable>
struct op_to_stream
{
inline void operator()(std::ostream& os, const T& v) const
{ os << v; }
};

template<typename T>
struct op_to_stream<T, std::enable_if_t<std::is_enum_v<T>>>
{
inline void operator()(std::ostream& os, const T& v) const
{ os << enum_conversion<T>::to_string(v); }
};

template<typename T>
struct op_to_stream<T, decltype(std::declval<T>().to_string(std::declval<std::ostream&>()), void())>
{
inline void operator()(std::ostream& os, const T& v) const
{ v.to_string(os); }
};

template<typename T>
struct op_to_stream<T, decltype(std::declval<T>().to_string(), void())>
{
inline void operator()(std::ostream& os, const T& v) const
{ os << v.to_string(); }
};

#ifdef CPPCORE_HAS_VECTOR
template<typename T>
struct op_to_stream<std::vector<T>, void>
{
inline void operator()(std::ostream& os, const std::vector<T>& 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<typename T>
struct op_to_stream<std::list<T>, void>
{
inline void operator()(std::ostream& os, const std::list<T>& v) const
{
bool first = true;
for (auto& x : v)
{
if (first) first = false;
else os << ',';
to_string(os, x);
}
}
};
#endif

/* op_from_string */

template<typename T, typename Enable>
struct op_from_string
{
inline bool operator()(const std::string& s, T& value) const
{
std::istringstream ss(s);
ss >> value;
return static_cast<bool>(ss);
}
};

template<>
struct op_from_string<std::string, void>
{
inline bool operator()(const std::string& s, std::string& value) const
{
value = s;
return true;
}
};

template<>
struct op_from_string<const char*, void>
{
inline bool operator()(const std::string& s, const char*& value) const
{
value = s.c_str();
return true;
}
};

template<>
struct op_from_string<bool, void>
{
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<typename T>
struct op_from_string<T, std::enable_if_t<std::is_enum_v<T>>>
{
inline bool operator()(const std::string& s, T& v) const
{ return enum_conversion<T>::try_to_enum(s, v); }
};

template<typename T>
struct op_from_string<T, decltype(std::declval<T&>().from_string(std::declval<const std::string&>()), void())>
{
inline bool operator()(const std::string& s, T& v) const
{ return v.from_string(s); }
};

template<typename T>
struct op_from_string<T, decltype(T::from_string(std::declval<const std::string&>(), std::declval<T&>()), void())>
{
inline bool operator()(const std::string& s, T& v) const
{ return T::from_string(s, v); }
};

template<typename T>
struct op_from_string<T, typename std::enable_if_t<std::is_integral_v<T>>>
{
inline bool operator()(const std::string& s, T& v) const
{
char *e = nullptr;
const char *c = s.c_str();
v = static_cast<T>(std::strtoull(c, &e, 0));
return (c != e);
}
};

template<typename T>
struct op_from_string<T, typename std::enable_if_t<std::is_floating_point_v<T>>>
{
inline bool operator()(const std::string& s, T& value) const
{
char *e = nullptr;
const char *c = s.c_str();
value = static_cast<T>(std::strtold(c, &e));
return (c != e);
}
};

#ifdef CPPCORE_HAS_VECTOR
template<typename T>
struct op_from_string<std::vector<T>, void>
{
inline bool operator()(const std::string& str, std::vector<T>& value) const
{
std::vector<T> 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<typename T>
struct op_from_string<std::list<T>, void>
{
inline bool operator()(const std::string& str, std::list<T>& value) const
{
std::list<T> 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<typename T_char, typename T_traits, typename X>
inline auto operator<<(basic_ostream<T_char, T_traits>& os, X&& x)
-> decltype(
std::forward<X>(x).to_string(std::declval<basic_ostream<T_char, T_traits>&>()),
std::declval<basic_ostream<T_char, T_traits>&>())
{
std::forward<X>(x).to_string(os);
return os;
}

}

+ 65
- 0
include/cppcore/conversion/time.h View File

@@ -0,0 +1,65 @@
#pragma once

#include <chrono>
#include <utility>

namespace cppcore
{

namespace __impl
{

/**
* @brief Helper class to convert time values.
*/
template<class From, class To>
struct op_convert_time;

}

/**
* @brief Cast a chrono duration to a timeval.
*
* @param[in] d Chrono duration to cast.
*
* @return Casted timeval.
*/
template<typename T, typename Rep, typename Period>
inline auto duration_cast(const std::chrono::duration<Rep, Period>& d)
-> std::enable_if_t<std::is_same<T, ::timeval>::value, ::timeval>;


/**
* @brief Cast a chrono duration to a timespec.
*
* @param[in] d Chrono duration to cast.
*
* @return Casted timespec.
*/
template<typename T, typename Rep, typename Period>
inline auto duration_cast(const std::chrono::duration<Rep, Period>& d)
-> std::enable_if_t<std::is_same<T, ::timespec>::value, ::timespec>;

/**
* @brief Cast a timeval to a chrono duration.
*
* @param[in] tv Timeval to cast to chrono duration.
*
* @return Casted chrono duration.
*/
template<typename Duration>
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<typename Duration>
Duration duration_cast(const ::timespec& ts);

}

#include "time.inl"

+ 83
- 0
include/cppcore/conversion/time.inl View File

@@ -0,0 +1,83 @@
#pragma once

#include "time.h"

namespace cppcore
{

namespace __impl
{

/* op_convert_time */

template<typename Rep, typename Period>
struct op_convert_time<std::chrono::duration<Rep, Period>, ::timeval>
{
static ::timeval cast(const std::chrono::duration<Rep, Period>& d)
{
::timeval tv;
const std::chrono::seconds sec = std::chrono::duration_cast<std::chrono::seconds>(d);
tv.tv_sec = sec.count();
tv.tv_usec = std::chrono::duration_cast<std::chrono::microseconds>(d - sec).count();
return tv;
}

};

template<typename Rep, typename Period>
struct op_convert_time<::timeval, std::chrono::duration<Rep, Period>>
{
static std::chrono::duration<Rep, Period> cast(const ::timeval& tv)
{
return std::chrono::duration_cast<std::chrono::duration<Rep, Period>>(
std::chrono::seconds(tv.tv_sec) + std::chrono::microseconds(tv.tv_usec));
}
};

template<typename Rep, typename Period>
struct op_convert_time<std::chrono::duration<Rep, Period>, ::timespec>
{
static ::timespec cast(const std::chrono::duration<Rep, Period>& d)
{
::timespec ts;
const std::chrono::seconds sec = std::chrono::duration_cast<std::chrono::seconds>(d);
ts.tv_sec = sec.count();
ts.tv_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(d - sec).count();
return ts;
}

};

template<typename Rep, typename Period>
struct op_convert_time<::timespec, std::chrono::duration<Rep, Period>>
{
static std::chrono::duration<Rep, Period> cast(const ::timespec& ts)
{
return std::chrono::duration_cast<std::chrono::duration<Rep, Period>>(
std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec));
}
};

}

/* global */

template<typename T, typename Rep, typename Period>
inline auto duration_cast(const std::chrono::duration<Rep, Period>& d)
-> std::enable_if_t<std::is_same<T, ::timeval>::value, ::timeval>
{ return __impl::op_convert_time<std::chrono::duration<Rep, Period>, ::timeval>::cast(d); }

template<typename T, typename Rep, typename Period>
inline auto duration_cast(const std::chrono::duration<Rep, Period>& d)
-> std::enable_if_t<std::is_same<T, ::timespec>::value, ::timespec>
{ return __impl::op_convert_time<std::chrono::duration<Rep, Period>, ::timespec>::cast(d); }

template<typename Duration>
Duration duration_cast(const ::timeval& tv)
{ return __impl::op_convert_time<::timeval, Duration>::cast(tv); }

template<typename Duration>
Duration duration_cast(const ::timespec& ts)
{ return __impl::op_convert_time<::timespec, Duration>::cast(ts); }

}

+ 25
- 0
include/cppcore/defines.h View File

@@ -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

+ 8
- 0
include/cppcore/misc.h View File

@@ -0,0 +1,8 @@
#pragma once

#include <cppcore/misc/exception.h>
#include <cppcore/misc/flags.h>
#include <cppcore/misc/indent.h>
#include <cppcore/misc/stream.h>
#include <cppcore/misc/typehelper.h>
#include <cppcore/misc/utils.h>

+ 27
- 0
include/cppcore/misc/compare.h View File

@@ -0,0 +1,27 @@
#pragma once

#include <string>

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"

+ 29
- 0
include/cppcore/misc/compare.inl View File

@@ -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;
}

}

+ 219
- 0
include/cppcore/misc/exception.h View File

@@ -0,0 +1,219 @@
#pragma once

#include <string>
#include <cstdint>
#include <iomanip>
#include <string.h>
#include <execinfo.h>

#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<print_flag>;

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"

+ 124
- 0
include/cppcore/misc/exception.inl View File

@@ -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<uintptr_t>(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;
}

}

+ 245
- 0
include/cppcore/misc/flags.h View File

@@ -0,0 +1,245 @@
#pragma once

#include <stdio.h>
#include <limits>
#include <cassert>
#include <type_traits>
#include <initializer_list>

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<class T_enum, class T_base>
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<class T_enum, class T_base>
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<T_enum>::type,
class T_op = __impl::op_flag_convert_none<T_enum, T_base>>
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<ssize_t>(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<enum_type> 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<T_enum>::type>
using simple_flags = flags<T_enum, T_base, __impl::op_flag_convert_none<T_enum, T_base>>;

/**
* @brief Flag class that uses the enum as index.
*/
template<
class T_enum,
class T_base = typename std::underlying_type<T_enum>::type>
using shifted_flags = flags<T_enum, T_base, __impl::op_flag_convert_shift<T_enum, T_base>>;

}

#include "flags.inl"

+ 196
- 0
include/cppcore/misc/flags.inl View File

@@ -0,0 +1,196 @@
#pragma once

#include "flags.h"

namespace cppcore
{

namespace __impl
{

/* op_flag_convert_none */

template<class T_enum, class T_base>
T_base op_flag_convert_none<T_enum, T_base>
::to_base(const T_enum& e)
{ return static_cast<T_base>(e); }

template<class T_enum, class T_base>
T_enum op_flag_convert_none<T_enum, T_base>
::from_index(const ssize_t& index)
{ return static_cast<T_enum>(1 << index); }

/* op_flag_convert_shift */

template<class T_enum, class T_base>
T_base op_flag_convert_shift<T_enum, T_base>
::to_base(const T_enum& e)
{ return static_cast<T_base>(1 << static_cast<int>(e)); }

template<class T_enum, class T_base>
T_enum op_flag_convert_shift<T_enum, T_base>
::from_index(const ssize_t& index)
{ return static_cast<T_enum>(index); }

}

/* flags::iterator */

template<class T_enum, class T_base, class T_op>
flags<T_enum, T_base, T_op>::iterator
::iterator(const base_type& base, bool is_end)
: _base(base)
, _pos (is_end ? bit_count : -1)
{ next(); }

template<class T_enum, class T_base, class T_op>
bool flags<T_enum, T_base, T_op>::iterator
::operator==(const iterator& other) const
{ return _base == other._base && _pos == other._pos; }

template<class T_enum, class T_base, class T_op>
bool flags<T_enum, T_base, T_op>::iterator
::operator!=(const iterator& other) const
{ return _base != other._base || _pos != other._pos; }

template<class T_enum, class T_base, class T_op>
T_enum flags<T_enum, T_base, T_op>::iterator
::operator*() const
{
assert(_pos < bit_count);
return op_type::from_index(_pos);
}

template<class T_enum, class T_base, class T_op>
typename flags<T_enum, T_base, T_op>::iterator&
flags<T_enum, T_base, T_op>::iterator
::operator++()
{
next();
return *this;
}

template<class T_enum, class T_base, class T_op>
typename flags<T_enum, T_base, T_op>::iterator
flags<T_enum, T_base, T_op>::iterator
::operator++(int)
{
auto ret = *this;
next();
return ret;
}

template<class T_enum, class T_base, class T_op>
void flags<T_enum, T_base, T_op>::iterator
::next()
{
if (_pos >= bit_count)
return;
do
{
++_pos;
} while(_pos < bit_count && !static_cast<bool>(_base & (1 << _pos)));
}

/* flags */

template<class T_enum, class T_base, class T_op>
flags<T_enum, T_base, T_op>
::flags()
: value(0)
{ }

template<class T_enum, class T_base, class T_op>
flags<T_enum, T_base, T_op>
::flags(base_type v)
: value(v)
{ }

template<class T_enum, class T_base, class T_op>
flags<T_enum, T_base, T_op>
::flags(enum_type e)
: value(T_op::to_base(e))
{ }

template<class T_enum, class T_base, class T_op>
flags<T_enum, T_base, T_op>
::flags(const flags& other)
: value(other.value)
{ }

template<class T_enum, class T_base, class T_op>
flags<T_enum, T_base, T_op>
::flags(std::initializer_list<enum_type> list)
: flags()
{
for (auto& e : list)
set(e);
}

template<class T_enum, class T_base, class T_op>
auto flags<T_enum, T_base, T_op>
::begin() const
{ return iterator(value, false); }

template<class T_enum, class T_base, class T_op>
auto flags<T_enum, T_base, T_op>
::end() const
{ return iterator(value, true); }

template<class T_enum, class T_base, class T_op>
bool flags<T_enum, T_base, T_op>
::is_set(enum_type e) const
{ return static_cast<bool>(value & op_type::to_base(e)); }

template<class T_enum, class T_base, class T_op>
void flags<T_enum, T_base, T_op>
::set(enum_type e)
{ value = static_cast<base_type>(value | op_type::to_base(e)); }

template<class T_enum, class T_base, class T_op>
void flags<T_enum, T_base, T_op>
::clear(enum_type e)
{ value = static_cast<base_type>(value & ~op_type::to_base(e)); }

template<class T_enum, class T_base, class T_op>
void flags<T_enum, T_base, T_op>
::reset()
{ value = 0; }

template<class T_enum, class T_base, class T_op>
T_base flags<T_enum, T_base, T_op>
::operator()() const
{ return value; }

template<class T_enum, class T_base, class T_op>
flags<T_enum, T_base, T_op>
::operator base_type() const
{ return static_cast<base_type>(value); }

template<class T_enum, class T_base, class T_op>
flags<T_enum, T_base, T_op>
::operator bool() const
{ return static_cast<bool>(value); }

template<class T_enum, class T_base, class T_op>
bool flags<T_enum, T_base, T_op>
::operator[](enum_type e) const
{ return is_set(e); }

template<class T_enum, class T_base, class T_op>
const flags<T_enum, T_base, T_op>& flags<T_enum, T_base, T_op>
::empty()
{
const flags value(0);
return value;
}

template<class T_enum, class T_base, class T_op>
const flags<T_enum, T_base, T_op>& flags<T_enum, T_base, T_op>
::all()
{
const flags value(std::numeric_limits<base_type>::max());
return value;
}

}

+ 70
- 0
include/cppcore/misc/indent.h View File

@@ -0,0 +1,70 @@
#pragma once

#include <iostream>

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<typename T_char, typename T_traits>
inline basic_ostream<T_char, T_traits>& operator<< (basic_ostream<T_char, T_traits>& 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;
}
}

+ 209
- 0
include/cppcore/misc/stream.h View File

@@ -0,0 +1,209 @@
#pragma once

#include <iostream>
#include <cppcore/misc/exception.h>

namespace cppcore
{

namespace __impl
{

/**
* @brief Predicate class to read any type from the stream.
*/
template<class T, class T_enable = void>
struct op_stream_read;

/**
* @brief Predicate class to write any type to the stream.
*/
template<class T, class T_enable = void>
struct op_stream_write;

/**
* @brief Traits class to wrapp the needed types.
*/
struct stream_helper_traits
{
template<typename T>
using write_stream_predicate_type = op_stream_write<T>;

template<typename T>
using read_stream_predicate_type = op_stream_read<T>;
};

}

/**
* @brief Helper class to read/write data to/from stream.
*
* @tparam T_traits Traits class to use for the stream operations.
*/
template<typename T_traits>
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<class T>
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<class T>
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<class T>
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<typename T_traits >
struct stream_writer_t
{
public:
using traits_type = T_traits;
using stream_helper_type = stream_helper_t<traits_type>;

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<class T>
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<typename T_traits>
struct stream_reader_t
{
public:
using traits_type = T_traits;
using stream_helper_type = stream_helper_t<traits_type>;

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<class T>
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<class T>
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"

+ 176
- 0
include/cppcore/misc/stream.inl View File

@@ -0,0 +1,176 @@
#pragma once

#include "stream.h"

namespace cppcore
{

/* stream_helper_t */

template<typename T_traits>
template<class T>
size_t stream_helper_t<T_traits>
::write(std::ostream& os, const T& t)
{ return __impl::op_stream_write<T>()(os, t); }

template<typename T_traits>
template<class T>
size_t stream_helper_t<T_traits>
::read(std::istream& is, T& t)
{ return __impl::op_stream_read<T>()(is, t); }

template<typename T_traits>
template<class T>
T stream_helper_t<T_traits>
::read(std::istream& is)
{
T t;
read<T>(is, t);
return std::move(t);
}

/* stream_writer_t */

template<typename T_traits>
stream_writer_t<T_traits>
::stream_writer_t(std::ostream& p_stream)
: stream(p_stream)
{ }

template<typename T_traits>
template<class T>
void stream_writer_t<T_traits>
::write(const T& t)
{ stream_helper_type::template write<T>(stream, t); }

/* stream_reader_t */

template<typename T_traits>
stream_reader_t<T_traits>
::stream_reader_t(std::istream& p_stream)
: stream(p_stream)
{ }

template<typename T_traits>
template<class T>
size_t stream_reader_t<T_traits>
::read(T& t)
{ return stream_helper_type::template read<T>(stream, t); }

template<typename T_traits>
template<class T>
T stream_reader_t<T_traits>
::read(std::istream& is)
{ return stream_helper_type::template read<T>(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<class T, class Enable>
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<char*>(&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<std::string, void>
{
inline size_t operator()(std::istream& is, std::string& t)
{
uint32_t sz;
op_stream_read<uint32_t, void>()(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<char*>(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<class T>
struct op_stream_read<T, decltype(std::declval<T>().deserialize(std::declval<std::istream&>()), void())>
{
inline size_t operator()(std::istream& is, T& t)
{
auto pos = is.tellg();
t.deserialize(is);
return static_cast<size_t>(is.tellg() - pos);
}
};

/* op_stream_write */

template<class T, class Enable>
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<const char*>(&t), sizeof(t));
if (!os)
throw exception("unable to write data to stream: stream error");
return sizeof(t);
}
};

template<>
struct op_stream_write<std::string, void>
{
inline size_t operator()(std::ostream& os, const std::string& t)
{
if (t.size() > std::numeric_limits<uint32_t>::max())
throw exception("unable to write data to stream: string is to large");
op_stream_write<uint32_t, void>()(os, static_cast<uint32_t>(t.size()));
if (!os)
throw exception("unable to write data to stream: invalid stream");
os.write(t.data(), static_cast<std::streamsize>(t.size()));
if (!os)
throw exception("unable to write data to stream: stream error");
return sizeof(uint32_t) + t.size();
}
};

template<class T>
struct op_stream_write<T, decltype(std::declval<T>().serialize(std::declval<std::ostream&>()), void())>
{
inline size_t operator()(std::ostream& os, const T& t)
{
auto pos = os.tellp();
t.serialize(os);
return static_cast<size_t>(os.tellp() - pos);
}
};

}

}

+ 49
- 0
include/cppcore/misc/type_helper.h View File

@@ -0,0 +1,49 @@
#pragma once

#include <cxxabi.h>
#include <string>

namespace cppcore
{

/**
* @brief Class to get meta infos for a specific type.
*/
template<typename T>
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<typename...>
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<typename T_counter, typename... X>
inline size_t get_unique_id();

}

#include "type_helper.inl"

+ 39
- 0
include/cppcore/misc/type_helper.inl View File

@@ -0,0 +1,39 @@
#pragma once

#include "type_helper.h"

namespace cppcore
{

template<typename T>
std::string type_helper<T>::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<typename... T_args>
size_t& unique_counter<T_args...>::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<typename T_counter, typename... X>
size_t get_unique_id()
{
static const auto v = unique_counter<T_counter>::next();
return v;
}

}

+ 28
- 0
include/cppcore/misc/utils.h View File

@@ -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<class T, class S>
inline bool try_cast(T* t, S*& s);

}

#include "utils.inl"

+ 23
- 0
include/cppcore/misc/utils.inl View File

@@ -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<size_t>(((u + (u >> 3)) & 030707070707) % 63);
}

template<class T, class S>
bool try_cast(T* t, S*& s)
{
s = dynamic_cast<S*>(t);
return static_cast<bool>(s);
}

}

+ 3
- 0
include/cppcore/threading.h View File

@@ -0,0 +1,3 @@
#pragma once

#include <cppcore/threading/cancellation_token.h>

+ 107
- 0
include/cppcore/threading/cancellation_token.h View File

@@ -0,0 +1,107 @@
#pragma once

#include <atomic>
#include <exception>

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<typename T>
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<bool> _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<typename T>
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"

+ 78
- 0
include/cppcore/threading/cancellation_token.inl View File

@@ -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<typename T>
data_cancellation_exception<T>::data_cancellation_exception(const T& d)
: data(d)
{ }

/* data_cancellation_exception<void> */

template<>
struct data_cancellation_exception<void>
: 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<typename T>
void cancellation_source<T>
::throw_if_cancellation_requested() const
{
if (is_cancellation_requested())
throw data_cancellation_exception<T>(_value);
}

template<typename T>
void cancellation_source<T>
::cancel(const T& value)
{
cancellation_token::cancel();
_value = value;
}

/* cancellation_source<void> */

template<>
struct cancellation_source<void>
: public cancellation_token
{
public:
inline void throw_if_cancellation_requested() const
{
if (is_cancellation_requested())
throw cancellation_exception();
}

inline void cancel()
{ cancellation_token::cancel(); }
};

}

+ 25
- 0
src/CMakeLists.txt View File

@@ -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
$<BUILD_INTERFACE:${CPPCORE_INCLUDE_DIR}>
$<INSTALL_INTERFACE:${CPPCORE_INSTALL_DIR_INCLUDE}> )

# 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} )

+ 44
- 0
test/CMakeLists.txt View File

@@ -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 ( )

+ 39
- 0
test/cppcore/conversion/byteorder_tests.cpp View File

@@ -0,0 +1,39 @@
#include <gtest/gtest.h>
#include <cppcore/defines.h>
#include <cppcore/conversion/byteorder.h>

using namespace ::cppcore;

TEST(byteorder_tests, hton)
{
#if CPPCORE_BYTE_ORDER == CPPCORE_BYTE_ORDER_LITTLE
EXPECT_EQ(hton(static_cast<uint8_t> (0x12)), 0x12);
EXPECT_EQ(hton(static_cast<uint16_t>(0x1234)), 0x3412);
EXPECT_EQ(hton(static_cast<uint32_t>(0x12345678)), 0x78563412);
EXPECT_EQ(hton(static_cast<uint64_t>(0x0123456789ABCDEF)), 0xEFCDAB8967452301);
#elif CPPCORE_BYTE_ORDER == CPPCORE_BYTE_ORDER_BIG
EXPECT_EQ(hton(static_cast<uint8_t> (0x12)), 0x12);
EXPECT_EQ(hton(static_cast<uint16_t>(0x1234)), 0x1234);
EXPECT_EQ(hton(static_cast<uint32_t>(0x12345678)), 0x12345678);
EXPECT_EQ(hton(static_cast<uint64_t>(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<uint8_t> (0x12)), 0x12);
EXPECT_EQ(ntoh(static_cast<uint16_t>(0x1234)), 0x3412);
EXPECT_EQ(ntoh(static_cast<uint32_t>(0x12345678)), 0x78563412);
EXPECT_EQ(ntoh(static_cast<uint64_t>(0x0123456789ABCDEF)), 0xEFCDAB8967452301);
#elif CPPCORE_BYTE_ORDER == CPPCORE_BYTE_ORDER_BIG
EXPECT_EQ(ntoh(static_cast<uint8_t> (0x12)), 0x12);
EXPECT_EQ(ntoh(static_cast<uint16_t>(0x1234)), 0x1234);
EXPECT_EQ(ntoh(static_cast<uint32_t>(0x12345678)), 0x12345678);
EXPECT_EQ(ntoh(static_cast<uint64_t>(0x0123456789ABCDEF)), 0x0123456789ABCDEF);
#else
#error "Please add test!"
#endif
}

+ 58
- 0
test/cppcore/conversion/enum_tests.cpp View File

@@ -0,0 +1,58 @@
#include <gtest/gtest.h>
#include <cppcore/conversion/enum.h>
#include <cppcore/conversion/string.h>

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<TestEnum>::to_string(TestEnum::Test0, true));
EXPECT_EQ(std::string("Test1"), enum_conversion<TestEnum>::to_string(TestEnum::Test1, false));
EXPECT_EQ(std::string("Test2(2)"), enum_conversion<TestEnum>::to_string(TestEnum::Test2, true));
EXPECT_EQ(std::string("Test3"), enum_conversion<TestEnum>::to_string(TestEnum::Test3, false));
EXPECT_EQ(std::string("123"), enum_conversion<TestEnum>::to_string(static_cast<TestEnum>(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<TestEnum>::to_enum("abc"), convert_exception);
}

TEST(enum_tests, to_enum)
{
TestEnum e;
EXPECT_TRUE (enum_conversion<TestEnum>::try_to_enum("test0", e, false));
EXPECT_EQ (TestEnum::Test0, e);

EXPECT_TRUE (enum_conversion<TestEnum>::try_to_enum("Test1", e, false));
EXPECT_EQ (TestEnum::Test1, e);

EXPECT_FALSE(enum_conversion<TestEnum>::try_to_enum("asd", e, false));

EXPECT_FALSE(enum_conversion<TestEnum>::try_to_enum("1", e, false));

EXPECT_TRUE (enum_conversion<TestEnum>::try_to_enum("2", e, true));
EXPECT_EQ (TestEnum::Test2, e);
}

+ 95
- 0
test/cppcore/conversion/string_tests.cpp View File

@@ -0,0 +1,95 @@
#include <list>
#include <vector>
#include <gtest/gtest.h>
#include <cppcore/conversion/string.h>

struct TestClass1
{
int i;

std::string to_string() const
{ return std::to_string(i); }

bool from_string(const std::string& s)
{ return static_cast<bool>(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<bool>(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<int> { 1, 2, 3, 4, 5 }));
}

TEST(string_tests, list_to_string)
{
EXPECT_EQ(
"1,2,3,4,5",
::cppcore::to_string(std::list<int> { 1, 2, 3, 4, 5 }));
}

TEST(string_tests, string_to_vector)
{
EXPECT_EQ(
(std::vector<int> { 1, 245, 3, 4, 5 }),
::cppcore::from_string<std::vector<int>>("1, 245, 3, 4, 5"));
}

TEST(string_tests, string_to_list)
{
EXPECT_EQ(
(std::list<int> { 123, 2, 3, 4, 5 }),
::cppcore::from_string<std::list<int>>("123, 2, 3, 4, 5"));
}

+ 30
- 0
test/cppcore/conversion/time_tests.cpp View File

@@ -0,0 +1,30 @@
#include <gtest/gtest.h>
#include <cppcore/conversion/time.h>

using namespace ::cppcore;

TEST(time_tests, duration_to_timespec)
{
auto ts1 = duration_cast<timespec>(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<timeval>(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<std::chrono::microseconds>(timespec{ 1, 234567890 });
EXPECT_EQ(1234567, d1.count());
}

TEST(time_tests, timeval_to_duration)
{
auto d1 = duration_cast<std::chrono::microseconds>(timeval{ 1, 234567 });
EXPECT_EQ(1234567, d1.count());
}

+ 101
- 0
test/cppcore/misc/flags_tests.cpp View File

@@ -0,0 +1,101 @@
#include <gtest/gtest.h>
#include <cppcore/misc/flags.h>

using namespace ::cppcore;
using namespace ::testing;

enum class TestFlag : uint8_t
{
value0,
value1,
value2,
value3,
value4
};

using Testflags = shifted_flags<TestFlag>;

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<bool>(f));
f.set(TestFlag::value1);
EXPECT_TRUE(static_cast<bool>(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<bool>(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);
}

+ 84
- 0
test/cppcore/misc/stream_tests.cpp View File

@@ -0,0 +1,84 @@
#include <gtest/gtest.h>
#include <cppcore/misc/stream.h>

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<uint32_t>(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<uint32_t>(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<std::string>(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<SimpleStruct>(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<ManagedStruct>(is);
EXPECT_EQ(std::string("test"), ret.s);
}

+ 15
- 0
test/cppcore/threading/cancellation_token_tests.cpp View File

@@ -0,0 +1,15 @@
#include <gtest/gtest.h>
#include <cppcore/threading/cancellation_token.h>

using namespace ::cppcore;

TEST(cancellation_token_tests, cancel)
{
cancellation_source<int> cancellation;

cancellation.throw_if_cancellation_requested();

cancellation.cancel(5);

EXPECT_THROW(cancellation.throw_if_cancellation_requested(), data_cancellation_exception<int>);
}

Loading…
Cancel
Save