Browse Source

* initial commit

own_hana
bergmann 6 years ago
commit
f83ab0a28a
30 changed files with 7975 additions and 0 deletions
  1. +15
    -0
      CMakeLists.txt
  2. +14
    -0
      src/CMakeLists.txt
  3. +257
    -0
      src/LoggerImpl.cpp
  4. +158
    -0
      src/cpputils/EnumConversion.h
  5. +167
    -0
      src/cpputils/Exception.h
  6. +103
    -0
      src/cpputils/Flags.h
  7. +1477
    -0
      src/cpputils/Linq.h
  8. +187
    -0
      src/cpputils/Logger.h
  9. +189
    -0
      src/cpputils/LoggerImpl.h
  10. +980
    -0
      src/cpputils/MetaProgramming.h
  11. +69
    -0
      src/cpputils/Misc.h
  12. +143
    -0
      src/cpputils/Nullable.h
  13. +166
    -0
      src/cpputils/SmartPtr.h
  14. +107
    -0
      src/cpputils/StreamHelper.h
  15. +170
    -0
      src/cpputils/StringHelper.h
  16. +81
    -0
      src/cpputils/Time.h
  17. +236
    -0
      src/cpputils/TransformIterator.h
  18. +1834
    -0
      src/cpputils/old_linq.h
  19. +24
    -0
      test/CMakeLists.txt
  20. +61
    -0
      test/EnumConversionTests.cpp
  21. +76
    -0
      test/FlagsTests.cpp
  22. +464
    -0
      test/LinqTests.cpp
  23. +135
    -0
      test/LoggingTests.cpp
  24. +339
    -0
      test/MetaProgrammingTests.cpp
  25. +22
    -0
      test/MiscTests.cpp
  26. +161
    -0
      test/NullableTests.cpp
  27. +87
    -0
      test/StreamHelperTests.cpp
  28. +97
    -0
      test/StringHelperTests.cpp
  29. +35
    -0
      test/TimeTests.cpp
  30. +121
    -0
      test/TransformIteratorTests.cpp

+ 15
- 0
CMakeLists.txt View File

@@ -0,0 +1,15 @@
# Initialize CMake ################################################################################

If ( NOT CMAKE_BUILD_TYPE )
Set ( CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build!" FORCE )
EndIf ( NOT CMAKE_BUILD_TYPE )

CMake_Minimum_Required ( VERSION 3.5.1 FATAL_ERROR )
Include ( cotire OPTIONAL )
CMake_Policy ( SET CMP0065 NEW )
CMake_Policy ( SET CMP0065 NEW )

# Projects ########################################################################################

Add_SubDirectory ( ${CMAKE_CURRENT_SOURCE_DIR}/src )
Add_SubDirectory ( ${CMAKE_CURRENT_SOURCE_DIR}/test )

+ 14
- 0
src/CMakeLists.txt View File

@@ -0,0 +1,14 @@
# Project: tsoutils ###############################################################################

Project ( tsoutils )
Set ( CMAKE_CXX_STANDARD 14 )
Include ( GlobalCompilerFlags OPTIONAL )
File ( GLOB_RECURSE SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp )
Add_Library ( tsoutils STATIC ${SOURCE_FILES} )
Target_Include_Directories (
tsoutils
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
)
If ( __COTIRE_INCLUDED )
Cotire ( tsoutils )
EndIf ( )

+ 257
- 0
src/LoggerImpl.cpp View File

@@ -0,0 +1,257 @@
#include <list>
#include <iomanip>

#include "cpputils/LoggerImpl.h"

using namespace logging;

struct StreamFormatSaver
{
std::ios state;
std::ostream& stream;

StreamFormatSaver(std::ostream& s) :
state (nullptr),
stream (s)
{ state.copyfmt(stream); }

~StreamFormatSaver()
{ stream.copyfmt(state); }
};


struct Manager
{
private:
using LoggerImplPtrU = std::unique_ptr<LoggerImpl>;

LoggerImpl _defaultLogger;
std::map<std::string, LoggerImplPtrU> _logger;
std::list<Rule> _rules;
std::set<Consumer*> _consumer;

LoggerImpl& initLogger(LoggerImpl& logger)
{
for (auto& rule : _rules)
{
if (rule.loggerMatcher->match(logger))
logger.registerRule(rule);
}
return logger;
}

public:
inline Logger& getLogger(const std::string& name)
{
if (name.empty())
return _defaultLogger;
auto it = _logger.find(name);
return (it != _logger.end()
? *it->second
: initLogger(*_logger.emplace(name, LoggerImplPtrU(new LoggerImpl(name))).first->second));
}

void registerConsumer(Consumer& consumer)
{
_consumer.insert(&consumer);
for (auto& rule : _rules)
{
if (rule.consumerMatcher->match(consumer))
rule.registerConsumer(consumer);
}
}

void unregisterConsumer(Consumer& consumer)
{
for (auto& rule : _rules)
rule.unregisterConsumer(consumer);
}

RuleHandle defineRule(MatcherPtrU loggerMatcher, MatcherPtrU consumerMatcher, Level minLevel, Level maxLevel)
{
_rules.emplace_back(std::move(loggerMatcher), std::move(consumerMatcher), minLevel, maxLevel);
auto& rule = _rules.back();
for (auto& c : _consumer)
{
if (rule.consumerMatcher->match(*c))
rule.registerConsumer(*c);
}
if (rule.loggerMatcher->match(_defaultLogger))
_defaultLogger.registerRule(rule);
for (auto& l : _logger)
{
if (rule.loggerMatcher->match(*l.second))
l.second->registerRule(rule);
}
return &rule;
}

void undefineRule(RuleHandle rule)
{
auto r = static_cast<Rule*>(rule);
auto it = _rules.begin();
while (&*it != r && it != _rules.end())
++it;
if (it == _rules.end())
return;
_defaultLogger.unregisterRule(*it);
for (auto& l : _logger)
l.second->unregisterRule(*it);
_rules.erase(it);
}

inline void reset()
{
_logger.clear();
_rules.clear();
_consumer.clear();
}

Manager() :
_defaultLogger(std::string())
{ }
};

Manager& manager()
{
static Manager value;
return value;
}

std::string EmptyString;

Consumer::Consumer(const std::string& n, bool autoRegister) :
_name(n)
{
if (autoRegister)
registerConsumer(*this);
}

Consumer::~Consumer()
{ unregisterConsumer(*this); }

void StreamConsumer::log(DataPtrS data)
{
std::lock_guard<std::mutex> lk(_mutex);

using namespace std;
if (!data)
return;
auto& d = *data;
auto t = std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1>>>(d.time.time_since_epoch()).count();
auto f = d.file;
if (f)
{
auto tmp = strrchr(f, '/');
if (tmp)
f = tmp + 1;
}
else
f = "unknown";

StreamFormatSaver formatSaver(*_stream);
if (t >= 0) *_stream << "[" << fixed << setfill(' ') << setw(17) << setprecision(6) << t << "] ";
switch(d.level)
{
case Level::Debug: *_stream << "DEBUG "; break;
case Level::Info: *_stream << "INFO "; break;
case Level::Warn: *_stream << "WARN "; break;
case Level::Error: *_stream << "ERROR "; break;
}

if (d.sender) *_stream << "0x" << hex << setw(2 * sizeof(void*)) << setfill('0') << d.sender;
else *_stream << " ";
if (d.thread != std::thread::id()) *_stream << "@" << hex << setw(2 * sizeof(void*)) << setfill('0') << d.thread;
else *_stream << " ";
if (!d.name.empty()) *_stream << " '" << d.name << "'";
if (d.line) *_stream << " - " << setw(25) << setfill(' ') << f << ":" << setw(5) << setfill(' ') << dec << d.line;
if (!d.message.empty())
{
*_stream << ": " << d.message;
if (d.message.back() != '\n')
*_stream << std::endl;
}
else
*_stream << std::endl;
}



bool AllMatcher::match(const Logger& logger) const
{ return true; }

bool AllMatcher::match(const Consumer& consumer) const
{ return true; }

bool RegexMatcher::match(const Logger& logger) const
{ return !logger.name().empty() && std::regex_match(logger.name(), _regex) != _invert; }

bool RegexMatcher::match(const Consumer& consumer) const
{ return !consumer.name().empty() && std::regex_match(consumer.name(), _regex) != _invert; }

bool DefaultLoggerMatcher::match(const Logger& logger) const
{ return &_defaultLogger == &logger; }



LogHelper::LogHelper(Logger& logger, DataPtrS data) :
_logger (logger),
_data (data)
{ }

LogHelper::~LogHelper()
{ _logger.log(_data); }



const std::string& Logger::name() const
{ return EmptyString; }

bool Logger::isEnabled(Level level) const
{ return false; }

void Logger::log(DataPtrS data) const
{ /* no op */ }



const std::string& LoggerImpl::name() const
{ return _name; }

bool LoggerImpl::isEnabled(Level level) const
{
for (auto& rule : _rules)
{
if (rule->isEnabled(level))
return true;
}
return false;
}

void LoggerImpl::log(DataPtrS data) const
{
std::lock_guard<std::mutex> lk(_mutex);
for (auto& r : _rules)
r->log(data);
}

namespace logging
{
Logger& getLogger(const std::string& name)
{ return manager().getLogger(name); }

void registerConsumer(Consumer& consumer)
{ return manager().registerConsumer(consumer); }

void unregisterConsumer(Consumer& consumer)
{ return manager().unregisterConsumer(consumer); }

RuleHandle defineRule(MatcherPtrU loggerMatcher, MatcherPtrU consumerMatcher, Level minLevel, Level maxLevel)
{ return manager().defineRule(std::move(loggerMatcher), std::move(consumerMatcher), minLevel, maxLevel); }

void undefineRule(RuleHandle rule)
{ return manager().undefineRule(rule); }

void resetLogging()
{ manager().reset(); }
}

+ 158
- 0
src/cpputils/EnumConversion.h View File

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

#include <map>
#include <type_traits>

#include "Exception.h"

#define DefEnumToStringMap(enum, ...) \
namespace utl { \
namespace __impl { \
template<> \
struct map_enum_to_string<enum> \
{ \
using enum_to_string_map_type = std::map<enum, std::string>; \
static inline enum_to_string_map_type value() \
{ \
static const enum_to_string_map_type map({ \
__VA_ARGS__ \
}); \
return map; \
} \
}; } }

#define DefStringToEnumMap(enum, less, ...) \
namespace utl { \
namespace __impl { \
template<> \
struct map_string_to_enum<enum> \
{ \
using string_to_enum_map_type = std::map<std::string, enum, less>; \
static inline string_to_enum_map_type value() \
{ \
static const string_to_enum_map_type map({ \
__VA_ARGS__ \
}); \
return map; \
} \
}; } }

#define DefDefaultEnumValue(enum, value) \
namespace utl { \
namespace __impl { \
template<> \
struct default_enum_value<enum> \
{ \
static inline enum value() \
{ return value; } \
}; } }

namespace utl
{

namespace __impl
{
struct invariant_string_less
{
inline bool 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;
}
};

template<class T>
struct map_enum_to_string
{
using enum_to_string_map_type = std::map<T, std::string>;
static inline enum_to_string_map_type value()
{
static const enum_to_string_map_type map;
return map;
}
};

template<class T>
struct map_string_to_enum
{
using string_to_enum_map_type = std::map<std::string, T>;
static inline string_to_enum_map_type value()
{
static const string_to_enum_map_type map;
return map;
}
};

template<class T>
struct default_enum_value
{
static inline T value()
{ throw Exception("unable to convert string to enum"); }
};
}

using InvariantStringLess = __impl::invariant_string_less;

template<class T>
struct EnumConversion
{
using map_enum_to_string = __impl::map_enum_to_string<T>;
using map_string_to_enum = __impl::map_string_to_enum<T>;
using default_enum_value = __impl::default_enum_value<T>;
using base_type = typename std::underlying_type<T>::type;

static inline std::string toString(T value, bool addValue)
{
static const auto& map = map_enum_to_string::value();
auto it = map.find(value);
std::string ret;
if (it != map.end())
{
ret = it->second;
if (addValue)
ret += '(' + std::to_string(static_cast<base_type>(value)) + ')';
}
else
ret = std::to_string(static_cast<base_type>(value));
return ret;
}

static inline bool tryToEnum(const std::string& str, T& value, bool acceptNumeric)
{
static const auto& map = map_string_to_enum::value();
auto it = map.find(str);
if (it != map.end())
{
value = it->second;
return true;
}
if (!acceptNumeric)
return false;
char *e = nullptr;
const char *c = str.c_str();
value = static_cast<T>(std::strtoull(c, &e, 0));
return (c != e);
}

static inline T toEnum(const std::string& str)
{
T value;
return tryToEnum(str, value)
? value
: default_enum_value::value();
}
};

}

+ 167
- 0
src/cpputils/Exception.h View File

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

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

#include "Flags.h"

