@@ -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 ) |
@@ -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 ( ) |
@@ -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(); } | |||
} |
@@ -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(); | |||
} | |||
}; | |||
} |
@@ -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) | |||
{ } | |||
}; | |||
} |
@@ -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>>; | |||
} |
@@ -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(); | |||
} |
@@ -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); | |||
} |
@@ -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...); | |||
}; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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)) | |||
{ } | |||
}; | |||
} |
@@ -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...)) | |||
{ } | |||
}; | |||
} |
@@ -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); | |||
} | |||
}; | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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); } | |||
} |
@@ -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; } | |||
} |
@@ -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 ) |
@@ -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); | |||
} |
@@ -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)); | |||
} |
@@ -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()); | |||
} |
@@ -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"; | |||
} | |||
@@ -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") ); | |||
} |
@@ -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)); | |||
} |
@@ -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); | |||
} |
@@ -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); | |||
} |
@@ -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); | |||
} |
@@ -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()); | |||
} |
@@ -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); | |||
} |