namespace utl
{

struct Exception : public std::exception
{
public:
enum class PrintFlag
{
ResolveAddress,
};
using PrintFlags = ShiftedFlags<PrintFlag>;

public:
static constexpr size_t StackSize = 15;
static const PrintFlags& defaultPrintFlags()
{
static const PrintFlags value({ PrintFlag::ResolveAddress });
return value;
};

private:
mutable bool _msgCacheEmpty;
mutable std::string _msgCache;

protected:
virtual void printMessage(std::ostream& os) const
{
os << message;
}

public:
std::string message;
void* stack[StackSize];
int stackSize;

public:
void print(std::ostream& os, const PrintFlags& flags = defaultPrintFlags()) const
{
printMessage(os);
os << std::endl;
char** lines = nullptr;
if (flags.isSet(PrintFlag::ResolveAddress))
lines = backtrace_symbols(stack, stackSize);
for (int i = 0; i < stackSize; ++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 print(const PrintFlags& flags = defaultPrintFlags()) const
{
std::ostringstream os;
print(os, flags);
return os.str();
}

const char* what() const throw() override
{
if (_msgCacheEmpty)
{
_msgCache = print();
_msgCacheEmpty = false;
}
return _msgCache.c_str();
}

inline friend std::ostream& operator <<(std::ostream& os, const Exception& ex)
{
ex.print(os);
return os;
}

public:
Exception() :
Exception("")
{ }

Exception(std::string msg) :
message (msg),
_msgCacheEmpty (true)
{
stackSize = backtrace(stack, StackSize);
}
};

struct ErrorException : public Exception
{
public:
int error;

ErrorException(int e) :
Exception(std::to_string(e) + " - " + strerror(e)),
error(e)
{ }

ErrorException(const std::string& msg, int e) :
Exception(msg + ": " + std::to_string(e) + " - " + strerror(e)),
error(e)
{ }
};

struct EOutOfRange : public Exception
{
protected:
void printMessage(std::ostream& os) const override
{
os << "index out of range (min=" << min << "; max=" << max << "; index=" << index << ")";
if (!message.empty())
os << " - " << message;
}

public:
size_t min;
size_t max;
size_t index;

EOutOfRange(size_t mi, size_t ma, size_t idx, std::string msg = "") :
Exception (msg),
min (mi),
max (ma),
index (idx)
{ }
};

struct EArgument : public Exception
{
protected:
void printMessage(std::ostream& os) const override
{
os << "invalid argument";
if (!argument.empty())
os << "(" << argument << ")";
if (!message.empty())
os << " - " << message;
}

public:
std::string argument;

EArgument(std::string arg, std::string msg) :
Exception (message),
argument (arg)
{ }
};

struct EInvalidOperation : public Exception
{
public:
EInvalidOperation(std::string msg) :
Exception(msg)
{ }
};

}

+ 103
- 0
src/cpputils/Flags.h View File

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

#include <limits>
#include <type_traits>
#include <initializer_list>

namespace utl
{
template<class TEnum, class TBase>
struct op_flag_convert_none
{
static inline TBase toBase(const TEnum e)
{ return static_cast<TBase>(e); }
};

template<class TEnum, class TBase>
struct op_flag_convert_shift
{
static inline TBase toBase(const TEnum e)
{ return static_cast<TBase>(1 << static_cast<int>(e)); }
};

template<
class TEnum,
class TBase = typename std::underlying_type<TEnum>::type,
class Op = op_flag_convert_none<TEnum, TBase>>
struct Flags
{
public:
TBase value;

public:
inline bool isSet(TEnum e) const
{ return static_cast<bool>(value & Op::toBase(e)); }

inline void set(TEnum e)
{ value = static_cast<TBase>(value | Op::toBase(e)); }

inline void clear(TEnum e)
{ value = static_cast<TBase>(value & ~Op::toBase(e)); }

inline void reset()
{ value = 0; }

public:
TBase operator()() const
{ return value; }

operator bool() const
{ return static_cast<bool>(value); }

bool operator[](TEnum e) const
{ return isSet(e); }

public:
Flags() :
value(0)
{ }

explicit Flags(TBase v) :
value(v)
{ }

Flags(TEnum e) :
value(Op::toBase(e))
{ }

Flags(const Flags& other) :
value(other.value)
{ }

Flags(std::initializer_list<TEnum> list) :
Flags()
{
for (auto& e : list)
set(e);
}

public:
static inline const Flags& empty()
{
static const Flags value(0);
return value;
}

static inline const Flags& all()
{
static const Flags value(std::numeric_limits<TBase>::max());
return value;
}
};

template<
class TEnum,
class TBase = typename std::underlying_type<TEnum>::type>
using ShiftedFlags = Flags<TEnum, TBase, op_flag_convert_shift<TEnum, TBase>>;

template<
class TEnum,
class TBase = typename std::underlying_type<TEnum>::type>
using SimpleFlags = Flags<TEnum, TBase, op_flag_convert_none<TEnum, TBase>>;

}

+ 1477
- 0
src/cpputils/Linq.h
File diff suppressed because it is too large
View File


+ 187
- 0
src/cpputils/Logger.h View File

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

#include <memory>
#include <chrono>
#include <thread>
#include <sstream>

#include "Exception.h"

// () mandatory
// [] optional
// (logger), (LogLevel: Debug|Info|Warn|Error), [Sender], [Message, [Arguments]]
#define logMessage(logger, level, ...) \
if (logger.isEnabled(logging::Level::level)) \
logger.makeLogHelper(logging::Level::level, __FILE__, __LINE__, ## __VA_ARGS__ ) = logging::LogMessage()

// () mandatory
// [] optional
// (LogLevel: Debug|Info|Warn|Error), [Sender], [Message, [Arguments]]
#define logGlobalMessage(level, ...) \
if (logging::isEnabled(logging::Level::level)) \
logging::makeLogHelper(logging::Level::level, __FILE__, __LINE__, ## __VA_ARGS__ ) = logging::LogMessage()


namespace logging
{

enum class Level
{
Debug = 0,
Info,
Warn,
Error,
};

struct Data
{
Level level;
std::chrono::steady_clock::time_point time;
void* sender;
std::thread::id thread;
const char* file;
int line;
std::string name;
std::string message;
};
using DataPtrS = std::shared_ptr<Data>;

struct Logger;

struct LogMessage
{
private:
std::ostringstream _msg;

public:
inline std::string str() const
{ return _msg.str(); }

template <typename T>
inline LogMessage& operator <<(const T& value)
{
_msg << value;
return *this;
}

inline LogMessage& operator <<(std::ostream& (*callback)(std::ostream&))
{
callback(_msg);
return *this;
}

LogMessage() :
_msg()
{ }

LogMessage(const std::string& msg) :
_msg(msg)
{ }

private:
LogMessage(const LogMessage&) = delete;
LogMessage(LogMessage&&) = delete;
};

struct LogHelper
{
private:
Logger& _logger;
DataPtrS _data;

public:
inline void operator=(const LogMessage& msg)
{ _data->message += msg.str(); }

LogHelper(Logger& logger, DataPtrS data);
~LogHelper();
};

struct Logger
{
private:
Logger(const Logger&) = delete;
Logger(Logger&&) = delete;

public:
Logger() { }

virtual const std::string& name() const;
virtual bool isEnabled(Level level) const;
virtual void log(DataPtrS data) const;

template<class Sender>
inline LogHelper makeLogHelper(Level level, const char* file, int line, Sender* sender, std::string message)
{ return makeLogHelper<void>(level, file, line, static_cast<void*>(sender), message); }

template<class Sender>
inline LogHelper makeLogHelper(Level level, const char* file, int line, Sender* sender)
{ return makeLogHelper<void>(level, file, line, static_cast<void*>(sender), std::string()); }

inline LogHelper makeLogHelper(Level level, const char* file, int line, std::string message)
{ return makeLogHelper(level, file, line, nullptr, message); }

inline LogHelper makeLogHelper(Level level, const char* file, int line)
{ return makeLogHelper(level, file, line, nullptr, std::string()); }

template<class Sender, class... Args>
inline LogHelper makeLogHelper(Level level, const char* file, int line, Sender* sender, std::string message, Args... args)
{
std::unique_ptr<char, decltype(&free)> buff(static_cast<char*>(malloc(0x8000)), &free);
auto len = snprintf(buff.get(), 10240, message.c_str(), args...);
if (len < 0)
throw utl::ErrorException(errno);
return makeLogHelper<Sender>(level, file, line, sender, std::string(buff.get(), len));
}

template<class... Args>
inline LogHelper makeLogHelper(Level level, const char* file, int line, std::string message, Args... args)
{ return makeLogHelper<void, Args...>(level, file, line, nullptr, message, args...); }

};

template<>
inline LogHelper Logger::makeLogHelper<void>(Level level, const char* file, int line, void* sender, std::string message)
{
DataPtrS data(new Data());
data->level = level;
data->time = std::chrono::steady_clock::now();
data->thread = std::this_thread::get_id();
data->file = file;
data->line = line;
data->sender = sender;
data->name = name();
data->message = message;
return LogHelper(*this, data);
}

Logger& getLogger(const std::string& name = "");

inline bool isEnabled(Level level)
{ return getLogger().isEnabled(level); }

template <class Sender>
inline LogHelper makeLogHelper(Level level, const char* file, int line, Sender* sender, std::string message)
{ return getLogger().makeLogHelper<Sender>(level, file, line, sender, message); }

template <class Sender>
inline LogHelper makeLogHelper(Level level, const char* file, int line, Sender* sender)
{ return getLogger().makeLogHelper<Sender>(level, file, line, sender, std::string()); }

inline LogHelper makeLogHelper(Level level, const char* file, int line, std::string message)
{ return getLogger().makeLogHelper<void>(level, file, line, nullptr, message); }

inline LogHelper makeLogHelper(Level level, const char* file, int line)
{ return getLogger().makeLogHelper<void>(level, file, line, nullptr, std::string()); }

template <class Sender, class... Args>
inline LogHelper makeLogHelper(Level level, const char* file, int line, Sender* sender, std::string message, Args... args)
{ return getLogger().makeLogHelper<Sender, Args...>(level, file, line, sender, message, args...); }

template <class... Args>
inline LogHelper makeLogHelper(Level level, const char* file, int line, std::string message, Args... args)
{ return getLogger().makeLogHelper<void, Args...>(level, file, line, nullptr, message, args...); }

void resetLogging();

}

+ 189
- 0
src/cpputils/LoggerImpl.h View File

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

#include <set>
#include <map>
#include <regex>
#include <mutex>
#include <iostream>

#include "Logger.h"

namespace logging
{

struct Consumer
{
private:
std::string _name;

public:
virtual void log(DataPtrS data) = 0;

inline const std::string& name() const
{ return _name; }

Consumer(const std::string& n, bool autoRegister);
~Consumer();
};

struct StreamConsumer : public Consumer
{
private:
mutable std::mutex _mutex;
std::ostream* _stream;
bool _ownsStream;

public:
void log(DataPtrS data) override;

StreamConsumer(const std::string& name, std::ostream& stream, bool ownsStream, bool autoRegister) :
Consumer (name, autoRegister),
_stream (&stream),
_ownsStream (ownsStream)
{ }

virtual ~StreamConsumer()
{
if (_ownsStream && _stream)
{
delete _stream;
_stream = nullptr;
}
}
};



struct Matcher
{
virtual bool match(const Logger& logger) const
{ return false; }

virtual bool match(const Consumer& consumer) const
{ return false; }
};

struct AllMatcher : public Matcher
{
bool match(const Logger& logger) const override;
bool match(const Consumer& consumer) const override;
};

struct RegexMatcher : public Matcher
{
private:
std::regex _regex;
bool _invert;

public:
bool match(const Logger& logger) const override;
bool match(const Consumer& consumer) const override;

RegexMatcher(const std::string expression, bool invert = false) :
_regex (expression),
_invert (invert)
{ }
};

struct DefaultLoggerMatcher : public Matcher
{
private:
Logger& _defaultLogger;

public:
using Matcher::match;
bool match(const Logger& logger) const override;

DefaultLoggerMatcher() :
_defaultLogger(getLogger(std::string()))
{ }
};

using RuleHandle = void*;
using MatcherPtrU = std::unique_ptr<Matcher>;

struct Rule
{
private:
mutable std::mutex _mutex;
std::set<Consumer*> _consumer;

public:
MatcherPtrU loggerMatcher;
MatcherPtrU consumerMatcher;
Level minLevel;
Level maxLevel;

inline bool isEnabled(Level level) const
{
return minLevel <= level
&& maxLevel >= level;
}

inline void registerConsumer(Consumer& consumer)
{
std::lock_guard<std::mutex> lk(_mutex);
_consumer.insert(&consumer);
}

inline void unregisterConsumer(Consumer& consumer)
{
std::lock_guard<std::mutex> lk(_mutex);
_consumer.erase(&consumer);
}

inline void log(DataPtrS data)
{
std::lock_guard<std::mutex> lk(_mutex);
if (isEnabled(data->level))
{
for (auto& c : _consumer)
c->log(data);
}
}

Rule(MatcherPtrU lm, MatcherPtrU cm, Level min, Level max) :
loggerMatcher (std::move(lm)),
consumerMatcher(std::move(cm)),
minLevel (min),
maxLevel (max)
{ }
};


struct LoggerImpl : public Logger
{
private:
mutable std::mutex _mutex;
std::string _name;
std::set<Rule*> _rules;

public:
const std::string& name() const override;
bool isEnabled(Level level) const override;
void log(DataPtrS data) const override;

inline void registerRule(Rule& rule)
{
std::lock_guard<std::mutex> lk(_mutex);
_rules.insert(&rule);
}

inline void unregisterRule(Rule& rule)
{
std::lock_guard<std::mutex> lk(_mutex);
_rules.erase(&rule);
}

LoggerImpl(const std::string& n) :
_name(n)
{ }
};

void registerConsumer (Consumer& consumer);
void unregisterConsumer(Consumer& consumer);

RuleHandle defineRule(MatcherPtrU loggerMatcher, MatcherPtrU consumerMatcher, Level minLevel = Level::Debug, Level maxLevel = Level::Error);
void undefineRule(RuleHandle handle);

}

+ 980
- 0
src/cpputils/MetaProgramming.h View File

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

#include <tuple>
#include <string>
#include <cstdlib>
#include <cstdint>
#include <type_traits>

#define MP_DEBUG

#if defined __GNUC__
# define LIKELY(EXPR) __builtin_expect(!!(EXPR), 1)
#else
# define LIKELY(EXPR) (!!(EXPR))
#endif

#if defined MP_DEBUG
# define X_ASSERT(CHECK) void(0)
#else
# define X_ASSERT(CHECK) ( LIKELY(CHECK) ? void(0) : []{assert(!#CHECK);}() )
#endif

namespace utl
{

/* types */
template<class T, T t>
using mp_const = std::integral_constant<T, t>;

template<bool B>
using mp_bool = mp_const<bool, B>;

template<size_t S>
using mp_size_t = mp_const<size_t, S>;

template<class...>
struct mp_list { };

template<class T>
struct mp_identity
{ using type = T; };

template<class... T>
struct mp_inherit : T...
{ };

template<class T, T... Ints>
struct mp_integer_sequence
{ };

template<size_t... Ints>
using mp_index_sequence = mp_integer_sequence<size_t, Ints...>;



/* constants */
using mp_true = mp_bool<true>;
using mp_false = mp_bool<false>;
using mp_zero = mp_size_t<0>;



/* modifier */
template<class T>
using mp_add_pointer = T*;

template<class T>
using mp_add_reference = T&;

template<class T>
using mp_remove_ptr = typename std::remove_pointer<T>::type;

template<class T>
using mp_remove_const = typename std::remove_const<T>::type;

template<class T>
using mp_remove_cv = typename std::remove_cv<T>::type;

template<class T>
using mp_remove_ref = typename std::remove_reference<T>::type;

template<class T>
using mp_clean_type = mp_remove_cv<mp_remove_ptr<mp_remove_ref<T>>>;



/* conditionals */
template<class T, class S>
using mp_is_same = mp_bool<std::is_same<T, S>::value>;

template<class Base, class Derived>
using mp_is_base_of = mp_bool<std::is_base_of<Base, Derived>::value>;

template<class T, class S = void>
using mp_enable_if = typename std::enable_if<T::value, S>::type;

template<bool B, class S = void>
using mp_enable_if_c = typename std::enable_if<B, S>::type;



namespace __impl
{
/* logical operations */
template<bool C, class T, class E>
struct mp_if_c_impl;

template<bool C, class T, template<class...> class E, class... A>
struct mp_eval_if_c_impl;

template<class L>
struct mp_not_impl;

template<class L>
struct mp_and_impl;

template<class L>
struct mp_or_impl;



/* arithmetic */
template<class L>
struct mp_plus_impl;

template<class L>
struct mp_minus_impl;

template<class L>
struct mp_multiply_impl;

template<class L>
struct mp_divide_impl;



/* generator functions */
template<class S>
struct next_integer_sequence;

template<class T, T I, T N>
struct mp_make_int_seq_impl;

template<size_t N, class... T>
struct mp_repeat_impl;



/* list operations returning a numeric value */
template<class L>
struct mp_size_impl;

template<class L, class V>
struct mp_count_impl;

template<class L, template<class...> class P>
struct mp_count_if_impl;



/* operations to manipulate list content */
template<class L, class... T>
struct mp_push_front_impl;

template<class L>
struct mp_pop_front_impl;

template<class L, class... T>
struct mp_push_back_impl;

template<class... L>
struct mp_concat_impl;

template<class L, template<class> class F>
struct mp_filter_impl;



/* list operations returning a bool value */
template<class... L>
struct mp_empty_impl;

template<class L, class V>
struct mp_contains_impl;



/* list operations returing elements from the list */
template<class L>
struct mp_front_impl;

template<class L>
struct mp_second_impl;

template<class L, size_t I>
struct mp_at_c_impl;

template<class L, template<class> class F, class TDefault>
struct mp_search_impl;

template<class L, class V>
struct mp_find_impl;

template<class M, class K>
struct mp_map_find_impl;



/* list manipulators returning a new list */
template<class L>
struct mp_clear_impl;

template<class L, class C>
struct mp_unique_impl;

template<class A, template<class...> class B>
struct mp_rename_impl;

template<class... L>
struct mp_append_impl;

template<class L, class S>
struct mp_map_from_list_impl;

template<template<class...> class F, class E, class... L>
struct mp_transform_impl;



/* unique id */
template<class Counter>
struct mp_unique_counter;

template<class Counter, class... Args>
struct mp_unique_id;



/* strings */
struct tag_reference { };
struct tag_array { };

template<size_t N, class T>
struct mp_string_impl;

template<class I, int Base>
struct mp_int_to_string_impl;

template<size_t N>
using mp_array_string = mp_string_impl<N, tag_array>;

template<class J, class L>
struct mp_string_type_join_impl;



/* tuple */
template<bool, class F, class T, class... Args>
struct mp_tuple_call_helper
{
static inline void exec(F f, T& t, Args&&... args)
{ }
};

template<class F, class T, class... Args>
struct mp_tuple_call_helper<true, F, T, Args...>
{
static inline void exec(F f, T& t, Args&&... args)
{ f(t, std::forward<Args>(args)...); }
};

template<class T>
using mp_tuple_filter = mp_true;

template<template<class> class Filter, class T, class F, size_t... I, class... Args>
void for_each_in_tuple_filter(T& t, F f, const mp_index_sequence<I...>&, Args&&... args)
{
using expander = int[];
(void) f;
(void) expander { (mp_tuple_call_helper<
Filter<decltype(std::get<I>(t))>::value,
F,
decltype(std::get<I>(t)),
Args...>::exec(
f,
std::get<I>(t),
std::forward<Args>(args)...), 0)... };
}
}



/* logical operations */
template<class B, class T, class E>
using mp_if = typename __impl::mp_if_c_impl<B::value != 0, T, E>::type;

template<bool B, class T, class E>
using mp_if_c = typename __impl::mp_if_c_impl<B, T, E>::type;

template<class C, class T, template<class...> class E, class... A>
using mp_eval_if = typename __impl::mp_eval_if_c_impl<C::value != 0, T, E, A...>::type;

template<bool C, class T, template<class...> class E, class... A>
using mp_eval_if_c = typename __impl::mp_eval_if_c_impl<C, T, E, A...>::type;

template<class L>
using mp_not = typename __impl::mp_not_impl<L>::type;

template<class L>
using mp_and = typename __impl::mp_and_impl<L>::type;

template<class L>
using mp_or = typename __impl::mp_or_impl<L>::type;



/* arithmetic */
template<class L>
using mp_plus = typename __impl::mp_plus_impl<L>::type;

template<class L>
using mp_minus = typename __impl::mp_minus_impl<L>::type;

template<class L>
using mp_multiply = typename __impl::mp_multiply_impl<L>::type;

template<class L>
using mp_divide = typename __impl::mp_divide_impl<L>::type;



/* generator functions */
template<class T, T N>
using mp_make_integer_sequence = typename __impl::mp_make_int_seq_impl<T, 0, N>::type;

template<size_t N>
using mp_make_index_sequence = mp_make_integer_sequence<size_t, N>;

template<size_t N, class... T>
using mp_repeat = typename __impl::mp_repeat_impl<N, T...>::type;



/* list operations returning a numeric value */
template<class... T>
using mp_length = mp_const<size_t, sizeof...(T)>;

template<class L>
using mp_size = typename __impl::mp_rename_impl<L, mp_length>::type;

template<class L, class V>
using mp_count = typename __impl::mp_count_impl<L, V>::type;

template<class L, template<class...> class P>
using mp_count_if = typename __impl::mp_count_if_impl<L, P>::type;



/* operations to manipulate list content */
template<class L, class... T>
using mp_push_front = typename __impl::mp_push_front_impl<L, T...>::type;

template<class L, class... T>
using mp_pop_front = typename __impl::mp_pop_front_impl<L>::type;

template<class L, class... T>
using mp_push_back = typename __impl::mp_push_back_impl<L, T...>::type;

template<class... L>
using mp_concat = typename __impl::mp_concat_impl<L...>::type;

template<class L, template<class> class F>
using mp_filter = typename __impl::mp_filter_impl<L, F>::type;



/* list operations returning a bool value */
template<class... L>
using mp_empty = typename __impl::mp_empty_impl<L...>::type;

template<class L, class V>
using mp_contains = mp_bool<mp_count<L, V>::value != 0>;



/* list operations returing elements from the list */
template<class L>
using mp_front = typename __impl::mp_front_impl<L>::type;

template<class L>
using mp_second = typename __impl::mp_second_impl<L>::type;

template<class L, class I>
using mp_at = typename __impl::mp_at_c_impl<L, I::value>::type;

template<class L, size_t I>
using mp_at_c = typename __impl::mp_at_c_impl<L, I>::type;

template<class L, template<class> class F, class TDefault = void>
using mp_search = typename __impl::mp_search_impl<L, F, TDefault>::type;

template<class L, class V>
using mp_find = typename __impl::mp_find_impl<L, V>::type;

template<class M, class K>
using mp_map_find = typename __impl::mp_map_find_impl<M, K>::type;



/* list manipulators returning a new list */
template<class L>
using mp_clear = typename __impl::mp_clear_impl<L>::type;

template<class L>
using mp_unique = typename __impl::mp_unique_impl<L, mp_clear<L>>::type;

template<class A, template<class...> class B>
using mp_rename = typename __impl::mp_rename_impl<A, B>::type;

template<class... L>
using mp_append = typename __impl::mp_append_impl<L...>::type;

template<class L>
using mp_map_from_list = typename __impl::mp_map_from_list_impl<L, mp_make_index_sequence<mp_size<L>::value>>::type;

template<template<class...> class F, class... L>
using mp_transform = typename __impl::mp_transform_impl<F, mp_empty<L...>, L...>::type;



/* unique id */
template<class Counter>
inline size_t nextUniqueId()
{ return __impl::mp_unique_counter<Counter>::next(); }

template<class Counter, class... Args>
inline size_t getUniqueId()
{ return __impl::mp_unique_id<Counter, Args...>::value(); }



/* strings */
template<size_t N>
using mp_string = __impl::mp_string_impl<N, __impl::tag_reference>;

template<class I, int Base>
constexpr auto mp_int_to_string()
{ return __impl::mp_int_to_string_impl<I, Base>::value; }

template <int N>
constexpr mp_string<N - 1> mp_make_string(const char (&data)[N])
{ return mp_string<N - 1>(data); }

template <size_t N1, size_t N2, class T1, class T2>
constexpr __impl::mp_array_string<N1 + N2> mp_string_cat(
const __impl::mp_string_impl<N1, T1>& s1,
const __impl::mp_string_impl<N2, T2>& s2)
{ return __impl::mp_array_string<N1 + N2>(s1, s2); };

template <size_t N1, size_t N2, class T1, class T2>
constexpr auto operator + (
const __impl::mp_string_impl<N1, T1>& s1,
const __impl::mp_string_impl<N2, T2>& s2)
{ return mp_string_cat(s1, s2); };

template<size_t NJ, size_t N1, size_t N2, class TJ, class T1, class T2>
constexpr auto mp_string_join(
const __impl::mp_string_impl<NJ, TJ>& j,
const __impl::mp_string_impl<N1, T1>& s1,
const __impl::mp_string_impl<N2, T2>& s2)
{ return mp_string_cat(s1, mp_string_cat(j, s2)); }

template<size_t NJ, size_t N1, size_t... NX, class TJ, class T1, class... TX>
constexpr auto mp_string_join(
const __impl::mp_string_impl<NJ, TJ>& j,
const __impl::mp_string_impl<N1, T1>& s,
const __impl::mp_string_impl<NX, TX>&... rest)
{ return mp_string_cat(s, mp_string_cat(j, mp_string_join(j, rest...))); }

template<class J, class L>
using mp_string_type_join = __impl::mp_string_type_join_impl<J, L>;



/* tuple */
template<class... T, class F, class... Args>
void mp_for_each_in_tuple(std::tuple<T...>& t, F f, Args&&... args)
{ __impl::for_each_in_tuple_filter<__impl::mp_tuple_filter>(t, f, mp_make_index_sequence<mp_length<T...>::value>(), std::forward<Args>(args)...); }

template<class... T, class F, class... Args>
void mp_for_each_in_tuple(const std::tuple<T...>& t, F f, Args&&... args)
{ __impl::for_each_in_tuple_filter<__impl::mp_tuple_filter>(t, f, mp_make_index_sequence<mp_length<T...>::value>(), std::forward<Args>(args)...); }

template<template<class> class Filter, class... T, class F, class... Args>
void mp_for_each_in_tuple_filter(std::tuple<T...>& t, F f, Args&&... args)
{ __impl::for_each_in_tuple_filter<Filter>(t, f, mp_make_index_sequence<mp_length<T...>::value>(), std::forward<Args>(args)...); }

template<template<class> class Filter, class... T, class F, class... Args>
void mp_for_each_in_tuple_filter(const std::tuple<T...>& t, F f, Args&&... args)
{ __impl::for_each_in_tuple_filter<Filter>(t, f, mp_make_index_sequence<mp_length<T...>::value>(), std::forward<Args>(args)...); }

template<template<class...> class F>
struct mp_for_each_proxy
{
template<class T, class... Args>
inline void operator()(T& t, Args&&... args) const
{ F<mp_clean_type<T>>::exec(t, std::forward<Args>(args)...); }
};



namespace __impl
{
/* helper */
constexpr size_t cx_plus()
{ return 0; }

template<class T1, class... T>
constexpr size_t cx_plus(T1 t1, T... t)
{ return t1 + cx_plus(t...); }

constexpr bool cx_and()
{ return true; }

template<class... T>
constexpr bool cx_and(bool b, T... t)
{ return b && cx_and(t...); }

constexpr bool cx_or()
{ return false; }

template<class... T>
constexpr bool cx_or(bool b, T... t)
{ return b || cx_or(t...); }

constexpr size_t cx_find_index( bool const * first, bool const * last )
{ return first == last || *first ? 0 : 1 + cx_find_index(first + 1, last); }



/* logical operations */
template<class T, class F>
struct mp_if_c_impl<true, T, F>
{ using type = T; };

template<class T, class F>
struct mp_if_c_impl<false, T, F>
{ using type = F; };

template<class T, template<class...> class E, class... A>
struct mp_eval_if_c_impl<true, T, E, A...>
{ using type = T; };

template<class T, template<class...> class E, class... A>
struct mp_eval_if_c_impl<false, T, E, A...>
{ using type = E<A...>; };

template<template<class...> class L, class... T>
struct mp_not_impl<L<T...>>
{ using type = L<mp_bool<T::value == 0>...>; };

template<template<class...> class L, class... T>
struct mp_and_impl<L<T...>>
{ using type = mp_bool<cx_and(mp_bool<T::value>::value...)>; };

template<template<class...> class L, class... T>
struct mp_or_impl<L<T...>>
{ using type = mp_bool<cx_or(mp_bool<T::value>::value...)>; };



/* arithmetic */
template<template<class...> class L, class T>
struct mp_plus_impl<L<T>>
{ using type = T; };

template<template<class...> class L, class T1, class... T>
struct mp_plus_impl<L<T1, T...>>
{
using _type = mp_remove_const<decltype(T1::value)>;
static constexpr _type _value = T1::value + mp_plus<L<T...>>::value;
using type = mp_const<_type, _value>;
};

template<template<class...> class L, class T>
struct mp_minus_impl<L<T>>
{ using type = T; };

template<template<class...> class L, class T1, class... T>
struct mp_minus_impl<L<T1, T...>>
{
using _type = mp_remove_const<decltype(T1::value)>;
static constexpr _type _value = T1::value - mp_plus<L<T...>>::value;
using type = mp_const<_type, _value>;
};

template<template<class...> class L, class T>
struct mp_multiply_impl<L<T>>
{ using type = T; };

template<template<class...> class L, class T1, class... T>
struct mp_multiply_impl<L<T1, T...>>
{
using _type = mp_remove_const<decltype(T1::value)>;
static constexpr _type _value = T1::value * mp_multiply<L<T...>>::value;
using type = mp_const<_type, _value>;
};

template<template<class...> class L, class T>
struct mp_divide_impl<L<T>>
{ using type = T; };

template<template<class...> class L, class T1, class... T>
struct mp_divide_impl<L<T1, T...>>
{
using _type = mp_remove_const<decltype(T1::value)>;
static constexpr _type _value = T1::value / mp_multiply<L<T...>>::value;
using type = mp_const<_type, _value>;
};



/* generator functions */
template<class S>
struct next_integer_sequence;

template<class T, T... Ints>
struct next_integer_sequence<mp_integer_sequence<T, Ints...>>
{ using type = mp_integer_sequence<T, Ints..., sizeof...(Ints)>; };

template<class T, T I, T N>
struct mp_make_int_seq_impl
{ using type = typename next_integer_sequence<typename mp_make_int_seq_impl<T, I+1, N>::type>::type; };

template<class T, T N>
struct mp_make_int_seq_impl<T, N, N>
{ using type = mp_integer_sequence<T>; };

template<size_t N, class... T>
struct mp_repeat_impl
{
using _l1 = typename mp_repeat_impl<N/2, T...>::type;
using _l2 = typename mp_repeat_impl<N%2, T...>::type;
using type = mp_append<_l1, _l1, _l2>;
};

template<class... T> struct mp_repeat_impl<0, T...>
{ using type = mp_list<>; };

template<class... T> struct mp_repeat_impl<1, T...>
{ using type = mp_list<T...>; };



/* list operations returning a numeric value */
template<template<class...> class L, class... T>
struct mp_size_impl<L<T...>>
{ using type = mp_length<T...>; };

template<template<class...> class L, class... T, class V>
struct mp_count_impl<L<T...>, V>
{ using type = mp_size_t<cx_plus(std::is_same<T, V>::value...)>; };

template<template<class...> class L, class... T, template<class...> class P>
struct mp_count_if_impl<L<T...>, P>
{ using type = mp_size_t<cx_plus(P<T>::value...)>; };



/* operations to manipulate list content */
template<template<class...> class L, class... U, class... T>
struct mp_push_front_impl<L<U...>, T...>
{ using type = L<T..., U...>; };

template<template<class...> class L, class T1, class... T>
struct mp_pop_front_impl<L<T1, T...>>
{ using type = L<T...>; };

template<template<class...> class L, class... U, class... T>
struct mp_push_back_impl<L<U...>, T...>
{ using type = L<U..., T...>; };

template<template<class...> class L1, template<class...> class L2, class... T1, class... T2>
struct mp_concat_impl<L1<T1...>, L2<T2...>>
{ using type = L1<T1..., T2...>; };

template<template<class...> class L1, template<class...> class L2, class... T1, class... T2, class... L>
struct mp_concat_impl<L1<T1...>, L2<T2...>, L...>
{
using _rest = mp_concat<L2<T2...>, L...>;
using type = mp_concat<L1<T1...>, _rest>;
};

template<template<class...> class L, template<class> class F>
struct mp_filter_impl<L<>, F>
{ using type = L<>; };

template<template<class...> class L, template<class> class F, class T1, class... T>
struct mp_filter_impl<L<T1, T...>, F>
{
using _rest = mp_filter<L<T...>, F>;
using _match = F<T1>;
using type = mp_eval_if_c<_match::value == 0, _rest, mp_push_front, _rest, T1>;
};



/* list operations returning a bool value */
template<template<class...> class L1, class... T>
struct mp_empty_impl<L1<T...>>
{ using type = mp_bool<mp_length<T...>::value == 0>; };

template<template<class...> class L1, class... T, class... L>
struct mp_empty_impl<L1<T...>, L...>
{
static constexpr bool _first = !static_cast<bool>(mp_length<T...>::value);
static constexpr bool _other = static_cast<bool>(mp_empty<L...>::value);
using type = mp_bool<_first && _other>;
};



/* list operations returing elements from the list */
template<template<class...> class L, class T1, class... T>
struct mp_front_impl<L<T1, T...>>
{ using type = T1; };

template<template<class...> class L, class T1, class T2, class... T>
struct mp_second_impl<L<T1, T2, T...>>
{ using type = T2; };

template<class L, size_t I>
struct mp_at_c_impl
{
using map = mp_map_from_list<L>;
using type = mp_second<mp_map_find<map, mp_size_t<I>>>;
};

template<bool B, class L, template<class> class F, class TDefault>
struct mp_search_helper;

template<template<class...> class L, template<class> class F, class TDefault, class T1, class... T>
struct mp_search_helper<true, L<T1, T...>, F, TDefault>
{ using type = T1; };

template<template<class...> class L, template<class> class F, class TDefault, class T1, class... T>
struct mp_search_helper<false, L<T1, T...>, F, TDefault>
{ using type = mp_search<L<T...>, F, TDefault>; };

template<template<class...> class L, template<class> class F, class TDefault>
struct mp_search_impl<L<>, F, TDefault>
{ using type = TDefault; };

template<template<class...> class L, template<class> class F, class TDefault, class T1, class... T>
struct mp_search_impl<L<T1, T...>, F, TDefault>
{
using c_value = F<T1>;
using type = typename mp_search_helper<c_value::value, L<T1, T...>, F, TDefault>::type;
};

template<template<class...> class L, class V>
struct mp_find_impl<L<>, V>
{ using type = mp_zero; };

template<template<class...> class L, class... T, class V>
struct mp_find_impl<L<T...>, V>
{
static constexpr bool _v[] = { std::is_same<T, V>::value... };
using type = mp_size_t<cx_find_index(_v, _v + sizeof...(T))>;
};

template<template<class...> class M, class... T, class K>
struct mp_map_find_impl<M<T...>, K>
{
static mp_identity<void> f( ... );

template<template<class...> class L, class... U>
static mp_identity<L<K, U...>>
f( mp_identity<L<K, U...>>* );

using U = mp_inherit<mp_identity<T>...>;
using V = decltype( f((U*)0) );
using type = typename V::type;
};



/* list manipulators returning a new list */
template<template<class...> class L, class... T>
struct mp_clear_impl<L<T...>>
{ using type = L<>; };

template<template<class...> class L, class... T>
struct mp_unique_impl<L<>, L<T...>>
{ using type = L<T...>; };

template<template<class...> class L, class C, class T1, class... T>
struct mp_unique_impl<L<T1, T...>, C>
{
using type = mp_if<
mp_contains<C, T1>,
typename mp_unique_impl<L<T...>, C>::type,
typename mp_unique_impl<L<T...>, mp_push_back<C, T1>>::type
>;
};

template<template<class...> class A, class... T, template<class...> class B>
struct mp_rename_impl<A<T...>, B>
{ using type = B<T...>; };

template<>
struct mp_append_impl<>
{ using type = mp_list<>; };

template<template<class...> class L, class... T>
struct mp_append_impl<L<T...>>
{ using type = L<T...>; };

template<
template<class...> class L1, class... T1,
template<class...> class L2, class... T2, class... Lr>
struct mp_append_impl<L1<T1...>, L2<T2...>, Lr...>
{ using type = mp_append<L1<T1..., T2...>, Lr...>; };

template<template<class...> class L, class... T, size_t... J>
struct mp_map_from_list_impl<L<T...>, mp_integer_sequence<size_t, J...>>
{ using type = mp_list<mp_list<mp_size_t<J>, T>...>; };

template<template<class...> class F, class L1, class... L>
struct mp_transform_impl<F, mp_true, L1, L...>
{ using type = mp_clear<L1>; };

template<template<class...> class F, class... L>
struct mp_transform_impl<F, mp_false, L...>
{
using _first = F< typename mp_front_impl<L>::type... >;
using _rest = mp_transform< F, typename mp_pop_front_impl<L>::type... >;
using type = mp_push_front<_rest, _first>;
};



/* unique id */
template<class Counter>
struct mp_unique_counter
{
static inline size_t next()
{
static size_t value;
return value++;
}
};

template<class Counter, class... Args>
struct mp_unique_id
{
static inline size_t value()
{
static const size_t value = mp_unique_counter<Counter>::next();
return value;
}
};



/* string */
template <size_t N>
struct mp_string_impl<N, tag_reference>
{
private:
const char (&_data)[N + 1];

public:
inline std::string str() const
{ return std::string(_data, N); }

inline constexpr const char* data() const
{ return _data; }

inline constexpr size_t size() const
{ return N; }

inline constexpr char operator[](size_t i) const
{ return X_ASSERT(i >= 0 && i < N), _data[i]; }

constexpr mp_string_impl(const char (&data)[N + 1]) :
_data((X_ASSERT(data[N] == '\0'), data))
{ }
};

template <size_t N>
struct mp_string_impl<N, tag_array>
{
private:
constexpr static size_t strsize(const char* s)
{
size_t ret = 0;
while (s[ret] != 0)
++ret;
return ret;
}

private:
char _data[N + 1];

template <size_t N1, class T1, class T2, size_t... I1, size_t... I2>
constexpr mp_string_impl(
const mp_string_impl< N1, T1>& s1,
const mp_string_impl<N - N1, T2>& s2,
mp_index_sequence<I1...>,
mp_index_sequence<I2...>) :
_data { s1[I1]..., s2[I2]..., '\0' }
{ }

public:
inline std::string str() const
{ return std::string(_data, N); }

inline constexpr const char* data() const
{ return _data; }

inline constexpr size_t size() const
{ return N; }

inline constexpr char operator[](size_t i) const
{ return X_ASSERT(i >= 0 && i < N), _data[i]; }

template<size_t X = N, class Enable = mp_enable_if_c<(X == 1), char>>
constexpr mp_string_impl(char c) :
_data { c, '\0' }
{ }

template <size_t N1, class T1, class T2, mp_enable_if_c<(N1 <= N), bool> = true>
constexpr mp_string_impl(
const mp_string_impl<N1, T1>& s1,
const mp_string_impl<N - N1, T2>& s2) :
mp_string_impl(s1, s2, mp_make_index_sequence<N1>(), mp_make_index_sequence<N - N1>())
{ }
};

template<class X, class Enable = void>
struct __int_to_char;

template<class X>
struct __int_to_char<X, mp_enable_if_c<X::value >= 0 && X::value <= 9>>
{ static constexpr auto value = mp_array_string<1>('0' + X::value); };

template<class X>
struct __int_to_char<X, mp_enable_if_c<X::value >= 10 && X::value <= 16>>
{ static constexpr auto value = mp_array_string<1>('A' + X::value - 10); };

template<class X, int Base, bool b>
struct __int_to_string_helper
{
using _type = typename X::value_type;
using _next = mp_const<_type, X::value / static_cast<_type>(Base)>;
using _this = mp_const<_type, X::value % static_cast<_type>(Base)>;
static constexpr auto value = mp_string_cat(
__int_to_string_helper<_next, Base, _next::value == 0>::value,
__int_to_char<_this>::value);
};

template<class X, int Base>
struct __int_to_string_helper<X, Base, true>
{ static constexpr auto value = mp_make_string(""); };

template<class I, int Base>
struct mp_int_to_string_impl
{
static constexpr auto value = __int_to_string_helper<I, Base, I::value == 0>::value;
};

template<class J, template<class...> class L, class... T>
struct mp_string_type_join_impl<J, L<T...>>
{
static constexpr auto value = mp_string_join(J::value, T::value...);
};

}
}

+ 69
- 0
src/cpputils/Misc.h View File

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

#include <iostream>

namespace utl
{

/* TypeHelper ********************************************************************************/
template<class T>
struct TypeHelper
{
public:
static inline std::string name()
{
int status;
auto name = abi::__cxa_demangle(typeid(T).name(), 0, 0, &status);
return std::string(name ? name : typeid(T).name());
}
};

/* Helper Methods ****************************************************************************/
inline int bitCount(uint32_t u)
{
u = u
- ((u >> 1) & 033333333333)
- ((u >> 2) & 011111111111);
return ((u + (u >> 3)) & 030707070707) % 63;
}

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

/* Indent Stream *****************************************************************************/
namespace __impl
{
inline int identStreamIndex()
{
const int value = std::ios::xalloc();
return value;
}
}

inline std::ostream& incindent(std::ostream& os)
{
++os.iword(__impl::identStreamIndex());
return os;
}

inline std::ostream& decindent(std::ostream& os)
{
auto& indent = os.iword(__impl::identStreamIndex());
if (--indent < 0)
indent = 0;
return os;
}

inline std::ostream& indent(std::ostream& os)
{
auto i = os.iword(__impl::identStreamIndex());
while (i--)
os << " ";
return os;
}

}

+ 143
- 0
src/cpputils/Nullable.h View File

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

#include <memory>
#include <type_traits>
#include "Exception.h"

namespace utl
{

template<class T>
struct Nullable
{
public:
using value_type = T;
using clean_type = typename std::remove_reference<T>::type;
using pointer_type = clean_type*;

private:
struct Container
{
value_type value;

template<class... Args>
Container(Args&&... args) :
value(std::forward<Args>(args)...)
{ }
};

private:
std::unique_ptr<Container> _container;

inline void check() const
{
if (!_container)
throw EInvalidOperation("nullable does not have a value");
}

public:
inline value_type& value()
{ check(); return _container->value; }

inline const value_type& value() const
{ check(); return _container->value; }

inline bool hasValue() const
{ return static_cast<bool>(_container); }

inline void reset()
{ _container.reset(); }



inline value_type& operator*()
{ return value(); }

inline const value_type& operator*() const
{ return value(); }

inline pointer_type operator->()
{ return &value(); }

inline const pointer_type operator->() const
{ return &value(); }

inline value_type& operator()()
{ return value(); }

inline const value_type& operator()() const
{ return value(); }

inline explicit operator bool() const
{ return hasValue(); }

inline bool operator==(T v) const
{ return hasValue() && value() == v; }

inline bool operator==(const Nullable& other) const
{ return hasValue() && other.hasValue() && value() == other.value(); }

inline bool operator<(const Nullable& other) const
{
return this->hasValue()
&& other.hasValue()
&& value() < other.value();
}

inline bool operator>(const Nullable& other) const
{
return this->hasValue()
&& other.hasValue()
&& value() < other.value();
}

inline bool operator<=(const Nullable& other) const
{
return this->hasValue()
&& other.hasValue()
&& value() < other.value();
}

inline bool operator>=(const Nullable& other) const
{
return this->hasValue()
&& other.hasValue()
&& value() < other.value();
}

inline void operator=(value_type&& t)
{ _container.reset(new Container(std::forward<value_type>(t))); }

inline void operator=(const Nullable& other)
{
if (other.hasValue())
*this = other.value();
else
reset();
}

inline void operator=(Nullable&& other)
{ _container = std::move(other._container); }



Nullable()
{ }

template<class... Args>
Nullable(Args&&... args) :
_container(new Container(std::forward<Args>(args)...))
{ }

Nullable(Nullable& other)
{ if (static_cast<bool>(other)) *this = other(); }

Nullable(const Nullable& other)
{ if (static_cast<bool>(other)) *this = other(); }

Nullable(Nullable&& other) :
_container(std::move(other._container))
{ }
};

}

+ 166
- 0
src/cpputils/SmartPtr.h View File

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

#include <memory>
#include "MetaProgramming.h"
#include "Exception.h"

namespace utl
{

template<class T>
struct SmartPtr
{
private:
template<class Tt_base, class Tt_derived>
using cop_is_base_of = std::is_base_of<utl::mp_clean_type<Tt_base>, utl::mp_clean_type<Tt_derived>>;

template<class X, class Enable = void>
struct __impl_cop_is_value :
public utl::mp_true { };

template<class X>
struct __impl_cop_is_value<X*, void> :
public utl::mp_false { };

template<template<class> class F, class X>
struct __impl_cop_is_value<F<X>, utl::mp_enable_if<cop_is_base_of<SmartPtr<X>, F<X>>>> :
public utl::mp_false { };

template<class X>
struct __impl_cop_is_value<std::reference_wrapper<X>, void> :
public utl::mp_false { };

template<class X>
using cop_is_value = __impl_cop_is_value<utl::mp_clean_type<X>>;

struct op_deleter_noop
{ inline void operator()(T*) { } };

private:
std::shared_ptr<T> _value;

public:
inline bool hasValue() const
{ return static_cast<bool>(_value); }

inline T& value()
{
if (!_value)
throw utl::EInvalidOperation("SmartPtr does not have a value");
return *_value;
}

inline const T& value() const
{
if (!_value)
throw utl::EInvalidOperation("SmartPtr does not have a value");
return *_value;
}

inline T& operator*()
{ return value(); }

inline const T& operator*() const
{ return value(); }

inline T* operator->()
{ return &value(); }

inline const T* operator->() const
{ return &value(); }

inline operator bool() const
{ return hasValue(); }

inline SmartPtr& reset()
{
_value.reset();
return *this;
}

template<class X = T, utl::mp_enable_if_c<cop_is_value<X>::value, int> = 0>
inline SmartPtr& reset(X& x)
{
_value.reset(new X(x));
return *this;
}

template<class X = T, utl::mp_enable_if_c<cop_is_value<X>::value, int> = 0>
inline SmartPtr& reset(X&& x)
{
_value.reset(new X(std::move(x)));
return *this;
}

template<class X = T, utl::mp_enable_if_c<cop_is_base_of<T, X>::value, int> = 0>
inline SmartPtr& reset(const SmartPtr<X>& other)
{
_value = other._value;
return *this;
}

template<class X = T, utl::mp_enable_if_c<cop_is_base_of<T, X>::value, int> = 0>
inline SmartPtr& reset(SmartPtr<X>&& other)
{
_value = std::move(other._value);
return *this;
}

template<class X = T, utl::mp_enable_if_c<cop_is_base_of<T, X>::value, int> = 0>
inline SmartPtr& reset(std::reference_wrapper<X>&& ref)
{
_value.reset(&ref.get(), op_deleter_noop());
return *this;
}

template<class... Args>
inline SmartPtr& reset(Args&&... args)
{
_value.reset(new T(std::forward<Args>(args)...));
return *this;
}

template<class X = T>
inline SmartPtr& operator=(X&& x)
{ return reset(std::forward<X>(x)); }

SmartPtr()
{ }

template<class X = T, utl::mp_enable_if_c<cop_is_value<X>::value, int> = 0>
SmartPtr(X& x) :
_value(new X(x))
{ }

template<class X = T, utl::mp_enable_if_c<cop_is_value<X>::value, int> = 0>
SmartPtr(X&& x) :
_value(new X(std::move(x)))
{ }

template<class X = T, utl::mp_enable_if_c<std::is_base_of<T, X>::value, int> = 0>
SmartPtr(X* x) :
_value(x)
{ }

template<class X = T, utl::mp_enable_if_c<std::is_base_of<T, X>::value, int> = 0>
SmartPtr(const SmartPtr<X>& other) :
_value(other._value)
{ }

template<class X = T, utl::mp_enable_if_c<std::is_base_of<T, X>::value, int> = 0>
SmartPtr(SmartPtr<X>&& other) :
_value(std::move(other._value))
{ }

template<class X = T, utl::mp_enable_if_c<std::is_base_of<T, X>::value, int> = 0>
SmartPtr(std::reference_wrapper<X>&& ref) :
_value(&ref.get(), op_deleter_noop())
{ }

template<class... Args>
SmartPtr(Args... args) :
_value(new T(args...))
{ }
};

}

+ 107
- 0
src/cpputils/StreamHelper.h View File

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

#include <iostream>
#include "Exception.h"

namespace utl
{

namespace __impl
{
template<class T, class Enable = void>
struct op_write_stream
{
inline void operator()(std::ostream& os, const T& t)
{
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");
}
};

template<class T, class Enable = void>
struct op_read_stream
{
inline void 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");
}
};

template<>
struct op_write_stream<std::string, void>
{
inline void 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_write_stream<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(), t.size());
if (!os)
throw Exception("unable to write data to stream: stream error");
}
};

template<>
struct op_read_stream<std::string, void>
{
inline void operator()(std::istream& is, std::string& t)
{
uint32_t sz;
op_read_stream<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);
}
};

template<class T>
struct op_write_stream<T, decltype(std::declval<T>().serialize(std::declval<std::ostream&>()), void())>
{
inline void operator()(std::ostream& os, const T& t)
{ t.serialize(os); }
};

template<class T>
struct op_read_stream<T, decltype(std::declval<T>().deserialize(std::declval<std::istream&>()), void())>
{
inline void operator()(std::istream& os, T& t)
{ t.deserialize(os); }
};
}

struct StreamHelper
{
template<class T>
static inline void write(std::ostream& os, const T& t)
{ __impl::op_write_stream<T>()(os, t); }

template<class T>
static inline void read(std::istream& is, T& t)
{ __impl::op_read_stream<T>()(is, t); }

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

}

+ 170
- 0
src/cpputils/StringHelper.h View File

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

#include <sstream>
#include "Nullable.h"
#include "EnumConversion.h"

namespace utl
{

namespace __impl
{
template<class T, class Enable = void>
struct op_to_string
{
inline void operator()(std::ostream& os, const T& s) const
{ os << s; }
};

template<class T>
struct op_to_string<T, decltype(std::declval<T>().toString(std::declval<std::ostream&>()), void())>
{
inline void operator()(std::ostream& os, const T& s) const
{ s.toString(os); }
};

template<class T>
struct op_to_string<T, decltype(std::declval<T>().toString(), void())>
{
inline void operator()(std::ostream& os, const T& s) const
{ os << s.toString(); }
};

template<class T>
struct op_to_string<T, typename std::enable_if<std::is_enum<T>::value>::type>
{
inline void operator()(std::ostream& os, const T& s) const
{ os << EnumConversion<T>::toString(s, true); }
};

template<class T>
struct op_to_string<Nullable<T>, void>
{
inline void operator()(std::ostream& os, const Nullable<T>& s) const
{
if (s.hasValue())
op_to_string<T>()(os, s.value());
else
os << "null";
}
};


template<class T, class Enable = void>
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<class T>
struct op_from_string<T, decltype(std::declval<T&>().fromString(std::declval<const std::string&>()), void())>
{
inline bool operator()(const std::string& s, T& value) const
{ return value.fromString(s); }
};

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

template<class T>
struct op_from_string<T, typename std::enable_if<std::is_enum<T>::value>::type>
{
inline bool operator()(const std::string& s, T& value) const
{ return EnumConversion<T>::tryToEnum(s, value, true); }
};

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

template<class T>
struct op_from_string<T, typename std::enable_if<std::is_floating_point<T>::value>::type>
{
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);
}
};
}

template<class T>
inline void toString(std::ostream& os, const T& t)
{ __impl::op_to_string<T>()(os, t); }

template<class T>
inline std::string toString(const T& t)
{
std::ostringstream ss;
toString(ss, t);
return ss.str();
}

template<>
inline std::string toString<std::string>(const std::string& s)
{ return s; }



template<class T>
inline bool tryFromString(const std::string& s, T& value)
{ return __impl::op_from_string<T>()(s, value); }

template<class T>
inline T fromString(const std::string& s, T defaultValue)
{
T tmp;
return tryFromString<T>(s, tmp)
? tmp
: defaultValue;
}

template<class T>
inline T fromString(const std::string& s)
{
T tmp;
if (!tryFromString<T>(s, tmp))
throw Exception(std::string("unable to convert string to specific value: ") + s);
return tmp;
}

}

+ 81
- 0
src/cpputils/Time.h View File

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

#include <chrono>

namespace utl
{

namespace __impl
{
template<class From, class To>
struct 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 std::move(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 std::move(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));
}
};

}

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

}

+ 236
- 0
src/cpputils/TransformIterator.h View File

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

namespace utl
{

namespace __impl
{
template<class It, class Tag, class T = void>
using mp_enable_if_has_iterator_tag = typename std::enable_if<std::is_same<typename It::iterator_category, Tag>::value, T>::type;
}

template<class It, class Transform, class Enable = void>
struct TransformIterator;

template<class It, class Transform>
struct TransformIterator<It, Transform, __impl::mp_enable_if_has_iterator_tag<It, std::forward_iterator_tag>>
{
public:
using iterator_type = TransformIterator;
using iterator_category = std::input_iterator_tag;
using difference_type = typename It::difference_type;
using return_type = decltype(std::declval<Transform>()(std::declval<typename It::reference>()));
using value_type = typename std::remove_reference<return_type>::type;
using reference = value_type&;
using pointer = value_type*;

private:
It _it;
Transform _transform;

public:
TransformIterator()
{ }

TransformIterator(It it, Transform transform) :
_it (it),
_transform (transform)
{ }

inline iterator_type& operator=(const iterator_type& other)
{
_it = other._it;
return *this;
}

inline iterator_type& operator++()
{
++_it;
return *this;
}

inline iterator_type operator++(int)
{ return iterator_type(_it++, _transform); }

inline bool operator==(const iterator_type& other) const
{ return (_it == other._it) && (_transform == other._transform); }

inline bool operator!= (const iterator_type& other) const
{ return (_it != other._it) || (_transform != other._transform); }

inline return_type operator*()
{ return _transform(*_it); }

inline pointer operator->()
{ return &_transform(*_it); }
};

template<class It, class Transform>
struct TransformIterator<It, Transform, __impl::mp_enable_if_has_iterator_tag<It, std::bidirectional_iterator_tag>>
{
public:
using iterator_type = TransformIterator;
using iterator_category = std::input_iterator_tag;
using difference_type = typename It::difference_type;
using return_type = decltype(std::declval<Transform>()(std::declval<typename It::reference>()));
using value_type = typename std::remove_reference<return_type>::type;
using reference = value_type&;
using pointer = value_type*;

private:
It _it;
Transform _transform;

public:
TransformIterator()
{ }

TransformIterator(It it, Transform transform) :
_it (it),
_transform (transform)
{ }

inline iterator_type& operator=(const iterator_type& other)
{
_it = other._it;
return *this;
}

inline iterator_type& operator++()
{
++_it;
return *this;
}

inline iterator_type operator++(int)
{ return iterator_type(_it++, _transform); }

inline iterator_type& operator--()
{
--_it;
return *this;
}

inline iterator_type operator--(int)
{ return iterator_type(_it--, _transform); }

inline bool operator==(const iterator_type& other) const
{ return (_it == other._it) && (_transform == other._transform); }

inline bool operator!= (const iterator_type& other) const
{ return (_it != other._it) || (_transform != other._transform); }

inline return_type operator*()
{ return _transform(*_it); }

inline pointer operator->()
{ return &_transform(*_it); }
};

template<class It, class Transform>
struct TransformIterator<It, Transform, __impl::mp_enable_if_has_iterator_tag<It, std::random_access_iterator_tag>>
{
public:
using iterator_type = TransformIterator;
using iterator_category = std::input_iterator_tag;
using difference_type = typename It::difference_type;
using return_type = decltype(std::declval<Transform>()(std::declval<typename It::reference>()));
using value_type = typename std::remove_reference<return_type>::type;
using reference = value_type&;
using pointer = value_type*;

private:
It _it;
Transform _transform;

public:
TransformIterator()
{ }

TransformIterator(It it, Transform transform) :
_it (it),
_transform (transform)
{ }

inline iterator_type& operator=(const iterator_type& other)
{
_it = other._it;
return *this;
}

inline iterator_type& operator++()
{
++_it;
return *this;
}

inline iterator_type operator++(int)
{ return iterator_type(_it++, _transform); }

inline iterator_type& operator+=(difference_type n)
{
_it += n;
return *this;
}

inline iterator_type operator+(difference_type n) const
{ return iterator_type(_it + n, _transform); }

inline iterator_type& operator--()
{
--_it;
return *this;
}

inline iterator_type operator--(int)
{ return iterator_type(_it--, _transform); }

inline iterator_type& operator-=(difference_type n)
{
_it -= n;
return *this;
}

inline iterator_type operator-(difference_type n) const
{ return iterator_type(_it - n); }

inline difference_type operator-(const iterator_type& other) const
{ return _it - other._it; }

inline bool operator==(const iterator_type& other) const
{ return (_it == other._it) && (_transform == other._transform); }

inline bool operator!=(const iterator_type& other) const
{ return (_it != other._it) || (_transform != other._transform); }

inline bool operator>(const iterator_type& other) const
{ return _it > other._it; }

inline bool operator<(const iterator_type& other) const
{ return _it < other._it; }

inline bool operator>=(const iterator_type& other) const
{ return _it >= other._it; }

inline bool operator<=(const iterator_type& other) const
{ return _it <= other._it; }

inline return_type operator*() const
{ return _transform(*_it); }

inline pointer operator->() const
{ return &_transform(*_it); }

inline return_type operator[](difference_type n) const
{ return *(*this + n); }
};

template<class It, class Transform>
TransformIterator<It, Transform> makeTransformIterator(const It& it, const Transform& transform)
{ return TransformIterator<It, Transform>(it, transform); }

template<class It, class Transform, __impl::mp_enable_if_has_iterator_tag<It, std::random_access_iterator_tag, int> = 0>
inline TransformIterator<It, Transform>
operator+(const typename TransformIterator<It, Transform>::difference_type& n, const TransformIterator<It, Transform>& it)
{ return it + n; }
}

+ 1834
- 0
src/cpputils/old_linq.h
File diff suppressed because it is too large
View File


+ 24
- 0
test/CMakeLists.txt View File

@@ -0,0 +1,24 @@
# Project: test_tsoutils ##########################################################################

Project ( test_tsoutils )
Set ( CMAKE_CXX_STANDARD 14 )
Include ( GlobalCompilerFlags OPTIONAL )
File ( GLOB_RECURSE SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp )
Add_Executable ( test_tsoutils EXCLUDE_FROM_ALL ${SOURCE_FILES} )
If ( __COTIRE_INCLUDED )
Cotire ( test_tsoutils )
EndIf ( )
Target_Link_Libraries (
test_tsoutils
gtest
gmock
gmock_main
pthread
)

Add_Custom_Target ( run_test_tsoutils DEPENDS test_tsoutils COMMAND ./test_tsoutils )

If ( NOT TARGET tests)
Add_Custom_Target ( tests )
EndIf ( )
Add_Dependencies ( tests run_test_tsoutils )

+ 61
- 0
test/EnumConversionTests.cpp View File

@@ -0,0 +1,61 @@
#include <gtest/gtest.h>

namespace enum_conversion_tests
{
#include "../src/cpputils/EnumConversion.h"

enum class TestEnum
{
Test0,
Test1,
Test2,
Test3
};

DefEnumToStringMap(
TestEnum,
{ TestEnum::Test0, "Test0" },
{ TestEnum::Test1, "Test1" },
{ TestEnum::Test2, "Test2" },
{ TestEnum::Test3, "Test3" }
);

DefStringToEnumMap(
TestEnum,
InvariantStringLess,
{ "test0", TestEnum::Test0 },
{ "test1", TestEnum::Test1 },
{ "test2", TestEnum::Test2 },
{ "test3", TestEnum::Test3 }
);
}

using namespace ::testing;
using namespace ::enum_conversion_tests;
using namespace ::enum_conversion_tests::utl;

TEST(EnumConversionTests, toString)
{
EXPECT_EQ(std::string("Test0(0)"), utl::EnumConversion<TestEnum>::toString(TestEnum::Test0, true));
EXPECT_EQ(std::string("Test1"), utl::EnumConversion<TestEnum>::toString(TestEnum::Test1, false));
EXPECT_EQ(std::string("Test2(2)"), utl::EnumConversion<TestEnum>::toString(TestEnum::Test2, true));
EXPECT_EQ(std::string("Test3"), utl::EnumConversion<TestEnum>::toString(TestEnum::Test3, false));
EXPECT_EQ(std::string("123"), utl::EnumConversion<TestEnum>::toString(static_cast<TestEnum>(123), true));
}

TEST(EnumConversionTests, toEnum)
{
TestEnum e;
EXPECT_TRUE (utl::EnumConversion<TestEnum>::tryToEnum("test0", e, false));
EXPECT_EQ (TestEnum::Test0, e);

EXPECT_TRUE (utl::EnumConversion<TestEnum>::tryToEnum("Test1", e, false));
EXPECT_EQ (TestEnum::Test1, e);

EXPECT_FALSE(utl::EnumConversion<TestEnum>::tryToEnum("asd", e, false));

EXPECT_FALSE(utl::EnumConversion<TestEnum>::tryToEnum("1", e, false));

EXPECT_TRUE (utl::EnumConversion<TestEnum>::tryToEnum("2", e, true));
EXPECT_EQ (TestEnum::Test2, e);
}

+ 76
- 0
test/FlagsTests.cpp View File

@@ -0,0 +1,76 @@
#include <gtest/gtest.h>

namespace flags_tests
{
#include "../src/cpputils/Flags.h"
}

using namespace ::testing;
using namespace ::flags_tests::utl;

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

using TestFlags = ShiftedFlags<TestFlag>;

TEST(FlagsTest, 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(FlagsTest, 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(FlagsTest, isSet)
{
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.isSet(TestFlag::value0));
EXPECT_TRUE (f.isSet(TestFlag::value1));
EXPECT_FALSE(f.isSet(TestFlag::value2));
EXPECT_TRUE (f.isSet(TestFlag::value3));
EXPECT_FALSE(f.isSet(TestFlag::value4));
}

TEST(FlagsTest, clear)
{
TestFlags f({ TestFlag::value1, TestFlag::value3 });
f.clear(TestFlag::value1);
EXPECT_FALSE(f.isSet(TestFlag::value1));
EXPECT_TRUE (f.isSet(TestFlag::value3));
}

TEST(FlagsTest, reset)
{
TestFlags f({ TestFlag::value1, TestFlag::value3 });
f.reset();
EXPECT_FALSE(static_cast<bool>(f));
}

+ 464
- 0
test/LinqTests.cpp View File

@@ -0,0 +1,464 @@
#include <vector>
#include <gtest/gtest.h>
#include "../src/cpputils/Misc.h"
#include "../src/cpputils/Linq.h"

namespace linq_tests
{
struct TestData
{
int value;

TestData(int v) :
value(v)
{ }
};

struct TestDataMany
{
std::vector<int> values;

TestDataMany(const std::vector<int>& v) :
values(v)
{ }
};
}

using namespace ::linq;
using namespace ::linq_tests;

TEST(LinqTest, from_iterator)
{
std::vector<int> data({ 4, 5, 6, 7, 8 });
auto range = from_iterator(data.begin(), data.end());
ASSERT_TRUE (range.next());
ASSERT_EQ (&data.at(0), &range.front());
ASSERT_EQ (4, range.front());
ASSERT_TRUE (range.next());
ASSERT_EQ (&data.at(1), &range.front());
ASSERT_EQ (5, range.front());
ASSERT_TRUE (range.next());
ASSERT_EQ (&data.at(2), &range.front());
ASSERT_EQ (6, range.front());
ASSERT_TRUE (range.next());
ASSERT_EQ (&data.at(3), &range.front());
ASSERT_EQ (7, range.front());
ASSERT_TRUE (range.next());
ASSERT_EQ (&data.at(4), &range.front());
ASSERT_EQ (8, range.front());
ASSERT_FALSE(range.next());
}

TEST(LinqTest, from_container)
{
std::vector<int> data;
auto range = from_container(data);
data.assign({ 4, 5, 6, 7, 8 });
ASSERT_TRUE (range.next());
ASSERT_EQ (&data.at(0), &range.front());
ASSERT_EQ (4, range.front());
ASSERT_TRUE (range.next());
ASSERT_EQ (&data.at(1), &range.front());
ASSERT_EQ (5, range.front());
ASSERT_TRUE (range.next());
ASSERT_EQ (&data.at(2), &range.front());
ASSERT_EQ (6, range.front());
ASSERT_TRUE (range.next());
ASSERT_EQ (&data.at(3), &range.front());
ASSERT_EQ (7, range.front());
ASSERT_TRUE (range.next());
ASSERT_EQ (&data.at(4), &range.front());
ASSERT_EQ (8, range.front());
ASSERT_FALSE(range.next());
}

TEST(LinqTest, from_array)
{
int data[] = { 4, 5, 6, 7, 8 };
auto range = from_array(data);
ASSERT_TRUE (range.next());
ASSERT_EQ (&data[0], &range.front());
ASSERT_EQ (4, range.front());
ASSERT_TRUE (range.next());
ASSERT_EQ (&data[1], &range.front());
ASSERT_EQ (5, range.front());
ASSERT_TRUE (range.next());
ASSERT_EQ (&data[2], &range.front());
ASSERT_EQ (6, range.front());
ASSERT_TRUE (range.next());
ASSERT_EQ (&data[3], &range.front());
ASSERT_EQ (7, range.front());
ASSERT_TRUE (range.next());
ASSERT_EQ (&data[4], &range.front());
ASSERT_EQ (8, range.front());
ASSERT_FALSE(range.next());
}

TEST(LinqTest, from_generator)
{
int pos = 0;
std::vector<int> data({ 4, 5, 6, 7, 8 });
auto range = from_generator([&]{
int* ret = nullptr;
if (pos < data.size())
{
ret = &data.at(pos);
++pos;
}
return ret;
});
ASSERT_TRUE (range.next());
ASSERT_EQ (&data[0], &range.front());
ASSERT_EQ (4, range.front());
ASSERT_TRUE (range.next());
ASSERT_EQ (&data[1], &range.front());
ASSERT_EQ (5, range.front());
ASSERT_TRUE (range.next());
ASSERT_EQ (&data[2], &range.front());
ASSERT_EQ (6, range.front());
ASSERT_TRUE (range.next());
ASSERT_EQ (&data[3], &range.front());
ASSERT_EQ (7, range.front());
ASSERT_TRUE (range.next());
ASSERT_EQ (&data[4], &range.front());
ASSERT_EQ (8, range.front());
ASSERT_FALSE(range.next());
}

TEST(LinqTest, where)
{
std::vector<int> data({ 4, 5, 6, 7, 8 });
auto range = from_container(data)
>> where([](int& i) {
return (i & 1) == 0;
});
ASSERT_TRUE (range.next());
ASSERT_EQ (&data[0], &range.front());
ASSERT_EQ (4, range.front());
ASSERT_TRUE (range.next());
ASSERT_EQ (&data[2], &range.front());
ASSERT_EQ (6, range.front());
ASSERT_TRUE (range.next());
ASSERT_EQ (&data[4], &range.front());
ASSERT_EQ (8, range.front());
ASSERT_FALSE(range.next());
}

TEST(LinqTest, select)
{
std::vector<TestData> data({ TestData(1), TestData(2), TestData(3) });
auto range = from_container(data)
>> select([](TestData& td)->int& {
return td.value;
});
ASSERT_TRUE (range.next());
ASSERT_EQ (&data[0].value, &range.front());
ASSERT_EQ (1, range.front());
ASSERT_TRUE (range.next());
ASSERT_EQ (&data[1].value, &range.front());
ASSERT_EQ (2, range.front());
ASSERT_TRUE (range.next());
ASSERT_EQ (&data[2].value, &range.front());
ASSERT_EQ (3, range.front());
ASSERT_FALSE(range.next());
}

TEST(LinqTest, select_many)
{
std::vector<TestDataMany> data({ TestDataMany({ 1, 2, 3, 4 }), TestDataMany({ }), TestDataMany({ 5, 6 }) });
auto range = from_container(data)
>> select_many([](TestDataMany& td)->std::vector<int>& {
return td.values;
});
ASSERT_TRUE (range.next());
ASSERT_EQ (&data[0].values[0], &range.front());
ASSERT_EQ (1, range.front());
ASSERT_TRUE (range.next());
ASSERT_EQ (&data[0].values[1], &range.front());
ASSERT_EQ (2, range.front());
ASSERT_TRUE (range.next());
ASSERT_EQ (&data[0].values[2], &range.front());
ASSERT_EQ (3, range.front());
ASSERT_TRUE (range.next());
ASSERT_EQ (&data[0].values[3], &range.front());
ASSERT_EQ (4, range.front());
ASSERT_TRUE (range.next());
ASSERT_EQ (&data[2].values[0], &range.front());
ASSERT_EQ (5, range.front());
ASSERT_TRUE (range.next());
ASSERT_EQ (&data[2].values[1], &range.front());
ASSERT_EQ (6, range.front());
ASSERT_FALSE(range.next());
}

TEST(LinqTest, order_by)
{
std::vector<int> data({ 6, 3, 8, 5, 1 });
auto range = from_container(data) >> order_by();
ASSERT_TRUE (range.next());
ASSERT_EQ (&data.at(4), &range.front());
ASSERT_EQ (1, range.front());
ASSERT_TRUE (range.next());
ASSERT_EQ (&data.at(1), &range.front());
ASSERT_EQ (3, range.front());
ASSERT_TRUE (range.next());
ASSERT_EQ (&data.at(3), &range.front());
ASSERT_EQ (5, range.front());
ASSERT_TRUE (range.next());
ASSERT_EQ (&data.at(0), &range.front());
ASSERT_EQ (6, range.front());
ASSERT_TRUE (range.next());
ASSERT_EQ (&data.at(2), &range.front());
ASSERT_EQ (8, range.front());
ASSERT_FALSE(range.next());
}

TEST(LinqTest, distinct)
{
std::vector<TestData> data({ TestData(1), TestData(2), TestData(3), TestData(1), TestData(2), TestData(4) });
auto range = from_container(data)
>> distinct([](TestData& l, TestData& r){
return l.value < r.value;
});
ASSERT_TRUE (range.next());
ASSERT_EQ (&data.at(0), &range.front());
ASSERT_TRUE (range.next());
ASSERT_EQ (&data.at(1), &range.front());
ASSERT_TRUE (range.next());
ASSERT_EQ (&data.at(2), &range.front());
ASSERT_TRUE (range.next());
ASSERT_EQ (&data.at(5), &range.front());
ASSERT_FALSE(range.next());
}

TEST(LinqTest, count)
{
std::vector<int> data({ 4, 5, 6, 7, 8 });
size_t cnt = from_container(data) >> count();
ASSERT_EQ(5, cnt);
}

TEST(LinqTest, sum)
{
std::vector<int> data({ 4, 5, 6, 7, 8 });
auto sum = from_container(data) >> linq::sum();
ASSERT_EQ(30, sum);
}

TEST(LinqTest, min)
{
std::vector<int> data({ 4, 5, 6, 7, 8 });
ASSERT_EQ(4, from_container(data) >> min());
}

TEST(LinqTest, max)
{
std::vector<int> data({ 4, 5, 6, 7, 8 });
ASSERT_EQ(8, from_container(data) >> max());
}

TEST(LinqTest, any)
{
std::vector<int> data({ 4, 5, 6, 7, 8 });
ASSERT_TRUE (from_container(data) >> any());
data.clear();
ASSERT_FALSE(from_container(data) >> any());
}

TEST(LinqTest, contains)
{
std::vector<int> data({ 4, 5, 6, 7, 8 });
ASSERT_TRUE (from_container(data) >> contains(5));
ASSERT_FALSE(from_container(data) >> contains(9));
}

TEST(LinqTest, single)
{
std::vector<int> data({ 4, 5, 6, 7, 8 });
EXPECT_ANY_THROW(from_container(data) >> single());
data.assign({ 9 });
EXPECT_EQ(9, from_container(data) >> single());
data.clear();
EXPECT_ANY_THROW(from_container(data) >> single());
}

TEST(LinqTest, single_or_default)
{
std::vector<int> data({ 4, 5, 6, 7, 8 });
EXPECT_EQ(0, from_container(data) >> single_or_default());
data.assign({ 9 });
EXPECT_EQ(9, from_container(data) >> single_or_default());
data.clear();
EXPECT_EQ(0, from_container(data) >> single_or_default());
}

TEST(LinqTest, first)
{
std::vector<int> data({ 4, 5, 6, 7, 8 });
EXPECT_EQ(4, from_container(data) >> first());
data.assign({ 9 });
EXPECT_EQ(9, from_container(data) >> first());
data.clear();
EXPECT_ANY_THROW(from_container(data) >> first());
}

TEST(LinqTest, first_or_default)
{
std::vector<int> data({ 4, 5, 6, 7, 8 });
EXPECT_EQ(4, from_container(data) >> first_or_default());
data.assign({ 9 });
EXPECT_EQ(9, from_container(data) >> first_or_default());
data.clear();
EXPECT_EQ(0, from_container(data) >> first_or_default());
}


TEST(LinqTest, last)
{
std::vector<int> data({ 4, 5, 6, 7, 8 });
EXPECT_EQ(8, from_container(data) >> last());
data.assign({ 9 });
EXPECT_EQ(9, from_container(data) >> last());
data.clear();
EXPECT_ANY_THROW(from_container(data) >> last());
}

TEST(LinqTest, last_or_default)
{
std::vector<int> data({ 4, 5, 6, 7, 8 });
EXPECT_EQ(8, from_container(data) >> last_or_default());
data.assign({ 9 });
EXPECT_EQ(9, from_container(data) >> last_or_default());
data.clear();
EXPECT_EQ(0, from_container(data) >> last_or_default());
}

TEST(LinqTest, to_vector)
{
int data[] = { 4, 5, 6, 7, 8 };
auto vec = from_array(data) >> to_vector();
ASSERT_EQ(5, vec.size());
EXPECT_EQ(4, vec[0]);
EXPECT_EQ(5, vec[1]);
EXPECT_EQ(6, vec[2]);
EXPECT_EQ(7, vec[3]);
EXPECT_EQ(8, vec[4]);
}

TEST(LinqTest, to_list)
{
int data[] = { 4, 5, 6, 7, 8 };
auto list = from_array(data) >> to_list();
auto it = list.begin();
ASSERT_NE(it, list.end());
EXPECT_EQ(4, *it);
++it;
ASSERT_NE(it, list.end());
EXPECT_EQ(5, *it);
++it;
ASSERT_NE(it, list.end());
EXPECT_EQ(6, *it);
++it;
ASSERT_NE(it, list.end());
EXPECT_EQ(7, *it);
++it;
ASSERT_NE(it, list.end());
EXPECT_EQ(8, *it);
++it;
ASSERT_EQ(it, list.end());
}

TEST(LinqTest, to_map)
{
using pair_type = std::pair<int, std::string>;
using vector_type = std::vector<pair_type>;
vector_type data({
std::make_pair(1, "1"),
std::make_pair(2, "2"),
std::make_pair(3, "3")
});

auto map0 = from_container(data) >> to_map();
auto it = map0.begin();
ASSERT_NE(it, map0.end());
EXPECT_EQ(1, it->first);
EXPECT_EQ(std::string("1"), it->second);
++it;
ASSERT_NE(it, map0.end());
EXPECT_EQ(2, it->first);
EXPECT_EQ(std::string("2"), it->second);
++it;
ASSERT_NE(it, map0.end());
EXPECT_EQ(3, it->first);
EXPECT_EQ(std::string("3"), it->second);
++it;
ASSERT_EQ(it, map0.end());

data.emplace_back(3, "4");
EXPECT_ANY_THROW(from_container(data) >> to_map());
}

TEST(LinqTest, for_each)
{
int index = 0;
int data[] = { 4, 5, 6, 7, 8 };
from_array(data) >> for_each([&](int& i){
EXPECT_EQ(&data[index], &i);
++index;
});
}

TEST(LinqTest, to_lookup)
{
using pair_type = std::pair<int, std::string>;
using vector_type = std::vector<pair_type>;
vector_type data({
{ 0, "Str0-0" },
{ 1, "Str1-0" },
{ 2, "Str2-0" },
{ 0, "Str0-1" },
{ 1, "Str1-1" },
{ 2, "Str2-1" },
{ 1, "Str1-2" },
{ 0, "Str0-2" },
});
auto lookup = from_container(data)
>> to_lookup([](pair_type& p)->int&{
return p.first;
}, [](pair_type& p)->std::string&{
return p.second;
});

auto range0 = lookup[0];
ASSERT_TRUE (range0.next());
ASSERT_EQ (&data.at(0).second, &range0.front());
ASSERT_EQ (std::string("Str0-0"), range0.front());
ASSERT_TRUE (range0.next());
ASSERT_EQ (&data.at(3).second, &range0.front());
ASSERT_EQ (std::string("Str0-1"), range0.front());
ASSERT_TRUE (range0.next());
ASSERT_EQ (&data.at(7).second, &range0.front());
ASSERT_EQ (std::string("Str0-2"), range0.front());
ASSERT_FALSE(range0.next());

auto range1 = lookup[1];
ASSERT_TRUE (range1.next());
ASSERT_EQ (&data.at(1).second, &range1.front());
ASSERT_EQ (std::string("Str1-0"), range1.front());
ASSERT_TRUE (range1.next());
ASSERT_EQ (&data.at(4).second, &range1.front());
ASSERT_EQ (std::string("Str1-1"), range1.front());
ASSERT_TRUE (range1.next());
ASSERT_EQ (&data.at(6).second, &range1.front());
ASSERT_EQ (std::string("Str1-2"), range1.front());
ASSERT_FALSE(range1.next());

auto range2 = lookup[2];
ASSERT_TRUE (range2.next());
ASSERT_EQ (&data.at(2).second, &range2.front());
ASSERT_EQ (std::string("Str2-0"), range2.front());
ASSERT_TRUE (range2.next());
ASSERT_EQ (&data.at(5).second, &range2.front());
ASSERT_EQ (std::string("Str2-1"), range2.front());
ASSERT_FALSE(range2.next());
}

+ 135
- 0
test/LoggingTests.cpp View File

@@ -0,0 +1,135 @@
#include <set>
#include <list>
#include <regex>
#include <mutex>
#include <thread>
#include <memory>
#include <chrono>
#include <iostream>
#include <gtest/gtest.h>
#include <gmock/gmock.h>

namespace logging_tests
{
#include "../src/LoggerImpl.cpp"
}

using namespace ::testing;
using namespace ::logging_tests;
using namespace ::logging_tests::logging;

struct LoggingReset
{
~LoggingReset()
{ ::logging_tests::logging::resetLogging(); }
};

struct ConsumerMock : public Consumer
{
MOCK_METHOD1(log, void (DataPtrS data));

ConsumerMock(const std::string& n) :
Consumer(n, true)
{ }
};

MATCHER_P5(MatchLogData, level, sender, thread, name, message, "")
{
if (!arg)
return false;
auto& d = *arg;
return d.level == level
&& d.sender == sender
&& d.thread == thread
&& d.name == name
&& d.message == message;
}

TEST(LoggingTests, AllMatcher)
{
LoggingReset loggingReset;
StreamConsumer c0("TestConsumer1", std::cout, false, false);
StreamConsumer c1("TestConsumer2", std::cout, false, false);

auto& l0 = getLogger();
auto& l1 = getLogger("TestLogger");

AllMatcher matcher;

EXPECT_TRUE(matcher.match(c0));
EXPECT_TRUE(matcher.match(c1));
EXPECT_TRUE(matcher.match(l0));
EXPECT_TRUE(matcher.match(l1));
}

TEST(LoggingTests, DefaultLoggerMatcher)
{
LoggingReset loggingReset;
StreamConsumer c0("TestConsumer1", std::cout, false, false);
StreamConsumer c1("TestConsumer2", std::cout, false, false);

auto& l0 = getLogger();
auto& l1 = getLogger("TestLogger");

DefaultLoggerMatcher matcher;

EXPECT_FALSE(matcher.match(c0));
EXPECT_FALSE(matcher.match(c1));
EXPECT_TRUE (matcher.match(l0));
EXPECT_FALSE(matcher.match(l1));
}

TEST(LoggingTests, RegexMatcher)
{
LoggingReset loggingReset;
StreamConsumer c0("TestConsumer1", std::cout, false, false);
StreamConsumer c1("TestConsumer2", std::cout, false, false);

auto& l0 = getLogger();
auto& l1 = getLogger("TestLogger");

RegexMatcher matcher0("TestConsumer1");
RegexMatcher matcher1("ASEF", true);

EXPECT_TRUE (matcher0.match(c0));
EXPECT_FALSE(matcher0.match(c1));
EXPECT_FALSE(matcher1.match(l0));
EXPECT_TRUE (matcher1.match(l1));
}

TEST(LoggingTests, log)
{
LoggingReset loggingReset;
StrictMock<ConsumerMock> c0("consumer0");
StrictMock<ConsumerMock> c1("Consumer1");

EXPECT_CALL(c0, log(MatchLogData(
Level::Info,
(void*)12,
std::this_thread::get_id(),
std::string("logger0"),
std::string("test1 info"))));

EXPECT_CALL(c0, log(MatchLogData(
Level::Warn,
(void*)13,
std::this_thread::get_id(),
std::string("logger0"),
std::string("test1 warn"))));

defineRule(MatcherPtrU(new RegexMatcher("logger0")), MatcherPtrU(new RegexMatcher("consumer0")), Level::Info, Level::Warn);

auto& l0 = getLogger("logger0");
auto& l1 = getLogger("logger1");

logMessage(l0, Debug, (void*)11, "test1 ") << "debug";
logMessage(l0, Info, (void*)12, "test1 ") << "info";
logMessage(l0, Warn, (void*)13, "test1 ") << "warn";
logMessage(l0, Error, (void*)14, "test1 ") << "error";

logMessage(l1, Debug, (void*)21, "test2 ") << "debug";
logMessage(l1, Info, (void*)22, "test2 ") << "info";
logMessage(l1, Warn, (void*)23, "test2 ") << "warn";
logMessage(l1, Error, (void*)24, "test2 ") << "error";
}


+ 339
- 0
test/MetaProgrammingTests.cpp View File

@@ -0,0 +1,339 @@
#include <gtest/gtest.h>

#include "../src/cpputils/MetaProgramming.h"

using namespace utl;

struct X1 { };
struct X2 { };
struct X3 { };

struct Y1 { };
struct Y2 { };
struct Y3 { };

struct Z1 { };
struct Z2 { };
struct Z3 { };


/* logical operations ****************************************************************************/
/* mp_if */
using mp_if_c_expected_0 = int;
using mp_if_c_actual_0 = mp_if_c<true, int, double>;
static_assert(std::is_same<mp_if_c_expected_0, mp_if_c_actual_0>::value, "mp_if");

using mp_if_c_expected_1 = double;
using mp_if_c_actual_1 = mp_if_c<false, int, double>;
static_assert(std::is_same<mp_if_c_expected_1, mp_if_c_actual_1>::value, "mp_if");

/* mp_eval_if(_c) */
using mp_eval_if_expected_0 = int;
using mp_eval_if_actual_0 = mp_eval_if_c<true, int, mp_add_pointer, int, int, int>; /* erroneous mp_add_pointer require only one argument */
static_assert(std::is_same<mp_eval_if_expected_0, mp_eval_if_actual_0>::value, "mp_eval_if(_c)");

using mp_eval_if_expected_1 = int*;
using mp_eval_if_actual_1 = mp_eval_if_c<false, int, mp_add_pointer, int>;
static_assert(std::is_same<mp_eval_if_expected_1, mp_eval_if_actual_1>::value, "");

/* mp_not */
using mp_not_input = mp_list<mp_false, mp_true, mp_false>;
using mp_not_exected = mp_list<mp_true, mp_false, mp_true>;
using mp_not_actual = mp_not<mp_not_input>;
static_assert(std::is_same<mp_not_exected, mp_not_actual>::value, "mp_not");

/* mp_and */
using mp_and_input_0 = mp_list<mp_true, mp_false, mp_true>;
using mp_and_expected_0 = mp_false;
using mp_and_actual_0 = mp_and<mp_and_input_0>;
static_assert(std::is_same<mp_and_expected_0, mp_and_actual_0>::value, "mp_and");

using mp_and_input_1 = mp_list<mp_true, mp_true, mp_true>;
using mp_and_expected_1 = mp_true;
using mp_and_actual_1 = mp_and<mp_and_input_1>;
static_assert(std::is_same<mp_and_expected_1, mp_and_actual_1>::value, "mp_and");

/* mp_or */
using mp_or_input_0 = mp_list<mp_false, mp_false, mp_false>;
using mp_or_expected_0 = mp_false;
using mp_or_actual_0 = mp_or<mp_or_input_0>;
static_assert(std::is_same<mp_or_expected_0, mp_or_actual_0>::value, "mp_or");

using mp_or_input_1 = mp_list<mp_false, mp_false, mp_true>;
using mp_or_expected_1 = mp_true;
using mp_or_actual_1 = mp_or<mp_or_input_1>;
static_assert(std::is_same<mp_or_expected_1, mp_or_actual_1>::value, "mp_or");



/* arithmetic ************************************************************************************/
/* mp_plus */
using mp_plus_input = mp_list<mp_const<int, 1>, mp_const<uint, 5>, mp_const<uint8_t, 3>>;
using mp_plus_expected = mp_const<int, 9>;
using mp_plus_actual = mp_plus<mp_plus_input>;
static_assert(std::is_same<mp_plus_expected, mp_plus_actual>::value, "mp_plus");

/* mp_minus */
using mp_minus_input = mp_list<mp_const<int, 1>, mp_const<uint, 5>, mp_const<uint8_t, 3>>;
using mp_minus_expected = mp_const<int, -7>;
using mp_minus_actual = mp_minus<mp_minus_input>;
static_assert(std::is_same<mp_minus_expected, mp_minus_actual>::value, "mp_minus");

/* mp_multiply */
using mp_multiply_input = mp_list<mp_const<int, 1>, mp_const<uint, 5>, mp_const<uint8_t, 3>>;
using mp_multiply_expected = mp_const<int, 15>;
using mp_multiply_actual = mp_multiply<mp_multiply_input>;
static_assert(std::is_same<mp_multiply_expected, mp_multiply_actual>::value, "mp_multiply");

/* mp_divide */
using mp_divide_input = mp_list<mp_const<int, 75>, mp_const<uint, 5>, mp_const<uint8_t, 3>>;
using mp_divide_expected = mp_const<int, 5>;
using mp_divide_actual = mp_divide<mp_divide_input>;
static_assert(std::is_same<mp_divide_expected, mp_divide_actual>::value, "mp_divide");



/* generator functions ***************************************************************************/
/* mp_make_integer_sequence */
using mp_make_integer_sequence_expected = mp_integer_sequence<int, 0, 1, 2, 3, 4, 5>;
using mp_make_integer_sequence_actual = mp_make_integer_sequence<int, 6>;
static_assert(std::is_same<mp_make_integer_sequence_expected, mp_make_integer_sequence_actual>::value, "mp_make_integer_sequence");

/* mp_make_index_sequence */
using mp_make_index_sequence_expected = mp_index_sequence<0, 1, 2, 3, 4, 5>;
using mp_make_index_sequence_actual = mp_make_index_sequence<6>;
static_assert(std::is_same<mp_make_index_sequence_expected, mp_make_index_sequence_actual>::value, "mp_make_index_sequence");

/* mp_repeat */
using mp_repeat_expected = mp_list<X1, X2, X3, X1, X2, X3, X1, X2, X3>;
using mp_repeat_actual = mp_repeat<3, X1, X2, X3>;
static_assert(std::is_same<mp_repeat_expected, mp_repeat_actual>::value, "mp_repeat");



/* list operations returning a numeric value *****************************************************/
/* mp_length */
using mp_length_expected = mp_size_t<3>;
using mp_length_actual = mp_length<X1, X2, X3>;
static_assert(std::is_same<mp_length_expected, mp_length_actual>::value, "mp_length");

/* mp_size */
using mp_size_input = mp_list<X1, X2, X3>;
using mp_size_expected = mp_size_t<3>;
using mp_size_actual = mp_size<mp_size_input>;
static_assert(std::is_same<mp_size_expected, mp_size_actual>::value, "mp_size");

/* mp_count */
using mp_count_input = mp_list<X1, X2, X3, X1, X2, X1>;
using mp_count_expected = mp_size_t<3>;
using mp_count_actual = mp_count<mp_count_input, X1>;
static_assert(std::is_same<mp_count_expected, mp_count_actual>::value, "mp_count");

/* mp_count_if */
template<class T>
using mp_count_if_op = mp_or<mp_list<std::is_same<T, X1>, std::is_same<T, X2>>>;
using mp_count_if_input = mp_list<X1, X2, X3, X1, X2, X1>;
using mp_count_if_expected = mp_size_t<5>;
using mp_count_if_actual = mp_count_if<mp_count_input, mp_count_if_op>;
static_assert(std::is_same<mp_count_if_expected, mp_count_if_actual>::value, "mp_count_if");



/* operations to manipulate list content *********************************************************/
/* mp_push_front */
using mp_push_front_input = mp_list<X1, X2>;
using mp_push_front_expected = mp_list<Y1, Y2, X1, X2>;
using mp_push_front_actual = mp_push_front<mp_push_front_input, Y1, Y2>;
static_assert(std::is_same<mp_push_front_expected, mp_push_front_actual>::value, "mp_push_front");

/* mp_pop_front */
using mp_pop_front_input = mp_list<X1, X2, X3>;
using mp_pop_front_expected = mp_list<X2, X3>;
using mp_pop_front_actual = mp_pop_front<mp_pop_front_input>;
static_assert(std::is_same<mp_pop_front_expected, mp_pop_front_actual>::value, "mp_pop_front");

/* mp_push_bask */
using mp_push_back_input = mp_list<X1, X2>;
using mp_push_back_expected = mp_list<X1, X2, Y1, Y2>;
using mp_push_back_actual = mp_push_back<mp_push_back_input, Y1, Y2>;
static_assert(std::is_same<mp_push_back_expected, mp_push_back_actual>::value, "mp_push_bask");

/* mp_concat */
using mp_concat_expected = mp_list<X1, X2, Y1, Y2, Z1, Z2, Z3>;
using mp_concat_actual = mp_concat<mp_list<X1, X2>, mp_list<Y1, Y2>, mp_list<Z1, Z2, Z3>>;
static_assert(std::is_same<mp_concat_expected, mp_concat_actual>::value, "mp_concat");

/* mp_filter */
template<class T>
using mp_filter_func = mp_bool<mp_or<mp_list<mp_is_same<T, X2>, mp_is_same<T, Y2>, mp_is_same<T, Z2>>>::value == 0>;
using mp_filter_input = mp_list<X1, X2, X3, Y1, Y2, Y3, Z1, Z2, Z3>;
using mp_filter_expected = mp_list<X1, X3, Y1, Y3, Z1, Z3>;
using mp_filter_actual = mp_filter<mp_filter_input, mp_filter_func>;
static_assert(std::is_same<mp_filter_expected, mp_filter_actual>::value, "mp_filter");



/* list operations returning a bool value ********************************************************/
/* mp_empty */
using mp_empty_input_0 = mp_list<>;
using mp_empty_input_1 = mp_list<X1>;
using mp_empty_expected_0 = mp_false;
using mp_empty_actual_0 = mp_empty<mp_empty_input_0, mp_empty_input_1>;
static_assert(std::is_same<mp_empty_expected_0, mp_empty_actual_0>::value, "mp_empty");

using mp_empty_expected_1 = mp_true;
using mp_empty_actual_1 = mp_empty<mp_empty_input_0, mp_empty_input_0>;
static_assert(std::is_same<mp_empty_expected_1, mp_empty_actual_1>::value, "mp_empty");

/* mp_contains */
using mp_contains_input_0 = mp_list<X1, X2, X3, X1>;
using mp_contains_expected_0 = mp_true;
using mp_contains_actual_0 = mp_contains<mp_contains_input_0, X1>;
static_assert(std::is_same<mp_contains_expected_0, mp_contains_actual_0>::value, "mp_contains");

using mp_contains_input_1 = mp_list<X1, X2, X3>;
using mp_contains_expected_1 = mp_false;
using mp_contains_actual_1 = mp_contains<mp_contains_input_1, Y1>;
static_assert(std::is_same<mp_contains_expected_1, mp_contains_actual_1>::value, "mp_contains");



/* list operations returing elements from the list ***********************************************/
/* mp_front */
using mp_front_input = mp_list<X1, X2, X3>;
using mp_front_expected = X1;
using mp_front_actual = mp_front<mp_front_input>;
static_assert(std::is_same<mp_front_expected, mp_front_actual>::value, "mp_front");

/* mp_second */
using mp_second_input = mp_list<X1, X2, X3>;
using mp_second_expected = X2;
using mp_second_actual = mp_second<mp_second_input>;
static_assert(std::is_same<mp_second_expected, mp_second_actual>::value, "mp_second");

/* mp_at(_c) */
using mp_at_c_input = mp_list<X1, X2, X3, Y1, Y2, Y3>;
using mp_at_c_expected = Y1;
using mp_at_c_actual = mp_at_c<mp_at_c_input, 3>;
static_assert(std::is_same<mp_at_c_expected, mp_at_c_actual>::value, "mp_at(_c)");

/* mp_search */
template<class T>
using mp_search_func_0 = std::is_same<T, X2>;
using mp_search_input_0 = mp_list<X1, X2, X3>;
using mp_search_expected_0 = X2;
using mp_search_actual_0 = mp_search<mp_search_input_0, mp_search_func_0>;
static_assert(std::is_same<mp_search_expected_0, mp_search_actual_0>::value, "mp_search");

template<class T>
using mp_search_func_1 = mp_false;
using mp_search_input_1 = mp_list<X1, X2, X3>;
using mp_search_expected_1 = Y2;
using mp_search_actual_1 = mp_search<mp_search_input_1, mp_search_func_1, Y2>;
static_assert(std::is_same<mp_search_expected_1, mp_search_actual_1>::value, "mp_search");

/* mp_find */
using mp_find_input_0 = mp_list<X1, X2, X3, Y2>;
using mp_find_expected_0 = mp_size_t<2>;
using mp_find_actual_0 = mp_find<mp_find_input_0, X3>;
static_assert(std::is_same<mp_find_expected_0, mp_find_actual_0>::value, "mp_find");

using mp_find_input_1 = mp_list<X1, X2, X3, Y2>;
using mp_find_expected_1 = mp_size_t<4>;
using mp_find_actual_1 = mp_find<mp_find_input_0, Z1>;
static_assert(std::is_same<mp_find_expected_1, mp_find_actual_1>::value, "mp_find");

/* mp_map_find */
using mp_map_find_input_0 = mp_list<mp_list<X1, X2, X3>, mp_list<Y1, Y2, Y3>, mp_list<Z1, Z2, Z3>>;
using mp_map_find_expected_0 = mp_list<Y1, Y2, Y3>;
using mp_map_find_actual_0 = mp_map_find<mp_map_find_input_0, Y1>;
static_assert(std::is_same<mp_map_find_expected_0, mp_map_find_actual_0>::value, "mp_map_find");

using mp_map_find_input_1 = mp_list<mp_list<X1, X2, X3>, mp_list<Y1, Y2, Y3>, mp_list<Z1, Z2, Z3>>;
using mp_map_find_expected_1 = void;
using mp_map_find_actual_1 = mp_map_find<mp_map_find_input_1, X3>;
static_assert(std::is_same<mp_map_find_expected_1, mp_map_find_actual_1>::value, "mp_map_find");



/* list manipulators returning a new list ********************************************************/
/* mp_clear */
using mp_clear_input = mp_list<X1, X2, X3>;
using mp_clear_expected = mp_list<>;
using mp_clear_actual = mp_clear<mp_clear_input>;
static_assert(std::is_same<mp_clear_expected, mp_clear_actual>::value, "mp_unique");

/* mp_unique */
using mp_unique_input = mp_list<X1, X2, X1, X2, X3, X1, X2, Y1, X3>;
using mp_unique_expected = mp_list<X1, X2, X3, Y1>;
using mp_unique_actual = mp_unique<mp_unique_input>;
static_assert(std::is_same<mp_unique_expected, mp_unique_actual>::value, "mp_unique");

/* mp_rename */
using mp_rename_input = mp_list<X1, X2, X3>;
using mp_rename_expected = std::tuple<X1, X2, X3>;
using mp_rename_actual = mp_rename<mp_rename_input, std::tuple>;
static_assert(std::is_same<mp_rename_expected, mp_rename_actual>::value, "mp_rename");

/* mp_append */
using mp_append_input_X = mp_list<X1, X2, X3>;
using mp_append_input_Y = mp_list<Y1, Y2, Y3>;
using mp_append_input_Z = mp_list<Z1, Z2, Z3>;
using mp_append_expected = mp_list<X1, X2, X3, Y1, Y2, Y3, Z1, Z2, Z3>;
using mp_append_actual = mp_append<mp_append_input_X, mp_append_input_Y, mp_append_input_Z>;
static_assert(std::is_same<mp_append_expected, mp_append_actual>::value, "mp_append");

/* mp_map_from_list */
using mp_map_from_list_input = mp_list<X1, X2, X3>;
using mp_map_from_list_expected = mp_list<mp_list<mp_size_t<0>, X1>, mp_list<mp_size_t<1>, X2>, mp_list<mp_size_t<2>, X3>>;
using mp_map_from_list_actual = mp_map_from_list<mp_map_from_list_input>;
static_assert(std::is_same<mp_map_from_list_expected, mp_map_from_list_actual>::value, "mp_map_from_list");

/* mp_transform */
using mp_transform_input_0 = std::tuple<X1, X2, X3>;
using mp_transform_input_1 = std::tuple<Y1, Y2, Y3>;
using mp_transform_expected_0 = std::tuple<std::pair<X1, Y1>, std::pair<X2, Y2>, std::pair<X3, Y3>>;
using mp_transform_actual_0 = mp_transform<std::pair, mp_transform_input_0, mp_transform_input_1>;
static_assert(std::is_same<mp_transform_expected_0, mp_transform_actual_0>::value, "mp_transform");

using mp_transform_input_2 = mp_list<X1, X2, X3>;
using mp_transform_expected_1 = mp_list<X1*, X2*, X3*>;
using mp_transform_actual_1 = mp_transform<mp_add_pointer, mp_transform_input_2>;
static_assert(std::is_same<mp_transform_expected_1, mp_transform_actual_1>::value, "mp_transform");

/* unique id *************************************************************************************/

struct Counter1 { };
struct Counter2 { };

TEST(MetaProgrammingTest, unique_id)
{
auto uid1_1 = nextUniqueId<Counter1>();
auto uid1_2 = nextUniqueId<Counter1>();
ASSERT_EQ(uid1_1, 0);
ASSERT_EQ(uid1_2, 1);

auto uid2_1 = getUniqueId<Counter2, int>();
auto uid2_2 = getUniqueId<Counter2, int, float>();
auto uid2_3 = getUniqueId<Counter2, float>();
auto uid2_4 = getUniqueId<Counter2, int>();
ASSERT_EQ(uid2_1, 0);
ASSERT_EQ(uid2_2, 1);
ASSERT_EQ(uid2_3, 2);
ASSERT_EQ(uid2_4, 0);
}

TEST(MetaProgrammingTest, string)
{
constexpr auto string1 = utl::mp_make_string("fuu bar");
ASSERT_EQ ( string1.str(), std::string("fuu bar") );
}

TEST(MetaProgrammingTest, int_to_str)
{
using t_int16 = utl::mp_const<int, 16>;
constexpr auto decStr = utl::mp_int_to_string<t_int16, 10>();
constexpr auto hexStr = utl::mp_int_to_string<t_int16, 16>();
ASSERT_EQ ( decStr.str(), std::string("16") );
ASSERT_EQ ( hexStr.str(), std::string("10") );
}

+ 22
- 0
test/MiscTests.cpp View File

@@ -0,0 +1,22 @@
#include <gtest/gtest.h>

namespace utils_tests
{
#include "../src/cpputils/Misc.h"
}

using namespace ::testing;
using namespace ::utils_tests::utl;

TEST(UtilsTests, bitCount)
{
EXPECT_EQ(0, bitCount(0b0000'0000'0000'0000'0000'0000'0000'0000u));
EXPECT_EQ(1, bitCount(0b0000'0000'0000'0000'0000'0000'0000'1000u));
EXPECT_EQ(2, bitCount(0b0000'0000'0000'0000'0000'1000'0000'1000u));
EXPECT_EQ(3, bitCount(0b0000'0000'0010'0000'0000'1000'0000'1000u));
EXPECT_EQ(4, bitCount(0b0010'0000'0010'0000'0000'1000'0000'1000u));
EXPECT_EQ(5, bitCount(0b0010'0000'0010'0000'0000'1000'1000'1000u));
EXPECT_EQ(6, bitCount(0b0010'0000'0010'0000'0000'1000'1000'1001u));
EXPECT_EQ(7, bitCount(0b0010'0000'0010'0000'0000'1000'1000'1101u));
EXPECT_EQ(8, bitCount(0b0010'0100'0010'0000'0000'1000'1000'1101u));
}

+ 161
- 0
test/NullableTests.cpp View File

@@ -0,0 +1,161 @@
#include <memory>
#include <type_traits>
#include <gtest/gtest.h>

namespace nullable_tests
{
#include "../src/cpputils/Nullable.h"

struct TestData
{
static int ctorCount;
static int dtorCount;

TestData()
{ ++ctorCount; }

~TestData()
{ ++dtorCount; }
};

struct NonCopyableTestData
{
int value;

NonCopyableTestData(int v) :
value(v)
{ }

NonCopyableTestData(NonCopyableTestData&& other) :
value(0)
{ std::swap(value, other.value); }

NonCopyableTestData(const NonCopyableTestData&) = delete;
};

using NullableInt = utl::Nullable<int>;
using NullableIntRef = utl::Nullable<int&>;
using NullableString = utl::Nullable<std::string>;
using NullableTestData = utl::Nullable<TestData>;
using NullableNonCopyableTestData = utl::Nullable<NonCopyableTestData>;

int TestData::ctorCount = 0;
int TestData::dtorCount = 0;
}

using namespace nullable_tests;
using namespace nullable_tests::utl;

TEST(NullableTest, ctor_empty)
{
NullableInt n1;
NullableIntRef n2;
EXPECT_FALSE(static_cast<bool>(n1));
EXPECT_FALSE(static_cast<bool>(n2));
}

TEST(NullableTest, ctor_value)
{
int i = 5;
NullableInt n1(i);
NullableIntRef n2(i);
EXPECT_TRUE(static_cast<bool>(n1));
EXPECT_TRUE(static_cast<bool>(n2));
EXPECT_EQ (5, n1());
EXPECT_EQ (&i, &n2());
}

TEST(NullableTest, ctor_copy)
{
int i = 5;
NullableInt i1(i);
NullableIntRef i2(i);
NullableInt n1(i1);
NullableIntRef n2(i2);
EXPECT_TRUE(static_cast<bool>(n1));
EXPECT_TRUE(static_cast<bool>(n2));
EXPECT_EQ (5, n1());
EXPECT_EQ (&i, &n2());
}

TEST(NullableTest, ctor_move)
{
NullableString i1("test");
NullableString n1(std::move(i1));
EXPECT_FALSE(static_cast<bool>(i1));
ASSERT_TRUE (static_cast<bool>(n1));
EXPECT_EQ (std::string("test"), n1());
}

TEST(NullableTest, move_assignment)
{
NullableString i1("test");
NullableString n1;
n1 = std::move(i1);
EXPECT_FALSE(static_cast<bool>(i1));
ASSERT_TRUE (static_cast<bool>(n1));
EXPECT_EQ (std::string("test"), n1());
}

TEST(NullableTest, movable_object)
{
NonCopyableTestData data(5);
NullableNonCopyableTestData tmp;
tmp = std::move(data);
ASSERT_TRUE ( tmp.hasValue() );
ASSERT_EQ ( 5, tmp.value().value );
ASSERT_EQ ( 0, data.value );
}

TEST(NullableTest, hasValue_operatorBool)
{
EXPECT_FALSE(static_cast<bool>(NullableInt()));
EXPECT_FALSE(NullableInt().hasValue());
EXPECT_TRUE (static_cast<bool>(NullableInt(5)));
EXPECT_TRUE (NullableInt(5).hasValue());
}

TEST(NullableTest, reset)
{
NullableTestData n(TestData{});
EXPECT_TRUE (n.hasValue());
int tmp = TestData::dtorCount;
n.reset();
EXPECT_FALSE(n.hasValue());
EXPECT_EQ (tmp + 1, TestData::dtorCount);
}

TEST(NullableTest, value_functor)
{
NullableInt n1(5);
NullableInt n2;

EXPECT_EQ (5, n1());
EXPECT_EQ (5, n1.value());
EXPECT_ANY_THROW(n2());
EXPECT_ANY_THROW(n2.value());
}

TEST(NullableTest, equalityCompareOperator)
{
NullableInt n1(5);
NullableInt n2(7);
NullableInt n3(5);
NullableInt n4;

EXPECT_FALSE(n2 == n1);
EXPECT_TRUE (n3 == n1);
EXPECT_FALSE(n4 == n1);

EXPECT_FALSE(n1 == n2);
EXPECT_FALSE(n3 == n2);
EXPECT_FALSE(n4 == n2);

EXPECT_TRUE (n1 == n3);
EXPECT_FALSE(n2 == n3);
EXPECT_FALSE(n4 == n3);

EXPECT_FALSE(n1 == n4);
EXPECT_FALSE(n2 == n4);
EXPECT_FALSE(n3 == n4);
}

+ 87
- 0
test/StreamHelperTests.cpp View File

@@ -0,0 +1,87 @@
#include <typeinfo>
#include <sstream>
#include <gtest/gtest.h>

#include "../src/cpputils/StreamHelper.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
{ utl::StreamHelper::write(os, s); }

void deserialize(std::istream& is)
{ utl::StreamHelper::read(is, s); }
};
}

using namespace utl;
using namespace ::testing;
using namespace stream_helper_tests;

TEST(StreamHelperTests, write_scalar)
{
std::ostringstream os;
StreamHelper::write(os, static_cast<uint32_t>(0x12345678));
EXPECT_EQ(std::string("\x78\x56\x34\x12", 4), os.str());
}

TEST(StreamHelperTests, write_string)
{
std::ostringstream os;
StreamHelper::write(os, std::string("test"));
EXPECT_EQ(std::string("\x04\x00\x00\x00test", 8), os.str());
}

TEST(StreamHelperTests, write_simpleStruct)
{
std::ostringstream os;
StreamHelper::write(os, SimpleStruct { 0x78563412, 5 });
EXPECT_EQ(std::string("\x12\x34\x56\x78\x05", 5), os.str());
}

TEST(StreamHelperTests, write_managedStruct)
{
std::ostringstream os;
StreamHelper::write(os, ManagedStruct { "test" });
EXPECT_EQ(std::string("\x04\x00\x00\x00test", 8), os.str());
}

TEST(StreamHelperTests, read_scalar)
{
std::istringstream is(std::string("\x78\x56\x34\x12", 4));
auto ret = StreamHelper::read<uint32_t>(is);
EXPECT_EQ(0x12345678, ret);
}

TEST(StreamHelperTests, read_string)
{
std::istringstream is(std::string("\x04\x00\x00\x00test", 8));
auto ret = StreamHelper::read<std::string>(is);
EXPECT_EQ(std::string("test"), ret);
}

TEST(StreamHelperTests, read_simpleStruct)
{
std::istringstream is(std::string("\x12\x34\x56\x78\x05", 5));
auto ret = StreamHelper::read<SimpleStruct>(is);
EXPECT_EQ(0x78563412, ret.u32);
EXPECT_EQ(5, ret.u8);
}

TEST(StreamHelperTests, read_managedStruct)
{
std::istringstream is(std::string("\x04\x00\x00\x00test", 8));
auto ret = StreamHelper::read<ManagedStruct>(is);
EXPECT_EQ(std::string("test"), ret.s);
}

+ 97
- 0
test/StringHelperTests.cpp View File

@@ -0,0 +1,97 @@
#include <memory>
#include <type_traits>
#include <gtest/gtest.h>

namespace string_helper_tests
{
#include "../src/cpputils/StringHelper.h"

enum class TestEnum
{
Test0,
Test1,
Test2,
Test3
};

DefEnumToStringMap(
TestEnum,
{ TestEnum::Test0, "Test0" },
{ TestEnum::Test1, "Test1" },
{ TestEnum::Test2, "Test2" },
{ TestEnum::Test3, "Test3" }
);

DefStringToEnumMap(
TestEnum,
InvariantStringLess,
{ "test0", TestEnum::Test0 },
{ "test1", TestEnum::Test1 },
{ "test2", TestEnum::Test2 },
{ "test3", TestEnum::Test3 }
);

struct TestClass1
{
int i;

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

bool fromString(const std::string& s)
{ return static_cast<bool>(std::istringstream(s) >> i); }
};

struct TestClass2
{
int i;

void toString(std::ostream& os) const
{ os << i; }

static bool fromString(const std::string& s, TestClass2& v)
{ return static_cast<bool>(std::istringstream(s) >> v.i); }
};
}

using namespace ::testing;
using namespace ::string_helper_tests;
using namespace ::string_helper_tests::utl;

TEST(StringHelperTests, toString)
{
EXPECT_EQ(std::string("4.5"), utl::toString(4.5));
EXPECT_EQ(std::string("test"), utl::toString("test"));
EXPECT_EQ(std::string("fuu"), utl::toString(std::string("fuu")));
EXPECT_EQ(std::string("Test1(1)"), utl::toString(TestEnum::Test1));
EXPECT_EQ(std::string("5"), utl::toString(TestClass1 { 5 }));
EXPECT_EQ(std::string("6"), utl::toString(TestClass2 { 6 }));
}

TEST(StringHelperTests, fromString)
{
double d;
EXPECT_FALSE(utl::tryFromString("abc", d));
EXPECT_TRUE (utl::tryFromString("4.5", d));
EXPECT_TRUE (d >= 4.5 && d <= 4.5);

int i;
EXPECT_FALSE(utl::tryFromString("abc", i));
EXPECT_TRUE (utl::tryFromString("123", i));
EXPECT_EQ (123, i);

TestEnum e;
EXPECT_FALSE(utl::tryFromString("abc", e));
EXPECT_TRUE (utl::tryFromString("test1", e));
EXPECT_EQ (TestEnum::Test1, e);

TestClass1 t1;
EXPECT_FALSE(utl::tryFromString("abc", t1));
EXPECT_TRUE (utl::tryFromString("11", t1));
EXPECT_EQ (11, t1.i);

TestClass2 t2;
EXPECT_FALSE(utl::tryFromString("abc", t2));
EXPECT_TRUE (utl::tryFromString("12", t2));
EXPECT_EQ (12, t2.i);
}

+ 35
- 0
test/TimeTests.cpp View File

@@ -0,0 +1,35 @@
#include <chrono>
#include <gtest/gtest.h>

namespace time_tests
{
#include "../src/cpputils/Time.h"
}

using namespace time_tests::utl;

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

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

+ 121
- 0
test/TransformIteratorTests.cpp View File

@@ -0,0 +1,121 @@
#include <list>
#include <vector>
#include <gtest/gtest.h>

namespace transform_iterator_tests
{
#include "../src/cpputils/TransformIterator.h"
}

using namespace transform_iterator_tests;
using namespace transform_iterator_tests::utl;

struct op_add_10
{
inline int operator()(int& i) const
{ return i + 10; }

inline bool operator==(const op_add_10& other) const
{ return true; }

inline bool operator!=(const op_add_10& other) const
{ return false; }
};

TEST(TransformIteratorTests, bidirectional_inc)
{
std::list<int> l { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
auto it = makeTransformIterator(l.begin(), op_add_10());
auto itEnd = makeTransformIterator(l.end(), op_add_10());
ASSERT_NE(itEnd, it);
EXPECT_EQ(11, *it++);
ASSERT_NE(itEnd, it);
EXPECT_EQ(12, *it++);
ASSERT_NE(itEnd, it);
EXPECT_EQ(13, *it++);
ASSERT_NE(itEnd, it);
EXPECT_EQ(14, *it++);
ASSERT_NE(itEnd, it);
EXPECT_EQ(15, *it++);
ASSERT_NE(itEnd, it);
EXPECT_EQ(16, *it++);
ASSERT_NE(itEnd, it);
EXPECT_EQ(17, *it++);
ASSERT_NE(itEnd, it);
EXPECT_EQ(18, *it++);
ASSERT_NE(itEnd, it);
EXPECT_EQ(19, *it++);
ASSERT_EQ(itEnd, it);
}

TEST(TransformIteratorTests, bidirectional_dec)
{
std::list<int> l { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
auto itBeg = makeTransformIterator(l.begin(), op_add_10());
auto it = makeTransformIterator(l.end(), op_add_10());
--it;
ASSERT_NE(itBeg, it);
EXPECT_EQ(19, *it--);
ASSERT_NE(itBeg, it);
EXPECT_EQ(18, *it--);
ASSERT_NE(itBeg, it);
EXPECT_EQ(17, *it--);
ASSERT_NE(itBeg, it);
EXPECT_EQ(16, *it--);
ASSERT_NE(itBeg, it);
EXPECT_EQ(15, *it--);
ASSERT_NE(itBeg, it);
EXPECT_EQ(14, *it--);
ASSERT_NE(itBeg, it);
EXPECT_EQ(13, *it--);
ASSERT_NE(itBeg, it);
EXPECT_EQ(12, *it--);
ASSERT_EQ(itBeg, it);
EXPECT_EQ(11, *it--);
}

TEST(TransformIteratorTests, random_access)
{
std::vector<int> l { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
auto it = makeTransformIterator(l.begin(), op_add_10());
auto itEnd = makeTransformIterator(l.end(), op_add_10());
ASSERT_NE(itEnd, it);
EXPECT_EQ(9, itEnd - it);
EXPECT_EQ(11, it[0]);
EXPECT_EQ(12, it[1]);
EXPECT_EQ(13, it[2]);
EXPECT_EQ(14, it[3]);
EXPECT_EQ(15, it[4]);
EXPECT_EQ(16, it[5]);
EXPECT_EQ(17, it[6]);
EXPECT_EQ(18, it[7]);
EXPECT_EQ(19, it[8]);
}

TEST(TransformIteratorTests, lambda)
{
std::vector<int> l { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
auto lambda = [](int& i) { return &i; };

auto it = makeTransformIterator(l.begin(), lambda);
auto itEnd = makeTransformIterator(l.end(), lambda);
ASSERT_NE(itEnd, it);
EXPECT_EQ(&l.at(0), *it++);
ASSERT_NE(itEnd, it);
EXPECT_EQ(&l.at(1), *it++);
ASSERT_NE(itEnd, it);
EXPECT_EQ(&l.at(2), *it++);
ASSERT_NE(itEnd, it);
EXPECT_EQ(&l.at(3), *it++);
ASSERT_NE(itEnd, it);
EXPECT_EQ(&l.at(4), *it++);
ASSERT_NE(itEnd, it);
EXPECT_EQ(&l.at(5), *it++);
ASSERT_NE(itEnd, it);
EXPECT_EQ(&l.at(6), *it++);
ASSERT_NE(itEnd, it);
EXPECT_EQ(&l.at(7), *it++);
ASSERT_NE(itEnd, it);
EXPECT_EQ(&l.at(8), *it++);
ASSERT_EQ(itEnd, it);
}

Loading…
Cancel
Save