@@ -1,8 +1,9 @@ | |||
# Project: cpputils ############################################################################### | |||
Project ( cpputils ) | |||
File ( GLOB_RECURSE SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ) | |||
File ( GLOB_RECURSE SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/impl/*.cpp ) | |||
Add_Library ( cpputils STATIC ${SOURCE_FILES} ) | |||
Include_Directories ( ${CMAKE_CURRENT_SOURCE_DIR} ) | |||
Target_Include_Directories ( | |||
cpputils | |||
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} | |||
@@ -1,257 +0,0 @@ | |||
#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(); } | |||
} |
@@ -1,167 +0,0 @@ | |||
#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) | |||
{ } | |||
}; | |||
} |
@@ -1,187 +0,0 @@ | |||
#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(); | |||
} |
@@ -1,189 +0,0 @@ | |||
#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); | |||
} |
@@ -1,237 +0,0 @@ | |||
#pragma once | |||
#include <cxxabi.h> | |||
#include <iostream> | |||
#if defined(__linux__) | |||
# include <endian.h> | |||
#elif defined(__FreeBSD__) || defined(__NetBSD__) | |||
# include <sys/endian.h> | |||
#elif defined(__OpenBSD__) | |||
# include <sys/types.h> | |||
# define be16toh(x) betoh16(x) | |||
# define be32toh(x) betoh32(x) | |||
# define be64toh(x) betoh64(x) | |||
#endif | |||
namespace 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()); | |||
} | |||
}; | |||
/* simple class that stores a value of type T */ | |||
template<class T> | |||
struct wrapper | |||
{ | |||
using value_type = T; | |||
value_type value; | |||
inline value_type& operator*() | |||
{ return value; } | |||
inline const value_type& operator*() const | |||
{ return value; } | |||
inline wrapper& operator=(const value_type v) | |||
{ | |||
value = v; | |||
return *this; | |||
} | |||
inline wrapper& operator=(const wrapper& other) | |||
{ | |||
value = other.value; | |||
return *this; | |||
} | |||
inline wrapper& operator=(wrapper&& other) | |||
{ | |||
value = std::move(other).value; | |||
return *this; | |||
} | |||
inline wrapper() : | |||
value(value_type()) | |||
{ } | |||
inline wrapper(const value_type& v) : | |||
value(v) | |||
{ } | |||
inline wrapper(const wrapper& other) : | |||
value(other.value) | |||
{ } | |||
inline wrapper(wrapper&& other) : | |||
value(std::move(other.value)) | |||
{ } | |||
}; | |||
template<class T> | |||
struct wrapper<T&> | |||
{ | |||
using value_type = T&; | |||
using storage_type = T*; | |||
storage_type value; | |||
inline value_type operator*() const | |||
{ | |||
assert(value != nullptr); | |||
return *value; | |||
} | |||
inline wrapper& operator=(const value_type v) | |||
{ | |||
value = &v; | |||
return *this; | |||
} | |||
inline wrapper& operator=(const wrapper& other) | |||
{ | |||
value = other.value; | |||
return *this; | |||
} | |||
inline wrapper& operator=(wrapper&& other) | |||
{ | |||
value = std::move(other.value); | |||
return *this; | |||
} | |||
inline wrapper() : | |||
value(nullptr) | |||
{ } | |||
inline wrapper(value_type v) : | |||
value(&v) | |||
{ } | |||
inline wrapper(const wrapper& other) : | |||
value(other.value) | |||
{ } | |||
inline wrapper(wrapper&& other) : | |||
value(std::move(other.value)) | |||
{ } | |||
}; | |||
/* 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> | |||
inline bool tryCast(T* t, S*& s) | |||
{ | |||
s = dynamic_cast<S*>(t); | |||
return static_cast<bool>(s); | |||
} | |||
namespace __impl | |||
{ | |||
template<class T, size_t N = sizeof(T)> | |||
struct network_convert_helper; | |||
} | |||
template<class T> | |||
inline T hton(const T& value) | |||
{ return __impl::network_convert_helper<T>::hton(value); } | |||
template<class T> | |||
inline T ntoh(const T& value) | |||
{ return __impl::network_convert_helper<T>::ntoh(value); } | |||
/* 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; | |||
} | |||
/* implementation ****************************************************************************/ | |||
namespace __impl | |||
{ | |||
template<class T> | |||
struct network_convert_helper<T, 1> | |||
{ | |||
static inline T hton(const T& t) | |||
{ return t; } | |||
static inline T ntoh(const T& t) | |||
{ return t; } | |||
}; | |||
template<class T> | |||
struct network_convert_helper<T, 2> | |||
{ | |||
static inline T hton(const T& t) | |||
{ return reinterpret_cast<T>(htobe16(reinterpret_cast<uint16_t>(t))); } | |||
static inline T ntoh(const T& t) | |||
{ return reinterpret_cast<T>(be16toh(reinterpret_cast<uint16_t>(t))); } | |||
}; | |||
template<class T> | |||
struct network_convert_helper<T, 4> | |||
{ | |||
static inline T hton(const T& t) | |||
{ return reinterpret_cast<T>(htobe32(reinterpret_cast<uint32_t>(t))); } | |||
static inline T ntoh(const T& t) | |||
{ return reinterpret_cast<T>(be32toh(reinterpret_cast<uint32_t>(t))); } | |||
}; | |||
template<class T> | |||
struct network_convert_helper<T, 8> | |||
{ | |||
static inline T hton(const T& t) | |||
{ return reinterpret_cast<T>(htobe64(reinterpret_cast<uint64_t>(t))); } | |||
static inline T ntoh(const T& t) | |||
{ return reinterpret_cast<T>(be64toh(reinterpret_cast<uint64_t>(t))); } | |||
}; | |||
} | |||
} |
@@ -0,0 +1,5 @@ | |||
#pragma once | |||
#include <cpputils/container/handle_manager.h> | |||
#include <cpputils/container/nullable.h> | |||
#include <cpputils/container/smart_ptr.h> |
@@ -2,13 +2,15 @@ | |||
#include <memory> | |||
#include <type_traits> | |||
#include "Exception.h" | |||
#include <cpputils/misc/string.h> | |||
#include <cpputils/misc/exception.h> | |||
namespace utl | |||
{ | |||
template<class T> | |||
struct Nullable | |||
struct nullable | |||
{ | |||
public: | |||
using value_type = T; | |||
@@ -16,23 +18,23 @@ namespace utl | |||
using pointer_type = clean_type*; | |||
private: | |||
struct Container | |||
struct container | |||
{ | |||
value_type value; | |||
template<class... Args> | |||
Container(Args&&... args) : | |||
container(Args&&... args) : | |||
value(std::forward<Args>(args)...) | |||
{ } | |||
}; | |||
private: | |||
std::unique_ptr<Container> _container; | |||
std::unique_ptr<container> _container; | |||
inline void check() const | |||
{ | |||
if (!_container) | |||
throw EInvalidOperation("nullable does not have a value"); | |||
throw invalid_operation_exception("nullable does not have a value"); | |||
} | |||
public: | |||
@@ -42,7 +44,7 @@ namespace utl | |||
inline const value_type& value() const | |||
{ check(); return _container->value; } | |||
inline bool hasValue() const | |||
inline bool has_value() const | |||
{ return static_cast<bool>(_container); } | |||
inline void reset() | |||
@@ -69,75 +71,90 @@ namespace utl | |||
{ return value(); } | |||
inline explicit operator bool() const | |||
{ return hasValue(); } | |||
{ return has_value(); } | |||
inline bool operator==(T v) const | |||
{ return hasValue() && value() == v; } | |||
{ return has_value() && value() == v; } | |||
inline bool operator==(const Nullable& other) const | |||
{ return hasValue() && other.hasValue() && value() == other.value(); } | |||
inline bool operator==(const nullable& other) const | |||
{ return has_value() && other.has_value() && value() == other.value(); } | |||
inline bool operator<(const Nullable& other) const | |||
inline bool operator<(const nullable& other) const | |||
{ | |||
return this->hasValue() | |||
&& other.hasValue() | |||
return this->has_value() | |||
&& other.has_value() | |||
&& value() < other.value(); | |||
} | |||
inline bool operator>(const Nullable& other) const | |||
inline bool operator>(const nullable& other) const | |||
{ | |||
return this->hasValue() | |||
&& other.hasValue() | |||
return this->has_value() | |||
&& other.has_value() | |||
&& value() < other.value(); | |||
} | |||
inline bool operator<=(const Nullable& other) const | |||
inline bool operator<=(const nullable& other) const | |||
{ | |||
return this->hasValue() | |||
&& other.hasValue() | |||
return this->has_value() | |||
&& other.has_value() | |||
&& value() < other.value(); | |||
} | |||
inline bool operator>=(const Nullable& other) const | |||
inline bool operator>=(const nullable& other) const | |||
{ | |||
return this->hasValue() | |||
&& other.hasValue() | |||
return this->has_value() | |||
&& other.has_value() | |||
&& value() < other.value(); | |||
} | |||
inline void operator=(value_type&& t) | |||
{ _container.reset(new Container(std::forward<value_type>(t))); } | |||
{ _container.reset(new container(std::forward<value_type>(t))); } | |||
inline void operator=(const Nullable& other) | |||
inline void operator=(const nullable& other) | |||
{ | |||
if (other.hasValue()) | |||
if (other.has_value()) | |||
*this = other.value(); | |||
else | |||
reset(); | |||
} | |||
inline void operator=(Nullable&& other) | |||
inline void operator=(nullable&& other) | |||
{ _container = std::move(other._container); } | |||
Nullable() | |||
nullable() | |||
{ } | |||
template<class... Args> | |||
Nullable(Args&&... args) : | |||
_container(new Container(std::forward<Args>(args)...)) | |||
nullable(Args&&... args) : | |||
_container(new container(std::forward<Args>(args)...)) | |||
{ } | |||
Nullable(Nullable& other) | |||
nullable(nullable& other) | |||
{ if (static_cast<bool>(other)) *this = other(); } | |||
Nullable(const Nullable& other) | |||
nullable(const nullable& other) | |||
{ if (static_cast<bool>(other)) *this = other(); } | |||
Nullable(Nullable&& other) : | |||
nullable(nullable&& other) : | |||
_container(std::move(other._container)) | |||
{ } | |||
}; | |||
namespace __impl | |||
{ | |||
template<class T> | |||
struct op_to_string<nullable<T>, void> | |||
{ | |||
inline void operator()(std::ostream& os, const nullable<T>& s) const | |||
{ | |||
if (s.has_value()) | |||
op_to_string<T>()(os, s.value()); | |||
else | |||
os << "null"; | |||
} | |||
}; | |||
} | |||
} |
@@ -2,13 +2,13 @@ | |||
#include <memory> | |||
#include "MetaProgramming.h" | |||
#include "Exception.h" | |||
#include "exception.h" | |||
namespace utl | |||
{ | |||
template<class T> | |||
struct SmartPtr | |||
struct smart_ptr | |||
{ | |||
private: | |||
template<class Tt_base, class Tt_derived> | |||
@@ -23,7 +23,7 @@ namespace utl | |||
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>>>> : | |||
struct __impl_cop_is_value<F<X>, utl::mp_enable_if<cop_is_base_of<smart_ptr<X>, F<X>>>> : | |||
public utl::mp_false { }; | |||
template<class X> | |||
@@ -40,20 +40,20 @@ namespace utl | |||
std::shared_ptr<T> _value; | |||
public: | |||
inline bool hasValue() const | |||
inline bool has_value() const | |||
{ return static_cast<bool>(_value); } | |||
inline T& value() | |||
{ | |||
if (!_value) | |||
throw utl::EInvalidOperation("SmartPtr does not have a value"); | |||
throw utl::invalid_operation_exception("smart_ptr does not have a value"); | |||
return *_value; | |||
} | |||
inline const T& value() const | |||
{ | |||
if (!_value) | |||
throw utl::EInvalidOperation("SmartPtr does not have a value"); | |||
throw utl::invalid_operation_exception("smart_ptr does not have a value"); | |||
return *_value; | |||
} | |||
@@ -70,95 +70,95 @@ namespace utl | |||
{ return &value(); } | |||
inline operator bool() const | |||
{ return hasValue(); } | |||
{ return has_value(); } | |||
inline SmartPtr& reset() | |||
inline smart_ptr& 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) | |||
inline smart_ptr& 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) | |||
inline smart_ptr& 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) | |||
inline smart_ptr& reset(const smart_ptr<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) | |||
inline smart_ptr& reset(smart_ptr<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) | |||
inline smart_ptr& reset(std::reference_wrapper<X>&& ref) | |||
{ | |||
_value.reset(&ref.get(), op_deleter_noop()); | |||
return *this; | |||
} | |||
template<class... Args> | |||
inline SmartPtr& reset(Args&&... args) | |||
inline smart_ptr& reset(Args&&... args) | |||
{ | |||
_value.reset(new T(std::forward<Args>(args)...)); | |||
return *this; | |||
} | |||
template<class X = T> | |||
inline SmartPtr& operator=(X&& x) | |||
inline smart_ptr& operator=(X&& x) | |||
{ return reset(std::forward<X>(x)); } | |||
SmartPtr() | |||
smart_ptr() | |||
{ } | |||
template<class X = T, utl::mp_enable_if_c<cop_is_value<X>::value, int> = 0> | |||
SmartPtr(X& x) : | |||
smart_ptr(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) : | |||
smart_ptr(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) : | |||
smart_ptr(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) : | |||
smart_ptr(const smart_ptr<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) : | |||
smart_ptr(smart_ptr<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) : | |||
smart_ptr(std::reference_wrapper<X>&& ref) : | |||
_value(&ref.get(), op_deleter_noop()) | |||
{ } | |||
template<class... Args> | |||
SmartPtr(Args... args) : | |||
smart_ptr(Args... args) : | |||
_value(new T(args...)) | |||
{ } | |||
}; |
@@ -0,0 +1,104 @@ | |||
#pragma once | |||
namespace utl | |||
{ | |||
/* simple class that stores a value of type T */ | |||
template<class T> | |||
struct wrapper | |||
{ | |||
using value_type = T; | |||
value_type value; | |||
inline value_type& operator*() | |||
{ return value; } | |||
inline const value_type& operator*() const | |||
{ return value; } | |||
inline wrapper& operator=(value_type v) | |||
{ | |||
value = v; | |||
return *this; | |||
} | |||
inline wrapper& operator=(const wrapper& other) | |||
{ | |||
value = other.value; | |||
return *this; | |||
} | |||
inline wrapper& operator=(wrapper&& other) | |||
{ | |||
value = std::move(other).value; | |||
return *this; | |||
} | |||
inline wrapper() : | |||
value(value_type()) | |||
{ } | |||
inline wrapper(const value_type& v) : | |||
value(v) | |||
{ } | |||
inline wrapper(const wrapper& other) : | |||
value(other.value) | |||
{ } | |||
inline wrapper(wrapper&& other) : | |||
value(std::move(other.value)) | |||
{ } | |||
}; | |||
template<class T> | |||
struct wrapper<T&> | |||
{ | |||
using value_type = T&; | |||
using storage_type = T*; | |||
storage_type value; | |||
inline value_type operator*() const | |||
{ | |||
assert(value != nullptr); | |||
return *value; | |||
} | |||
inline wrapper& operator=(value_type v) | |||
{ | |||
value = &v; | |||
return *this; | |||
} | |||
inline wrapper& operator=(const wrapper& other) | |||
{ | |||
value = other.value; | |||
return *this; | |||
} | |||
inline wrapper& operator=(wrapper&& other) | |||
{ | |||
value = std::move(other.value); | |||
return *this; | |||
} | |||
inline wrapper() : | |||
value(nullptr) | |||
{ } | |||
inline wrapper(value_type v) : | |||
value(&v) | |||
{ } | |||
inline wrapper(const wrapper& other) : | |||
value(other.value) | |||
{ } | |||
inline wrapper(wrapper&& other) : | |||
value(std::move(other.value)) | |||
{ } | |||
}; | |||
} |
@@ -0,0 +1,4 @@ | |||
#pragma once | |||
#include <cpputils/logging/consumer/consumer.h> | |||
#include <cpputils/logging/consumer/consumer_stream.h> |
@@ -0,0 +1,24 @@ | |||
#pragma once | |||
#include <cpputils/logging/types.h> | |||
namespace utl { | |||
namespace logging { | |||
struct consumer | |||
{ | |||
private: | |||
std::string _name; | |||
public: | |||
virtual void log(data_ptr_s data) = 0; | |||
inline const std::string& name() const | |||
{ return _name; } | |||
consumer(const std::string& n, bool autoRegister); | |||
~consumer(); | |||
}; | |||
} | |||
} |
@@ -0,0 +1,25 @@ | |||
#pragma once | |||
#include <mutex> | |||
#include <cpputils/logging/consumer/consumer.h> | |||
namespace utl { | |||
namespace logging { | |||
struct consumer_stream : public consumer | |||
{ | |||
private: | |||
mutable std::mutex _mutex; | |||
std::ostream* _stream; | |||
bool _ownsStream; | |||
public: | |||
void log(data_ptr_s data) override; | |||
consumer_stream(const std::string& name, std::ostream& stream, bool ownsStream, bool autoRegister); | |||
virtual ~consumer_stream(); | |||
}; | |||
} | |||
} |
@@ -0,0 +1,42 @@ | |||
#pragma once | |||
#include <cpputils/logging/types.h> | |||
#include <cpputils/logging/logger.h> | |||
// () mandatory | |||
// [] optional | |||
// (LogLevel: debug|info|warn|error), [T_sender], [Message, [Arguments]] | |||
#define log_global_message(level, ...) \ | |||
if (::utl::logging::is_enabled(::utl::logging::log_level::level)) \ | |||
::utl::logging::make_log_helper(::utl::logging::log_level::level, __FILE__, __LINE__, ## __VA_ARGS__ ) = ::utl::logging::log_message() | |||
namespace utl { | |||
namespace logging { | |||
inline bool is_enabled(log_level level) | |||
{ return get_logger().is_enabled(level); } | |||
template <class T_sender> | |||
inline logger::helper make_log_helper(log_level level, const char* file, int line, T_sender* sender, std::string message) | |||
{ return get_logger().make_log_helper<T_sender>(level, file, line, sender, message); } | |||
template <class T_sender> | |||
inline logger::helper make_log_helper(log_level level, const char* file, int line, T_sender* sender) | |||
{ return get_logger().make_log_helper<T_sender>(level, file, line, sender, std::string()); } | |||
inline logger::helper make_log_helper(log_level level, const char* file, int line, std::string message) | |||
{ return get_logger().make_log_helper<void>(level, file, line, nullptr, message); } | |||
inline logger::helper make_log_helper(log_level level, const char* file, int line) | |||
{ return get_logger().make_log_helper<void>(level, file, line, nullptr, std::string()); } | |||
template <class T_sender, class... Args> | |||
inline logger::helper make_log_helper(log_level level, const char* file, int line, T_sender* sender, std::string message, Args... args) | |||
{ return get_logger().make_log_helper<T_sender, Args...>(level, file, line, sender, message, args...); } | |||
template <class... Args> | |||
inline logger::helper make_log_helper(log_level level, const char* file, int line, std::string message, Args... args) | |||
{ return get_logger().make_log_helper<void, Args...>(level, file, line, nullptr, message, args...); } | |||
} | |||
} |
@@ -0,0 +1,42 @@ | |||
#pragma once | |||
namespace utl { | |||
namespace logging { | |||
struct log_message | |||
{ | |||
private: | |||
std::ostringstream _msg; | |||
public: | |||
inline std::string str() const | |||
{ return _msg.str(); } | |||
template <typename T> | |||
inline log_message& operator <<(const T& value) | |||
{ | |||
_msg << value; | |||
return *this; | |||
} | |||
inline log_message& operator <<(std::ostream& (*callback)(std::ostream&)) | |||
{ | |||
callback(_msg); | |||
return *this; | |||
} | |||
log_message() : | |||
_msg() | |||
{ } | |||
log_message(const std::string& msg) : | |||
_msg(msg) | |||
{ } | |||
private: | |||
log_message(log_message&&) = delete; | |||
log_message(const log_message&) = delete; | |||
}; | |||
} | |||
} |
@@ -0,0 +1,98 @@ | |||
#pragma once | |||
#include <cpputils/misc/exception.h> | |||
#include <cpputils/logging/types.h> | |||
#include <cpputils/logging/log_message.h> | |||
// () mandatory | |||
// [] optional | |||
// (logger), (LogLevel: debug|info|warn|error), [Sender], [Message, [Arguments]] | |||
#define log_message(logger, level, ...) \ | |||
if (logger.is_enabled(::utl::logging::log_level::level)) \ | |||
logger.make_log_helper(::utl::logging::log_level::level, __FILE__, __LINE__, ## __VA_ARGS__ ) = ::utl::logging::log_message() | |||
namespace utl { | |||
namespace logging { | |||
struct logger | |||
{ | |||
public: | |||
struct helper | |||
{ | |||
private: | |||
logger& _logger; | |||
data_ptr_s _data; | |||
public: | |||
inline void operator=(const log_message& msg) | |||
{ _data->message += msg.str(); } | |||
inline helper(logger& logger, data_ptr_s data) : | |||
_logger (logger), | |||
_data (data) | |||
{ } | |||
inline ~helper() | |||
{ _logger.log(_data); } | |||
public: | |||
static inline helper create(logger& logger, log_level level, const char* file, int line, void* sender, const std::string& name, std::string message) | |||
{ | |||
using namespace ::utl::logging; | |||
data_ptr_s ret(new data()); | |||
ret->level = level; | |||
ret->time = std::chrono::steady_clock::now(); | |||
ret->thread = std::this_thread::get_id(); | |||
ret->file = file; | |||
ret->line = line; | |||
ret->sender = sender; | |||
ret->name = name; | |||
ret->message = message; | |||
return helper(logger, ret); | |||
} | |||
}; | |||
private: | |||
logger(logger&&) = delete; | |||
logger(const logger&) = delete; | |||
public: | |||
logger() { } | |||
virtual const std::string& name () const; | |||
virtual bool is_enabled (log_level level) const; | |||
virtual void log (data_ptr_s data) const; | |||
template<class Sender, class... Args> | |||
inline logger::helper make_log_helper(log_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::error_exception(errno); | |||
return helper::create(*this, level, file, line, static_cast<void*>(sender), name(), std::string(buff.get(), len)); | |||
} | |||
template<class... Args> | |||
inline logger::helper make_log_helper(log_level level, const char* file, int line, std::string message, Args... args) | |||
{ return make_log_helper<void, Args...>(level, file, line, nullptr, message, args...); } | |||
template<class Sender> | |||
inline logger::helper make_log_helper(log_level level, const char* file, int line, Sender* sender, std::string message) | |||
{ return helper::create(*this, level, file, line, static_cast<void*>(sender), name(), message); } | |||
template<class Sender> | |||
inline logger::helper make_log_helper(log_level level, const char* file, int line, Sender* sender) | |||
{ return make_log_helper<void>(level, file, line, static_cast<void*>(sender), std::string()); } | |||
inline logger::helper make_log_helper(log_level level, const char* file, int line, std::string message) | |||
{ return make_log_helper<void>(level, file, line, nullptr, message); } | |||
inline logger::helper make_log_helper(log_level level, const char* file, int line) | |||
{ return make_log_helper<void>(level, file, line, nullptr, std::string()); } | |||
}; | |||
logger& get_logger(const std::string& name = ""); | |||
} | |||
} |
@@ -0,0 +1,51 @@ | |||
#pragma once | |||
#include <set> | |||
#include <mutex> | |||
#include <cpputils/logging/rule.h> | |||
#include <cpputils/logging/logger.h> | |||
namespace utl { | |||
namespace logging { | |||
struct logger_impl : public logger | |||
{ | |||
private: | |||
mutable std::mutex _mutex; | |||
std::string _name; | |||
std::set<rule*> _rules; | |||
public: | |||
const std::string& name () const override; | |||
bool is_enabled (log_level level) const override; | |||
void log (data_ptr_s 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); | |||
} | |||
logger_impl(const std::string& n) : | |||
_name(n) | |||
{ } | |||
}; | |||
using logger_impl_ptr_u = std::unique_ptr<logger_impl>; | |||
void register_consumer (consumer& consumer); | |||
void unregister_consumer(consumer& consumer); | |||
rule_handle define_rule (matcher_ptr_u logger_matcher, matcher_ptr_u consumer_matcher, log_level min_level = log_level::debug, log_level max_level = log_level::error); | |||
void undefine_rule (rule_handle handle); | |||
void reset_logging(); | |||
} } |
@@ -0,0 +1,6 @@ | |||
#pragma once | |||
#include <cpputils/logging/matcher/matcher.h> | |||
#include <cpputils/logging/matcher/matcher_all.h> | |||
#include <cpputils/logging/matcher/matcher_default.h> | |||
#include <cpputils/logging/matcher/matcher_regex.h> |
@@ -0,0 +1,20 @@ | |||
#pragma once | |||
#include <memory> | |||
#include <cpputils/logging/logger.h> | |||
#include <cpputils/logging/consumer/consumer.h> | |||
namespace utl { | |||
namespace logging { | |||
struct matcher | |||
{ | |||
virtual bool match(const logger& logger) const; | |||
virtual bool match(const consumer& consumer) const; | |||
}; | |||
using matcher_ptr_u = std::unique_ptr<matcher>; | |||
} | |||
} |
@@ -0,0 +1,15 @@ | |||
#pragma once | |||
#include <cpputils/logging/matcher/matcher.h> | |||
namespace utl { | |||
namespace logging { | |||
struct matcher_all : public matcher | |||
{ | |||
bool match(const logger& logger) const override; | |||
bool match(const consumer& consumer) const override; | |||
}; | |||
} | |||
} |
@@ -0,0 +1,20 @@ | |||
#pragma once | |||
#include <cpputils/logging/matcher/matcher.h> | |||
namespace utl { | |||
namespace logging { | |||
struct matcher_default : public matcher | |||
{ | |||
private: | |||
logger& _defaultLogger; | |||
public: | |||
using matcher::match; | |||
bool match(const logger& logger) const override; | |||
matcher_default(); | |||
}; | |||
} | |||
} |
@@ -0,0 +1,23 @@ | |||
#pragma once | |||
#include <regex> | |||
#include <cpputils/logging/matcher/matcher.h> | |||
namespace utl { | |||
namespace logging { | |||
struct matcher_regex : public matcher | |||
{ | |||
private: | |||
std::regex _regex; | |||
bool _invert; | |||
public: | |||
bool match(const logger& logger) const override; | |||
bool match(const consumer& consumer) const override; | |||
matcher_regex(const std::string expression, bool invert = false); | |||
}; | |||
} | |||
} |
@@ -0,0 +1,63 @@ | |||
#pragma once | |||
#include <set> | |||
#include <mutex> | |||
#include <cpputils/logging/matcher/matcher.h> | |||
#include <cpputils/logging/consumer/consumer.h> | |||
namespace utl { | |||
namespace logging { | |||
struct rule | |||
{ | |||
private: | |||
mutable std::mutex _mutex; | |||
std::set<consumer*> _consumer; | |||
public: | |||
matcher_ptr_u logger_matcher; | |||
matcher_ptr_u consumer_matcher; | |||
log_level min_level; | |||
log_level max_level; | |||
inline bool is_enabled(log_level level) const | |||
{ | |||
return min_level <= level | |||
&& max_level >= level; | |||
} | |||
inline void register_consumer(consumer& consumer) | |||
{ | |||
std::lock_guard<std::mutex> lk(_mutex); | |||
_consumer.insert(&consumer); | |||
} | |||
inline void unregister_consumer(consumer& consumer) | |||
{ | |||
std::lock_guard<std::mutex> lk(_mutex); | |||
_consumer.erase(&consumer); | |||
} | |||
inline void log(data_ptr_s data) | |||
{ | |||
std::lock_guard<std::mutex> lk(_mutex); | |||
if (is_enabled(data->level)) | |||
{ | |||
for (auto& c : _consumer) | |||
c->log(data); | |||
} | |||
} | |||
rule(matcher_ptr_u lm, matcher_ptr_u cm, log_level min, log_level max) : | |||
logger_matcher (std::move(lm)), | |||
consumer_matcher(std::move(cm)), | |||
min_level (min), | |||
max_level (max) | |||
{ } | |||
}; | |||
using rule_handle = void*; | |||
} | |||
} |
@@ -0,0 +1,31 @@ | |||
#pragma once | |||
#include <chrono> | |||
#include <thread> | |||
namespace utl { | |||
namespace logging { | |||
enum class log_level | |||
{ | |||
debug = 0, | |||
info, | |||
warn, | |||
error, | |||
}; | |||
struct data | |||
{ | |||
log_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 data_ptr_s = std::shared_ptr<data>; | |||
} | |||
} |
@@ -0,0 +1,14 @@ | |||
#pragma once | |||
#include <cpputils/misc/convert.h> | |||
#include <cpputils/misc/enum.h> | |||
#include <cpputils/misc/exception.h> | |||
#include <cpputils/misc/flags.h> | |||
#include <cpputils/misc/indent.h> | |||
#include <cpputils/misc/misc.h> | |||
#include <cpputils/misc/stream.h> | |||
#include <cpputils/misc/string.h> | |||
#include <cpputils/misc/time.h> | |||
#include <cpputils/misc/transform_iterator.h> | |||
#include <cpputils/misc/type_helper.h> | |||
#include <cpputils/misc/wrapper.h> |
@@ -0,0 +1,63 @@ | |||
#pragma once | |||
namespace utl | |||
{ | |||
namespace __impl | |||
{ | |||
template<class T, size_t N = sizeof(T)> | |||
struct network_convert_helper; | |||
} | |||
template<class T> | |||
inline T hton(const T& value) | |||
{ return __impl::network_convert_helper<T>::hton(value); } | |||
template<class T> | |||
inline T ntoh(const T& value) | |||
{ return __impl::network_convert_helper<T>::ntoh(value); } | |||
namespace __impl | |||
{ | |||
template<class T> | |||
struct network_convert_helper<T, 1> | |||
{ | |||
static inline T hton(const T& t) | |||
{ return t; } | |||
static inline T ntoh(const T& t) | |||
{ return t; } | |||
}; | |||
template<class T> | |||
struct network_convert_helper<T, 2> | |||
{ | |||
static inline T hton(const T& t) | |||
{ return reinterpret_cast<T>(htobe16(reinterpret_cast<uint16_t>(t))); } | |||
static inline T ntoh(const T& t) | |||
{ return reinterpret_cast<T>(be16toh(reinterpret_cast<uint16_t>(t))); } | |||
}; | |||
template<class T> | |||
struct network_convert_helper<T, 4> | |||
{ | |||
static inline T hton(const T& t) | |||
{ return reinterpret_cast<T>(htobe32(reinterpret_cast<uint32_t>(t))); } | |||
static inline T ntoh(const T& t) | |||
{ return reinterpret_cast<T>(be32toh(reinterpret_cast<uint32_t>(t))); } | |||
}; | |||
template<class T> | |||
struct network_convert_helper<T, 8> | |||
{ | |||
static inline T hton(const T& t) | |||
{ return reinterpret_cast<T>(htobe64(reinterpret_cast<uint64_t>(t))); } | |||
static inline T ntoh(const T& t) | |||
{ return reinterpret_cast<T>(be64toh(reinterpret_cast<uint64_t>(t))); } | |||
}; | |||
} | |||
} |
@@ -3,9 +3,10 @@ | |||
#include <map> | |||
#include <type_traits> | |||
#include "Exception.h" | |||
#include <cpputils/misc/string.h> | |||
#include <cpputils/misc/exception.h> | |||
#define DefEnumToStringMap(enum, ...) \ | |||
#define DEFINE_ENUM_TO_STRING_MAP(enum, ...) \ | |||
namespace utl { \ | |||
namespace __impl { \ | |||
template<> \ | |||
@@ -21,7 +22,7 @@ | |||
} \ | |||
}; } } | |||
#define DefStringToEnumMap(enum, less, ...) \ | |||
#define DEFINE_STRING_TO_ENUM_MAP(enum, less, ...) \ | |||
namespace utl { \ | |||
namespace __impl { \ | |||
template<> \ | |||
@@ -37,7 +38,7 @@ | |||
} \ | |||
}; } } | |||
#define DefDefaultEnumValue(enum, value) \ | |||
#define DEFINE_DEFAULT_ENUM_VALUE(enum, value) \ | |||
namespace utl { \ | |||
namespace __impl { \ | |||
template<> \ | |||
@@ -99,23 +100,23 @@ namespace utl | |||
struct default_enum_value | |||
{ | |||
static inline T value() | |||
{ throw Exception("unable to convert string to enum"); } | |||
{ throw exception("unable to convert string to enum"); } | |||
}; | |||
} | |||
using InvariantStringLess = __impl::invariant_string_less; | |||
using invariant_string_less = __impl::invariant_string_less; | |||
template<class T> | |||
struct EnumConversion | |||
struct enum_conversion | |||
{ | |||
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; | |||
using map_enum_to_string_type = __impl::map_enum_to_string<T>; | |||
using map_string_to_enum_type = __impl::map_string_to_enum<T>; | |||
using default_enum_value_type = __impl::default_enum_value<T>; | |||
using base_type = typename std::underlying_type<T>::type; | |||
static inline std::string toString(T value, bool addValue) | |||
static inline std::string to_string(T value, bool addValue) | |||
{ | |||
static const auto& map = map_enum_to_string::value(); | |||
static const auto& map = map_enum_to_string_type::value(); | |||
auto it = map.find(value); | |||
std::string ret; | |||
if (it != map.end()) | |||
@@ -129,9 +130,9 @@ namespace utl | |||
return ret; | |||
} | |||
static inline bool tryToEnum(const std::string& str, T& value, bool acceptNumeric) | |||
static inline bool try_to_enum(const std::string& str, T& value, bool acceptNumeric) | |||
{ | |||
static const auto& map = map_string_to_enum::value(); | |||
static const auto& map = map_string_to_enum_type::value(); | |||
auto it = map.find(str); | |||
if (it != map.end()) | |||
{ | |||
@@ -146,13 +147,30 @@ namespace utl | |||
return (c != e); | |||
} | |||
static inline T toEnum(const std::string& str) | |||
static inline T to_enum(const std::string& str) | |||
{ | |||
T value; | |||
return tryToEnum(str, value) | |||
return try_to_enum(str, value) | |||
? value | |||
: default_enum_value::value(); | |||
: default_enum_value_type::value(); | |||
} | |||
}; | |||
namespace __impl | |||
{ | |||
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 << enum_conversion<T>::to_string(s, true); } | |||
}; | |||
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 enum_conversion<T>::try_to_enum(s, value, true); } | |||
}; | |||
} | |||
} |
@@ -0,0 +1,168 @@ | |||
#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 print_flag | |||
{ | |||
ResolveAddress, | |||
}; | |||
using print_flags = shifted_flags<print_flag>; | |||
public: | |||
static constexpr size_t max_stack_size = 15; | |||
static const print_flags& default_print_flag() | |||
{ | |||
static const print_flags value({ print_flag::ResolveAddress }); | |||
return value; | |||
}; | |||
private: | |||
mutable bool _msg_cache_empty; | |||
mutable std::string _msg_cache; | |||
protected: | |||
virtual void print_message(std::ostream& os) const | |||
{ | |||
os << message; | |||
} | |||
public: | |||
std::string message; | |||
void* stack[max_stack_size]; | |||
int stack_size; | |||
public: | |||
void print(std::ostream& os, const print_flags& flags = default_print_flag()) const | |||
{ | |||
print_message(os); | |||
os << std::endl; | |||
char** lines = nullptr; | |||
if (flags.is_set(print_flag::ResolveAddress)) | |||
lines = backtrace_symbols(stack, stack_size); | |||
for (int i = 0; i < stack_size; ++i) | |||
{ | |||
os << " [0x" << std::setw(2 * sizeof(void*)) << std::setfill('0') << std::hex << reinterpret_cast<uintptr_t>(stack[i]) << "]"; | |||
if (lines && lines[i]) | |||
os << " " << lines[i]; | |||
os << std::endl; | |||
} | |||
} | |||
std::string print(const print_flags& flags = default_print_flag()) const | |||
{ | |||
std::ostringstream os; | |||
print(os, flags); | |||
return os.str(); | |||
} | |||
const char* what() const throw() override | |||
{ | |||
if (_msg_cache_empty) | |||
{ | |||
_msg_cache = print(); | |||
_msg_cache_empty = false; | |||
} | |||
return _msg_cache.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), | |||
stack_size (0), | |||
_msg_cache_empty(true) | |||
{ | |||
stack_size = backtrace(&stack[0], max_stack_size); | |||
} | |||
}; | |||
struct error_exception : public exception | |||
{ | |||
public: | |||
int error; | |||
error_exception(int e) : | |||
exception(std::to_string(e) + " - " + strerror(e)), | |||
error(e) | |||
{ } | |||
error_exception(const std::string& msg, int e) : | |||
exception(msg + ": " + std::to_string(e) + " - " + strerror(e)), | |||
error(e) | |||
{ } | |||
}; | |||
struct out_of_range_exception : public exception | |||
{ | |||
protected: | |||
void print_message(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; | |||
out_of_range_exception(size_t mi, size_t ma, size_t idx, std::string msg = "") : | |||
exception (msg), | |||
min (mi), | |||
max (ma), | |||
index (idx) | |||
{ } | |||
}; | |||
struct argument_exception : public exception | |||
{ | |||
protected: | |||
void print_message(std::ostream& os) const override | |||
{ | |||
os << "invalid argument"; | |||
if (!argument.empty()) | |||
os << "(" << argument << ")"; | |||
if (!message.empty()) | |||
os << " - " << message; | |||
} | |||
public: | |||
std::string argument; | |||
argument_exception(std::string arg, std::string msg) : | |||
exception (msg), | |||
argument (arg) | |||
{ } | |||
}; | |||
struct invalid_operation_exception : public exception | |||
{ | |||
public: | |||
invalid_operation_exception(std::string msg) : | |||
exception(msg) | |||
{ } | |||
}; | |||
} |
@@ -9,14 +9,14 @@ namespace utl | |||
template<class TEnum, class TBase> | |||
struct op_flag_convert_none | |||
{ | |||
static inline TBase toBase(const TEnum e) | |||
static inline TBase to_base(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) | |||
static inline TBase to_base(const TEnum e) | |||
{ return static_cast<TBase>(1 << static_cast<int>(e)); } | |||
}; | |||
@@ -24,20 +24,20 @@ namespace utl | |||
class TEnum, | |||
class TBase = typename std::underlying_type<TEnum>::type, | |||
class Op = op_flag_convert_none<TEnum, TBase>> | |||
struct Flags | |||
struct flags | |||
{ | |||
public: | |||
TBase value; | |||
public: | |||
inline bool isSet(TEnum e) const | |||
{ return static_cast<bool>(value & Op::toBase(e)); } | |||
inline bool is_set(TEnum e) const | |||
{ return static_cast<bool>(value & Op::to_base(e)); } | |||
inline void set(TEnum e) | |||
{ value = static_cast<TBase>(value | Op::toBase(e)); } | |||
{ value = static_cast<TBase>(value | Op::to_base(e)); } | |||
inline void clear(TEnum e) | |||
{ value = static_cast<TBase>(value & ~Op::toBase(e)); } | |||
{ value = static_cast<TBase>(value & ~Op::to_base(e)); } | |||
inline void reset() | |||
{ value = 0; } | |||
@@ -50,42 +50,42 @@ namespace utl | |||
{ return static_cast<bool>(value); } | |||
bool operator[](TEnum e) const | |||
{ return isSet(e); } | |||
{ return is_set(e); } | |||
public: | |||
Flags() : | |||
flags() : | |||
value(0) | |||
{ } | |||
explicit Flags(TBase v) : | |||
explicit flags(TBase v) : | |||
value(v) | |||
{ } | |||
Flags(TEnum e) : | |||
value(Op::toBase(e)) | |||
flags(TEnum e) : | |||
value(Op::to_base(e)) | |||
{ } | |||
Flags(const Flags& other) : | |||
flags(const flags& other) : | |||
value(other.value) | |||
{ } | |||
Flags(std::initializer_list<TEnum> list) : | |||
Flags() | |||
flags(std::initializer_list<TEnum> list) : | |||
flags() | |||
{ | |||
for (auto& e : list) | |||
set(e); | |||
} | |||
public: | |||
static inline const Flags& empty() | |||
static inline const flags& empty() | |||
{ | |||
static const Flags value(0); | |||
static const flags value(0); | |||
return value; | |||
} | |||
static inline const Flags& all() | |||
static inline const flags& all() | |||
{ | |||
static const Flags value(std::numeric_limits<TBase>::max()); | |||
static const flags value(std::numeric_limits<TBase>::max()); | |||
return value; | |||
} | |||
}; | |||
@@ -93,11 +93,11 @@ namespace utl | |||
template< | |||
class TEnum, | |||
class TBase = typename std::underlying_type<TEnum>::type> | |||
using ShiftedFlags = Flags<TEnum, TBase, op_flag_convert_shift<TEnum, TBase>>; | |||
using shifted_flags = 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>>; | |||
using simple_flags = flags<TEnum, TBase, op_flag_convert_none<TEnum, TBase>>; | |||
} |
@@ -0,0 +1,39 @@ | |||
#pragma once | |||
#include <iostream> | |||
namespace utl | |||
{ | |||
namespace __impl | |||
{ | |||
inline int indent_stream_index() | |||
{ | |||
const int value = std::ios::xalloc(); | |||
return value; | |||
} | |||
} | |||
inline std::ostream& incindent(std::ostream& os) | |||
{ | |||
++os.iword(__impl::indent_stream_index()); | |||
return os; | |||
} | |||
inline std::ostream& decindent(std::ostream& os) | |||
{ | |||
auto& indent = os.iword(__impl::indent_stream_index()); | |||
if (--indent < 0) | |||
indent = 0; | |||
return os; | |||
} | |||
inline std::ostream& indent(std::ostream& os) | |||
{ | |||
auto i = os.iword(__impl::indent_stream_index()); | |||
while (i--) | |||
os << " "; | |||
return os; | |||
} | |||
} |
@@ -4,21 +4,23 @@ | |||
#include <map> | |||
#include <list> | |||
#include <vector> | |||
#include <cassert> | |||
#include <algorithm> | |||
#include "Misc.h" | |||
#include "Nullable.h" | |||
#include "Exception.h" | |||
#include "mp/core.h" | |||
#include <cpputils/mp/core.h> | |||
#include <cpputils/misc/exception.h> | |||
#include <cpputils/container/wrapper.h> | |||
#include <cpputils/container/nullable.h> | |||
// #define LINQ_DEBUG | |||
#ifdef LINQ_DEBUG | |||
#include "Misc.h" | |||
#define LINQ_TYPE_NAME() utl::TypeHelper<decltype(*this)>::name() | |||
#define LINQ_CTOR() do { std::cout << "CTOR(this=" << this << ")" << LINQ_TYPE_NAME() << std::endl; } while(0) | |||
#define LINQ_COPY_CTOR() do { std::cout << "COPY(this=" << this << ")" << LINQ_TYPE_NAME() << std::endl; } while(0) | |||
#define LINQ_MOVE_CTOR() do { std::cout << "MOVE(this=" << this << ")" << LINQ_TYPE_NAME() << std::endl; } while(0) | |||
#define LINQ_DTOR() do { std::cout << "DTOR(this=" << this << ")" << LINQ_TYPE_NAME() << std::endl; } while(0) | |||
#include <cpputils/misc/type_helper.h> | |||
#define LINQ_TYPE_NAME() utl::type_helper<decltype(*this)>::name() | |||
#define LINQ_CTOR() do { std::cout << "CTOR " << ++::utl::linq::__impl::ref_counter<decltype(*this)>::value() << "(this=" << this << ")" << LINQ_TYPE_NAME() << std::endl; } while(0) | |||
#define LINQ_COPY_CTOR() do { std::cout << "COPY " << ++::utl::linq::__impl::ref_counter<decltype(*this)>::value() << "(this=" << this << ")" << LINQ_TYPE_NAME() << std::endl; } while(0) | |||
#define LINQ_MOVE_CTOR() do { std::cout << "MOVE " << ++::utl::linq::__impl::ref_counter<decltype(*this)>::value() << "(this=" << this << ")" << LINQ_TYPE_NAME() << std::endl; } while(0) | |||
#define LINQ_DTOR() do { std::cout << "DTOR " << --::utl::linq::__impl::ref_counter<decltype(*this)>::value() << "(this=" << this << ")" << LINQ_TYPE_NAME() << std::endl; } while(0) | |||
#else | |||
#define LINQ_TYPE_NAME() while(0) | |||
#define LINQ_CTOR() while(0) | |||
@@ -27,10 +29,23 @@ | |||
#define LINQ_DTOR() while(0) | |||
#endif | |||
namespace linq | |||
{ | |||
namespace utl{ | |||
namespace linq { | |||
namespace __impl | |||
{ | |||
#ifdef LINQ_DEBUG | |||
template<typename... T> | |||
struct ref_counter | |||
{ | |||
static inline size_t& value() | |||
{ | |||
static size_t value = 0; | |||
return value; | |||
} | |||
}; | |||
#endif | |||
struct tag_range { }; | |||
struct tag_builder { }; | |||
@@ -220,7 +235,7 @@ namespace linq | |||
assert(current != end); | |||
return value_type( | |||
*current->first, | |||
std::move(container.createRange(range_indices))); | |||
container.createRange(range_indices)); | |||
} | |||
inline bool next() | |||
@@ -315,11 +330,11 @@ namespace linq | |||
template<class TBuilder> | |||
inline auto operator >> (TBuilder&& builder) & | |||
{ return builder.build(std::move(lookup_key_value_range(*this))); } | |||
{ return builder.build(lookup_key_value_range(*this)); } | |||
template<class TBuilder> | |||
inline auto operator >> (TBuilder&& builder) && | |||
{ return builder.build(std::move(lookup_key_value_range(std::move(*this)))); } | |||
{ return builder.build(lookup_key_value_range(std::move(*this))); } | |||
private: | |||
inline lookup(keys_type&& k, values_type&& v) : | |||
@@ -606,7 +621,7 @@ namespace linq | |||
using this_type = select_range<range_type, predicate_type>; | |||
using range_value_type = mp_range_value_type<range_type>; | |||
using value_type = decltype(std::declval<predicate_type>()(std::declval<range_value_type>())); | |||
using cache_type = utl::Nullable<value_type>; | |||
using cache_type = utl::nullable<value_type>; | |||
predicate_type predicate; | |||
range_type range; | |||
@@ -670,12 +685,12 @@ namespace linq | |||
using this_type = where_range<range_type, predicate_type>; | |||
using range_value_type = mp_range_value_type<range_type>; | |||
using predicate_return_type = decltype(std::declval<predicate_type>()(std::declval<range_value_type>())); | |||
using inner_range_type = utl::mp::eval_if< | |||
using inner_range_type = utl::mp::eval_if_t< | |||
std::is_base_of<tag_range, predicate_return_type>, | |||
predicate_return_type, | |||
mp_make_inner_range, predicate_return_type>; | |||
using value_type = mp_range_value_type<inner_range_type>; | |||
using inner_range_cache_type = utl::Nullable<inner_range_type>; | |||
using inner_range_cache_type = utl::nullable<inner_range_type>; | |||
predicate_type predicate; | |||
range_type range; | |||
@@ -757,7 +772,7 @@ namespace linq | |||
inline value_type& front() | |||
{ | |||
assert(current >= 0 && static_cast<size_t>(current) < values.size()); | |||
return *values.at(current); | |||
return *values.at(static_cast<typename vector_type::size_type>(current)); | |||
} | |||
inline bool next() | |||
@@ -1179,10 +1194,10 @@ namespace linq | |||
using range_value_type = mp_range_value_type<TRange>; | |||
if (!range.next()) | |||
throw utl::Exception("range is empty"); | |||
throw utl::exception("range is empty"); | |||
auto ret = std::forward<range_value_type>(range.front()); | |||
if (range.next()) | |||
throw utl::Exception("range contains more than one value"); | |||
throw utl::exception("range contains more than one value"); | |||
return ret; | |||
} | |||
}; | |||
@@ -1210,7 +1225,7 @@ namespace linq | |||
{ | |||
using range_value_type = mp_range_value_type<TRange>; | |||
if (!range.next()) | |||
throw utl::Exception("range is empty"); | |||
throw utl::exception("range is empty"); | |||
return std::forward<range_value_type>(range.front()); | |||
} | |||
}; | |||
@@ -1281,7 +1296,7 @@ namespace linq | |||
while(range.next()) | |||
tmp = std::forward<range_value_type>(range.front()); | |||
if (!static_cast<bool>(tmp)) | |||
throw utl::Exception("range is empty"); | |||
throw utl::exception("range is empty"); | |||
return std::forward<range_value_type>(*tmp); | |||
} | |||
}; | |||
@@ -1318,7 +1333,7 @@ namespace linq | |||
vector_type ret; | |||
ret.reserve(capacity); | |||
while (range.next()) | |||
ret.emplace_back(std::move(range.front())); | |||
ret.emplace_back(std::forward<range_value_type>(range.front())); | |||
return ret; | |||
} | |||
@@ -1338,7 +1353,7 @@ namespace linq | |||
list_type ret; | |||
while (range.next()) | |||
ret.emplace_back(std::move(range.front())); | |||
ret.emplace_back(std::forward<range_value_type>(range.front())); | |||
return ret; | |||
} | |||
}; | |||
@@ -1369,7 +1384,7 @@ namespace linq | |||
key_predicate(range.front()), | |||
value_predicate(range.front())); | |||
if (!ret.second) | |||
throw utl::Exception("duplicate key in range"); | |||
throw utl::exception("duplicate key in range"); | |||
} | |||
return map; | |||
} | |||
@@ -1590,10 +1605,10 @@ namespace linq | |||
template<class TKeyPredicate> | |||
inline auto to_map(TKeyPredicate&& kp) | |||
{ return to_map(std::forward<TKeyPredicate>(kp), std::move(op_select_value_default())); } | |||
{ return to_map(std::forward<TKeyPredicate>(kp), op_select_value_default()); } | |||
inline auto to_map() | |||
{ return to_map(std::move(op_select_key_default()), std::move(op_select_value_default())); } | |||
{ return to_map(op_select_key_default(), op_select_value_default()); } | |||
template<class TPredicate> | |||
inline auto for_each(TPredicate&& p) | |||
@@ -1605,14 +1620,16 @@ namespace linq | |||
template<class TKeyPredicate> | |||
inline auto to_lookup(TKeyPredicate&& kp) | |||
{ return to_lookup(std::forward<TKeyPredicate>(kp), std::move(op_select_value_default())); } | |||
{ return to_lookup(std::forward<TKeyPredicate>(kp), op_select_value_default()); } | |||
inline auto to_lookup() | |||
{ return to_lookup(std::move(op_select_key_default()), std::move(op_select_value_default())); } | |||
{ return to_lookup(op_select_key_default(), op_select_value_default()); } | |||
template <class TKey, class TValue> | |||
using lookup_value_range_type = typename __impl::lookup<TKey, TValue>::lookup_range_wrapper; | |||
template <class TKey, class TValue> | |||
using lookup_key_value_range_type = typename __impl::lookup<TKey, TValue>::lookup_key_value_range_wrapper; | |||
} | |||
} |
@@ -0,0 +1,35 @@ | |||
#pragma once | |||
#include <cxxabi.h> | |||
#include <iostream> | |||
#if defined(__linux__) | |||
# include <endian.h> | |||
#elif defined(__FreeBSD__) || defined(__NetBSD__) | |||
# include <sys/endian.h> | |||
#elif defined(__OpenBSD__) | |||
# include <sys/types.h> | |||
# define be16toh(x) betoh16(x) | |||
# define be32toh(x) betoh32(x) | |||
# define be64toh(x) betoh64(x) | |||
#endif | |||
namespace utl | |||
{ | |||
inline int bit_count(uint32_t u) | |||
{ | |||
u = u | |||
- ((u >> 1) & 033333333333) | |||
- ((u >> 2) & 011111111111); | |||
return ((u + (u >> 3)) & 030707070707) % 63; | |||
} | |||
template<class T, class S> | |||
inline bool try_cast(T* t, S*& s) | |||
{ | |||
s = dynamic_cast<S*>(t); | |||
return static_cast<bool>(s); | |||
} | |||
} |
@@ -1,7 +1,7 @@ | |||
#pragma once | |||
#include <iostream> | |||
#include "Exception.h" | |||
#include "exception.h" | |||
namespace utl | |||
{ | |||
@@ -14,10 +14,10 @@ namespace utl | |||
inline void operator()(std::ostream& os, const T& t) | |||
{ | |||
if (!os) | |||
throw Exception("unable to write data to stream: invalid stream"); | |||
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"); | |||
throw exception("unable to write data to stream: stream error"); | |||
} | |||
}; | |||
@@ -27,11 +27,11 @@ namespace utl | |||
inline void operator()(std::istream& is, T& t) | |||
{ | |||
if (!is) | |||
throw Exception("unable to read data from stream: invalid stream"); | |||
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"); | |||
throw exception("unable to read data from stream: EOF"); | |||
if (!is) | |||
throw Exception("unable to read data from stream: stream error"); | |||
throw exception("unable to read data from stream: stream error"); | |||
} | |||
}; | |||
@@ -41,13 +41,13 @@ namespace utl | |||
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"); | |||
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()); | |||
throw exception("unable to write data to stream: invalid stream"); | |||
os.write(t.data(), static_cast<std::streamsize>(t.size())); | |||
if (!os) | |||
throw Exception("unable to write data to stream: stream error"); | |||
throw exception("unable to write data to stream: stream error"); | |||
} | |||
}; | |||
@@ -59,13 +59,13 @@ namespace utl | |||
uint32_t sz; | |||
op_read_stream<uint32_t, void>()(is, sz); | |||
if (!is) | |||
throw Exception("unable to read data from stream: invalid stream"); | |||
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"); | |||
throw exception("unable to read data from stream: EOF"); | |||
if (!is) | |||
throw Exception("unable to read data from stream: stream error"); | |||
throw exception("unable to read data from stream: stream error"); | |||
t = std::move(tmp); | |||
} | |||
}; | |||
@@ -85,7 +85,7 @@ namespace utl | |||
}; | |||
} | |||
struct StreamHelper | |||
struct stream_helper | |||
{ | |||
template<class T> | |||
static inline void write(std::ostream& os, const T& t) | |||
@@ -104,4 +104,18 @@ namespace utl | |||
} | |||
}; | |||
struct stream_format_saver | |||
{ | |||
std::ios state; | |||
std::ostream& stream; | |||
stream_format_saver(std::ostream& s) : | |||
state (nullptr), | |||
stream (s) | |||
{ state.copyfmt(stream); } | |||
~stream_format_saver() | |||
{ stream.copyfmt(state); } | |||
}; | |||
} |
@@ -1,8 +1,7 @@ | |||
#pragma once | |||
#include <sstream> | |||
#include "Nullable.h" | |||
#include "EnumConversion.h" | |||
#include <cpputils/misc/exception.h> | |||
namespace utl | |||
{ | |||
@@ -17,39 +16,19 @@ namespace utl | |||
}; | |||
template<class T> | |||
struct op_to_string<T, decltype(std::declval<T>().toString(std::declval<std::ostream&>()), void())> | |||
struct op_to_string<T, decltype(std::declval<T>().to_string(std::declval<std::ostream&>()), void())> | |||
{ | |||
inline void operator()(std::ostream& os, const T& s) const | |||
{ s.toString(os); } | |||
{ s.to_string(os); } | |||
}; | |||
template<class T> | |||
struct op_to_string<T, decltype(std::declval<T>().toString(), void())> | |||
struct op_to_string<T, decltype(std::declval<T>().to_string(), void())> | |||
{ | |||
inline void operator()(std::ostream& os, const T& s) const | |||
{ os << s.toString(); } | |||
{ os << s.to_string(); } | |||
}; | |||
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 | |||
{ | |||
@@ -82,24 +61,17 @@ namespace utl | |||
}; | |||
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())> | |||
struct op_from_string<T, decltype(std::declval<T&>().from_string(std::declval<const std::string&>()), void())> | |||
{ | |||
inline bool operator()(const std::string& s, T& value) const | |||
{ return T::fromString(s, value); } | |||
{ return value.from_string(s); } | |||
}; | |||
template<class T> | |||
struct op_from_string<T, typename std::enable_if<std::is_enum<T>::value>::type> | |||
struct op_from_string<T, decltype(T::from_string(std::declval<const std::string&>(), std::declval<T&>()), void())> | |||
{ | |||
inline bool operator()(const std::string& s, T& value) const | |||
{ return EnumConversion<T>::tryToEnum(s, value, true); } | |||
{ return T::from_string(s, value); } | |||
}; | |||
template<class T> | |||
@@ -128,42 +100,42 @@ namespace utl | |||
} | |||
template<class T> | |||
inline void toString(std::ostream& os, const T& t) | |||
inline void to_string(std::ostream& os, const T& t) | |||
{ __impl::op_to_string<T>()(os, t); } | |||
template<class T> | |||
inline std::string toString(const T& t) | |||
inline std::string to_string(const T& t) | |||
{ | |||
std::ostringstream ss; | |||
toString(ss, t); | |||
to_string(ss, t); | |||
return ss.str(); | |||
} | |||
template<> | |||
inline std::string toString<std::string>(const std::string& s) | |||
inline std::string to_string<std::string>(const std::string& s) | |||
{ return s; } | |||
template<class T> | |||
inline bool tryFromString(const std::string& s, T& value) | |||
inline bool try_from_string(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) | |||
inline T from_string(const std::string& s, T defaultValue) | |||
{ | |||
T tmp; | |||
return tryFromString<T>(s, tmp) | |||
return try_from_string<T>(s, tmp) | |||
? tmp | |||
: defaultValue; | |||
} | |||
template<class T> | |||
inline T fromString(const std::string& s) | |||
inline T from_string(const std::string& s) | |||
{ | |||
T tmp; | |||
if (!tryFromString<T>(s, tmp)) | |||
throw Exception(std::string("unable to convert string to specific value: ") + s); | |||
if (!try_from_string<T>(s, tmp)) | |||
throw exception(std::string("unable to convert string to specific value: ") + s); | |||
return tmp; | |||
} | |||
@@ -20,7 +20,7 @@ namespace utl | |||
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); | |||
return tv; | |||
} | |||
}; | |||
@@ -44,7 +44,7 @@ namespace utl | |||
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); | |||
return ts; | |||
} | |||
}; |
@@ -10,13 +10,13 @@ namespace utl | |||
} | |||
template<class It, class Transform, class Enable = void> | |||
struct TransformIterator; | |||
struct transform_iterator; | |||
template<class It, class Transform> | |||
struct TransformIterator<It, Transform, __impl::mp_enable_if_has_iterator_tag<It, std::forward_iterator_tag>> | |||
struct transform_iterator<It, Transform, __impl::mp_enable_if_has_iterator_tag<It, std::forward_iterator_tag>> | |||
{ | |||
public: | |||
using iterator_type = TransformIterator; | |||
using iterator_type = transform_iterator; | |||
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>())); | |||
@@ -29,10 +29,10 @@ namespace utl | |||
Transform _transform; | |||
public: | |||
TransformIterator() | |||
transform_iterator() | |||
{ } | |||
TransformIterator(It it, Transform transform) : | |||
transform_iterator(It it, Transform transform) : | |||
_it (it), | |||
_transform (transform) | |||
{ } | |||
@@ -66,10 +66,10 @@ namespace utl | |||
}; | |||
template<class It, class Transform> | |||
struct TransformIterator<It, Transform, __impl::mp_enable_if_has_iterator_tag<It, std::bidirectional_iterator_tag>> | |||
struct transform_iterator<It, Transform, __impl::mp_enable_if_has_iterator_tag<It, std::bidirectional_iterator_tag>> | |||
{ | |||
public: | |||
using iterator_type = TransformIterator; | |||
using iterator_type = transform_iterator; | |||
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>())); | |||
@@ -82,10 +82,10 @@ namespace utl | |||
Transform _transform; | |||
public: | |||
TransformIterator() | |||
transform_iterator() | |||
{ } | |||
TransformIterator(It it, Transform transform) : | |||
transform_iterator(It it, Transform transform) : | |||
_it (it), | |||
_transform (transform) | |||
{ } | |||
@@ -128,10 +128,10 @@ namespace utl | |||
}; | |||
template<class It, class Transform> | |||
struct TransformIterator<It, Transform, __impl::mp_enable_if_has_iterator_tag<It, std::random_access_iterator_tag>> | |||
struct transform_iterator<It, Transform, __impl::mp_enable_if_has_iterator_tag<It, std::random_access_iterator_tag>> | |||
{ | |||
public: | |||
using iterator_type = TransformIterator; | |||
using iterator_type = transform_iterator; | |||
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>())); | |||
@@ -144,10 +144,10 @@ namespace utl | |||
Transform _transform; | |||
public: | |||
TransformIterator() | |||
transform_iterator() | |||
{ } | |||
TransformIterator(It it, Transform transform) : | |||
transform_iterator(It it, Transform transform) : | |||
_it (it), | |||
_transform (transform) | |||
{ } | |||
@@ -226,11 +226,11 @@ namespace utl | |||
}; | |||
template<class It, class Transform> | |||
TransformIterator<It, Transform> makeTransformIterator(const It& it, const Transform& transform) | |||
{ return TransformIterator<It, Transform>(it, transform); } | |||
transform_iterator<It, Transform> make_transform_iterator(const It& it, const Transform& transform) | |||
{ return transform_iterator<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) | |||
inline transform_iterator<It, Transform> | |||
operator+(const typename transform_iterator<It, Transform>::difference_type& n, const transform_iterator<It, Transform>& it) | |||
{ return it + n; } | |||
} |
@@ -0,0 +1,20 @@ | |||
#pragma once | |||
#include <cxxabi.h> | |||
namespace utl | |||
{ | |||
template<class T> | |||
struct type_helper | |||
{ | |||
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()); | |||
} | |||
}; | |||
} |
@@ -0,0 +1,7 @@ | |||
#pragma once | |||
#include <cpputils/mp/core.h> | |||
#include <cpputils/mp/util.h> | |||
#include <cpputils/mp/intern.h> | |||
#include <cpputils/mp/container.h> | |||
#include <cpputils/mp/operations.h> |
@@ -1,980 +0,0 @@ | |||
#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,3 @@ | |||
#pragma once | |||
#include <cpputils/mp/container/pair.h> |
@@ -0,0 +1,238 @@ | |||
#pragma once | |||
#include <cpputils/mp/core.h> | |||
#include <cpputils/mp/util/make.h> | |||
#include <cpputils/mp/util/tag_of.h> | |||
#include <cpputils/mp/intern/ebo.h> | |||
#include <cpputils/mp/intern/operators_orderable.h> | |||
#include <cpputils/mp/intern/operators_comparable.h> | |||
#include <cpputils/mp/operations/first.h> | |||
#include <cpputils/mp/operations/second.h> | |||
#include <cpputils/mp/operations/compare/less.h> | |||
#include <cpputils/mp/operations/compare/less_equal.h> | |||
#include <cpputils/mp/operations/compare/equal.h> | |||
#include <cpputils/mp/operations/logical/or.h> | |||
#include <cpputils/mp/operations/logical/and.h> | |||
#include <cpputils/mp/operations/logical/not.h> | |||
namespace utl { | |||
namespace mp { | |||
struct tag_pair { }; | |||
constexpr auto make_pair = make<tag_pair>; | |||
namespace __impl | |||
{ | |||
template<int> | |||
struct pair_index; | |||
} | |||
template<typename First, typename Second> | |||
struct pair : | |||
private intern::ebo<__impl::pair_index<0>, First>, | |||
private intern::ebo<__impl::pair_index<1>, Second> | |||
{ | |||
using this_type = pair<First, Second>; | |||
// default constructor | |||
template<typename... dummy, typename = enable_if_c< | |||
is_constructible<First, dummy...>::value && | |||
is_constructible<Second, dummy...>::value>> | |||
constexpr pair() | |||
: intern::ebo<__impl::pair_index<0>, First>() | |||
, intern::ebo<__impl::pair_index<1>, Second>() | |||
{ } | |||
// variadic constructors | |||
template<typename... dummy, typename = enable_if_c< | |||
is_constructible<First, const First&, dummy...>::value && | |||
is_constructible<Second, const Second&, dummy...>::value>> | |||
constexpr pair(First f, Second s) | |||
: intern::ebo<__impl::pair_index<0>, First>(f) | |||
, intern::ebo<__impl::pair_index<1>, Second>(s) | |||
{ } | |||
template<typename F, typename S, typename = enable_if_c< | |||
is_convertible<F&&, First>::value && | |||
is_convertible<S&&, Second>::value>> | |||
constexpr pair(F&& f, S&& s) | |||
: intern::ebo<__impl::pair_index<0>, First>(std::forward<F>(f)) | |||
, intern::ebo<__impl::pair_index<1>, Second>(std::forward<S>(s)) | |||
{ } | |||
// copy and move constructor | |||
template<typename F, typename S, typename = enable_if_c< | |||
is_constructible<First, const F&>::value && | |||
is_constructible<Second, const S&>::value && | |||
is_convertible<const F&, First>::value && | |||
is_convertible<const S&, Second>::value>> | |||
constexpr pair(const pair<F, S>& other) | |||
: intern::ebo<__impl::pair_index<0>, First>(mp::first(other)) | |||
, intern::ebo<__impl::pair_index<1>, Second>(mp::second(other)) | |||
{ } | |||
template<typename F, typename S, typename = enable_if_c< | |||
is_constructible<First, const F&>::value && | |||
is_constructible<Second, const S&>::value && | |||
is_convertible<const F&, First>::value && | |||
is_convertible<const S&, Second>::value>> | |||
constexpr pair(pair<F, S>&& other) | |||
: intern::ebo<__impl::pair_index<0>, First>(mp::first(std::forward<pair<F, S>>(other))) | |||
, intern::ebo<__impl::pair_index<1>, Second>(mp::second(std::forward<pair<F, S>>(other))) | |||
{ } | |||
// copy and move assignment | |||
template <typename F, typename S, typename = enable_if_c< | |||
is_assignable<First&, const F&>::value && | |||
is_assignable<Second&, const S&>::value>> | |||
constexpr pair& operator=(const pair<F, S>& other) { | |||
mp::first(*this) = mp::first(other); | |||
mp::second(*this) = mp::second(other); | |||
return *this; | |||
} | |||
template <typename F, typename S, typename = enable_if_c< | |||
is_assignable<First&, const F &>::value && | |||
is_assignable<Second&, const S &>::value>> | |||
constexpr pair& operator=(pair<F, S>&& other) { | |||
mp::first(*this) = mp::first(std::move<pair<F, S>>(other)); | |||
mp::second(*this) = mp::second(std::move<pair<F, S>>(other)); | |||
return *this; | |||
} | |||
constexpr auto first() const | |||
{ return mp::first(*this); } | |||
constexpr auto second() const | |||
{ return mp::second(*this); } | |||
// Prevent the compiler from defining the default copy and move | |||
// constructors, which interfere with the SFINAE above. | |||
~pair() = default; | |||
friend struct __impl::first_impl<tag_pair>; | |||
friend struct __impl::second_impl<tag_pair>; | |||
}; | |||
namespace intern { | |||
template<> | |||
struct operators_comparable<tag_pair> | |||
{ static constexpr bool value = true; }; | |||
template<> | |||
struct operators_orderable<tag_pair> | |||
{ static constexpr bool value = true; }; | |||
} | |||
namespace __impl | |||
{ | |||
/* tag_of */ | |||
template<typename First, typename Second> | |||
struct tag_of_impl<pair<First, Second>> | |||
{ using type = tag_pair; }; | |||
/* make */ | |||
template<> | |||
struct make_impl<tag_pair> | |||
{ | |||
template<typename F, typename S> | |||
static constexpr pair<decay_type<F>, decay_type<S>> apply(F&& f, S&& s) | |||
{ return { static_cast<F&&>(f), static_cast<S&&>(s) }; } | |||
}; | |||
/* first */ | |||
template<> | |||
struct first_impl<tag_pair> | |||
{ | |||
using type = int; | |||
template<typename First, typename Second> | |||
static constexpr auto apply(pair<First, Second>& p) | |||
{ | |||
return intern::ebo_get<pair_index<0>>( | |||
static_cast<intern::ebo<pair_index<0>, First>&>(p) | |||
); | |||
} | |||
template<typename First, typename Second> | |||
static constexpr auto apply(pair<First, Second>&& p) | |||
{ | |||
return intern::ebo_get<pair_index<0>>( | |||
static_cast<intern::ebo<pair_index<0>, First>&&>(p) | |||
); | |||
} | |||
template<typename First, typename Second> | |||
static constexpr auto apply(const pair<First, Second>& p) | |||
{ | |||
return intern::ebo_get<pair_index<0>>( | |||
static_cast<const intern::ebo<pair_index<0>, First>&>(p) | |||
); | |||
} | |||
}; | |||
/* second */ | |||
template<> | |||
struct second_impl<tag_pair> | |||
{ | |||
template<typename First, typename Second> | |||
static constexpr auto apply(pair<First, Second>& p) | |||
{ | |||
return intern::ebo_get<pair_index<1>>( | |||
static_cast<intern::ebo<pair_index<1>, Second>&>(p) | |||
); | |||
} | |||
template<typename First, typename Second> | |||
static constexpr auto apply(pair<First, Second>&& p) | |||
{ | |||
return intern::ebo_get<pair_index<1>>( | |||
static_cast<intern::ebo<pair_index<1>, Second>&&>(p) | |||
); | |||
} | |||
template<typename First, typename Second> | |||
static constexpr auto apply(const pair<First, Second>& p) | |||
{ | |||
return intern::ebo_get<pair_index<1>>( | |||
static_cast<const intern::ebo<pair_index<1>, Second>&>(p) | |||
); | |||
} | |||
}; | |||
/* equal */ | |||
template<> | |||
struct equal_impl<tag_pair, tag_pair> | |||
{ | |||
template<typename L, typename R> | |||
static constexpr auto apply(const L& l, const R& r) | |||
{ | |||
return and_( | |||
equal(first(l), first(r)), | |||
equal(second(l), second(r))); | |||
} | |||
}; | |||
/* less */ | |||
template<> | |||
struct less_impl<tag_pair, tag_pair> | |||
{ | |||
template<typename L, typename R> | |||
static constexpr auto apply(const L& l, const R& r) | |||
{ | |||
return or_( | |||
less(first(l), first(r)), | |||
and_( | |||
less_equal(first (l), first (r)), | |||
less (second(l), second(r)) | |||
) | |||
); | |||
} | |||
}; | |||
} | |||
} | |||
} |
@@ -1,168 +1,7 @@ | |||
#include <cstddef> | |||
#include <utility> | |||
#pragma once | |||
namespace utl { | |||
namespace mp { | |||
/*********************************************************************************************/ | |||
/* types *************************************************************************************/ | |||
/*********************************************************************************************/ | |||
template<typename T> | |||
struct tag_const { }; | |||
template<typename T, T t> | |||
struct integral_constant : | |||
public std::integral_constant<T, t> | |||
{ | |||
using tag = tag_const<T>; | |||
}; | |||
template<typename T, T t> | |||
constexpr integral_constant<T, t> make_const { }; | |||
template<bool B> | |||
using c_bool = integral_constant<bool, B>; | |||
template<size_t S> | |||
using c_size_t = integral_constant<size_t, S>; | |||
template<typename...> | |||
struct list { }; | |||
template<typename... T> | |||
struct inherit : T... | |||
{ }; | |||
template<typename T, T... Ints> | |||
struct integer_sequence | |||
{ }; | |||
template<size_t... Ints> | |||
using index_sequence = integer_sequence<size_t, Ints...>; | |||
/*********************************************************************************************/ | |||
/* constants *********************************************************************************/ | |||
/*********************************************************************************************/ | |||
using c_true = c_bool<true>; | |||
using c_false = c_bool<false>; | |||
using c_zero = c_size_t<0>; | |||
/*********************************************************************************************/ | |||
/* modifier **********************************************************************************/ | |||
/*********************************************************************************************/ | |||
template<typename T> | |||
using add_pointer = T*; | |||
template<typename T> | |||
using add_reference = T&; | |||
template<typename T> | |||
using remove_ptr = typename std::remove_pointer<T>::type; | |||
template<typename T> | |||
using remove_const = typename std::remove_const<T>::type; | |||
template<typename T> | |||
using remove_cv = typename std::remove_cv<T>::type; | |||
template<typename T> | |||
using remove_ref = typename std::remove_reference<T>::type; | |||
template<typename T> | |||
using clean_type = remove_cv<remove_ptr<remove_ref<T>>>; | |||
namespace __impl /* forward declaration */ | |||
{ | |||
template<typename T, typename U = remove_ref<T>> | |||
struct decay_impl; | |||
} | |||
template<class T> | |||
using decay = typename __impl::decay_impl<T>::type; | |||
namespace __impl /* implementation */ | |||
{ | |||
template <typename T, typename U = remove_ref<T>> | |||
struct decay | |||
{ using type = remove_cv<U>; }; | |||
template <typename T, typename U> | |||
struct decay<T, U[]> | |||
{ using type = U*; }; | |||
template <typename T, typename U, std::size_t N> | |||
struct decay<T, U[N]> | |||
{ using type = U*; }; | |||
template <typename T, typename R, typename ...A> | |||
struct decay<T, R(A...)> | |||
{ using type = R(*)(A...); }; | |||
template <typename T, typename R, typename ...A> | |||
struct decay<T, R(A..., ...)> | |||
{ using type = R(*)(A..., ...); }; | |||
} | |||
/*********************************************************************************************/ | |||
/* conditionals ******************************************************************************/ | |||
/*********************************************************************************************/ | |||
namespace __impl /* forward declaration */ | |||
{ | |||
template<bool C, typename T, typename E> | |||
struct if_c_impl; | |||
template<bool C, typename T, template<typename...> typename E, typename... A> | |||
struct eval_if_c_impl; | |||
} | |||
template<typename T, typename S> | |||
using is_same = c_bool<std::is_same<T, S>::value>; | |||
template<typename Base, typename Derived> | |||
using is_base_of = c_bool<std::is_base_of<Base, Derived>::value>; | |||
template<typename T, typename S = void> | |||
using enable_if = typename std::enable_if<T::value, S>::type; | |||
template<bool B, typename S = void> | |||
using enable_if_c = typename std::enable_if<B, S>::type; | |||
template<typename B, typename T, typename E> | |||
using if_t = typename __impl::if_c_impl<B::value != 0, T, E>::type; | |||
template<bool B, typename T, typename E> | |||
using if_c = typename __impl::if_c_impl<B, T, E>::type; | |||
template<typename C, typename T, template<typename...> typename E, typename... A> | |||
using eval_if = typename __impl::eval_if_c_impl<C::value != 0, T, E, A...>::type; | |||
template<bool C, typename T, template<typename...> typename E, typename... A> | |||
using eval_if_c = typename __impl::eval_if_c_impl<C, T, E, A...>::type; | |||
namespace __impl /* implementation */ | |||
{ | |||
template<typename T, typename F> | |||
struct if_c_impl<true, T, F> | |||
{ using type = T; }; | |||
template<typename T, typename F> | |||
struct if_c_impl<false, T, F> | |||
{ using type = F; }; | |||
template<typename T, template<typename...> typename E, typename... A> | |||
struct eval_if_c_impl<true, T, E, A...> | |||
{ using type = T; }; | |||
template<typename T, template<typename...> typename E, typename... A> | |||
struct eval_if_c_impl<false, T, E, A...> | |||
{ using type = E<A...>; }; | |||
} | |||
} | |||
} | |||
#include <cpputils/mp/core/checker.h> | |||
#include <cpputils/mp/core/conditionals.h> | |||
#include <cpputils/mp/core/const.h> | |||
#include <cpputils/mp/core/modifier.h> | |||
#include <cpputils/mp/core/types.h> |
@@ -0,0 +1,51 @@ | |||
#pragma once | |||
#include <type_traits> | |||
#include <cpputils/mp/core/const.h> | |||
namespace utl { | |||
namespace mp { | |||
namespace __impl /* forward declaration */ | |||
{ | |||
template<typename...> | |||
struct is_valid_impl; | |||
} | |||
template<typename T, typename S> | |||
using is_same = c_bool<std::is_same<T, S>::value>; | |||
template<typename Base, typename Derived> | |||
using is_base_of = c_bool<std::is_base_of<Base, Derived>::value>; | |||
template<typename... T> | |||
using is_constructible = c_bool<std::is_constructible<T...>::value>; | |||
template<typename... T> | |||
using is_empty = c_bool<std::is_empty<T...>::value>; | |||
template<typename... T> | |||
using is_final = c_bool<std::is_final<T...>::value>; | |||
template<typename T, typename U> | |||
using is_convertible = c_bool<std::is_convertible<T, U>::value>; | |||
template<typename T, typename U> | |||
using is_assignable = c_bool<std::is_assignable<T, U>::value>; | |||
template<typename... T> | |||
using is_valid = __impl::is_valid_impl<T...>; | |||
template<typename T> | |||
using is_arithmetic = c_bool<std::is_arithmetic<T>::value>; | |||
namespace __impl /* implementation */ | |||
{ | |||
template<typename...> | |||
struct is_valid_impl : | |||
public c_true | |||
{ }; | |||
} | |||
} | |||
} |
@@ -0,0 +1,53 @@ | |||
#pragma once | |||
namespace utl { | |||
namespace mp { | |||
namespace __impl /* forward declaration */ | |||
{ | |||
template<bool C, typename T, typename E> | |||
struct if_c_impl; | |||
template<bool C, typename T, template<typename...> class E, typename... A> | |||
struct eval_if_c_impl; | |||
} | |||
template<typename T, typename S = void> | |||
using enable_if = typename std::enable_if<T::value, S>::type; | |||
template<bool B, typename S = void> | |||
using enable_if_c = typename std::enable_if<B, S>::type; | |||
template<typename B, typename T, typename E> | |||
using if_t = typename __impl::if_c_impl<B::value != 0, T, E>::type; | |||
template<bool B, typename T, typename E> | |||
using if_c = typename __impl::if_c_impl<B, T, E>::type; | |||
template<typename C, typename T, template<typename...> class E, typename... A> | |||
using eval_if_t = typename __impl::eval_if_c_impl<C::value != 0, T, E, A...>::type; | |||
template<bool C, typename T, template<typename...> class E, typename... A> | |||
using eval_if_c = typename __impl::eval_if_c_impl<C, T, E, A...>::type; | |||
namespace __impl /* implementation */ | |||
{ | |||
template<typename T, typename F> | |||
struct if_c_impl<true, T, F> | |||
{ using type = T; }; | |||
template<typename T, typename F> | |||
struct if_c_impl<false, T, F> | |||
{ using type = F; }; | |||
template<typename T, template<typename...> class E, typename... A> | |||
struct eval_if_c_impl<true, T, E, A...> | |||
{ using type = T; }; | |||
template<typename T, template<typename...> class E, typename... A> | |||
struct eval_if_c_impl<false, T, E, A...> | |||
{ using type = E<A...>; }; | |||
} | |||
} | |||
} |
@@ -0,0 +1,30 @@ | |||
#pragma once | |||
#include <cstddef> | |||
#include <type_traits> | |||
namespace utl { | |||
namespace mp { | |||
template<typename T> | |||
struct tag_const { }; | |||
template<typename T, T t> | |||
struct integral_constant : | |||
public std::integral_constant<T, t> | |||
{ | |||
using tag = tag_const<T>; | |||
}; | |||
template<bool B> | |||
using c_bool = integral_constant<bool, B>; | |||
template<size_t S> | |||
using c_size_t = integral_constant<size_t, S>; | |||
using c_true = c_bool<true>; | |||
using c_false = c_bool<false>; | |||
using c_zero = c_size_t<0>; | |||
} | |||
} |
@@ -0,0 +1,78 @@ | |||
#pragma once | |||
#include <type_traits> | |||
namespace utl { | |||
namespace mp { | |||
template<typename T> | |||
using add_pointer = T*; | |||
template<typename T> | |||
using add_reference = T&; | |||
template<typename T> | |||
using remove_ptr = typename std::remove_pointer<T>::type; | |||
template<typename T> | |||
using remove_const = typename std::remove_const<T>::type; | |||
template<typename T> | |||
using remove_cv = typename std::remove_cv<T>::type; | |||
template<typename T> | |||
using remove_ref = typename std::remove_reference<T>::type; | |||
template<typename T> | |||
using clean_type = remove_cv<remove_ptr<remove_ref<T>>>; | |||
namespace __impl /* forward declaration */ | |||
{ | |||
template<typename T, typename U = remove_ref<T>> | |||
struct decay_impl; | |||
template<typename T, typename U, typename = void> | |||
struct common_type_impl | |||
{ }; | |||
} | |||
template<class T> | |||
using decay = __impl::decay_impl<T>; | |||
template<class T> | |||
using decay_type = typename __impl::decay_impl<T>::type; | |||
template<typename T, typename U> | |||
using common_type = __impl::common_type_impl<T, U>; | |||
namespace __impl /* implementation */ | |||
{ | |||
/* decay */ | |||
template <typename T, typename U> | |||
struct decay_impl | |||
{ using type = remove_cv<U>; }; | |||
template <typename T, typename U> | |||
struct decay_impl<T, U[]> | |||
{ using type = U*; }; | |||
template <typename T, typename U, std::size_t N> | |||
struct decay_impl<T, U[N]> | |||
{ using type = U*; }; | |||
template <typename T, typename R, typename ...A> | |||
struct decay_impl<T, R(A...)> | |||
{ using type = R(*)(A...); }; | |||
template <typename T, typename R, typename ...A> | |||
struct decay_impl<T, R(A..., ...)> | |||
{ using type = R(*)(A..., ...); }; | |||
/* common_type */ | |||
template<typename T, typename U> | |||
struct common_type_impl<T, U, decltype((void)(true ? std::declval<T>() : std::declval<U>()))> | |||
{ using type = decay<decltype((void)(true ? std::declval<T>() : std::declval<U>()))>; }; | |||
} | |||
} | |||
} |
@@ -0,0 +1,10 @@ | |||
#pragma once | |||
namespace utl { | |||
namespace mp { | |||
template<typename...> | |||
using void_t = void; | |||
} | |||
} |
@@ -0,0 +1,3 @@ | |||
#pragma once | |||
#include <cpputils/mp/intern/ebo.h> |
@@ -0,0 +1,39 @@ | |||
#pragma once | |||
#include <utility> | |||
#include <cpputils/mp/core.h> | |||
namespace utl { | |||
namespace mp { | |||
namespace intern { | |||
template<typename T, typename U = T, typename = void> | |||
struct comparable_equal | |||
: c_false | |||
{ }; | |||
template<typename T> | |||
struct comparable_equal<T, T, void_t< | |||
decltype(std::forward<T>(std::declval<T>()) == std::forward<T>(std::declval<T>()) ? 0 : 0), | |||
decltype(std::forward<T>(std::declval<T>()) != std::forward<T>(std::declval<T>()) ? 0 : 0)>> | |||
: c_true | |||
{ }; | |||
template <typename T, typename U> | |||
struct comparable_equal<T, U, enable_if_c< | |||
!is_same<T, U>::value, | |||
void_t< | |||
decltype(std::forward<T>(std::declval<T>()) == std::forward<U>(std::declval<U>()) ? 0 : 0), | |||
decltype(std::forward<U>(std::declval<U>()) == std::forward<T>(std::declval<T>()) ? 0 : 0), | |||
decltype(std::forward<T>(std::declval<T>()) != std::forward<U>(std::declval<U>()) ? 0 : 0), | |||
decltype(std::forward<U>(std::declval<U>()) != std::forward<T>(std::declval<T>()) ? 0 : 0), | |||
common_type<T, U>>>> | |||
: c_bool< | |||
comparable_equal<T>::value && | |||
comparable_equal<U>::value && | |||
comparable_equal<common_type<T, U>>::value> | |||
{ }; | |||
} | |||
} | |||
} |
@@ -0,0 +1,36 @@ | |||
#pragma once | |||
#include <utility> | |||
#include <cpputils/mp/core.h> | |||
namespace utl { | |||
namespace mp { | |||
namespace intern { | |||
template<typename T, typename U = T, typename = void> | |||
struct comparable_less | |||
: c_false | |||
{ }; | |||
template<typename T> | |||
struct comparable_less<T, T, void_t< | |||
decltype(std::forward<T>(std::declval<T>()) < std::forward<T>(std::declval<T>()) ? 0 : 0)>> | |||
: c_true | |||
{ }; | |||
template <typename T, typename U> | |||
struct comparable_less<T, U, enable_if_c< | |||
!is_same<T, U>::value, | |||
void_t< | |||
decltype(std::forward<T>(std::declval<T>()) < std::forward<U>(std::declval<U>()) ? 0 : 0), | |||
decltype(std::forward<U>(std::declval<U>()) < std::forward<T>(std::declval<T>()) ? 0 : 0), | |||
common_type<T, U>>>> | |||
: c_bool< | |||
comparable_less<T>::value && | |||
comparable_less<U>::value && | |||
comparable_less<common_type<T, U>>::value> | |||
{ }; | |||
} | |||
} | |||
} |
@@ -0,0 +1,83 @@ | |||
#pragma once | |||
#include "../core.h" | |||
namespace utl { | |||
namespace mp { | |||
namespace intern { | |||
template<typename Key, typename Value, bool = is_empty<Value>::value && !is_final<Value>::value> | |||
struct ebo; | |||
template<typename Key, typename Value> | |||
constexpr const Value& ebo_get(const ebo<Key, Value, true>& x) | |||
{ return x; } | |||
template<typename Key, typename Value> | |||
constexpr Value& ebo_get(ebo<Key, Value, true>& x) | |||
{ return x; } | |||
template<typename Key, typename Value> | |||
constexpr Value&& ebo_get(ebo<Key, Value, true>&& x) | |||
{ return x; } | |||
template<typename Key, typename Value> | |||
constexpr const Value& ebo_get(const ebo<Key, Value, false>& x) | |||
{ return x._data; } | |||
template<typename Key, typename Value> | |||
constexpr Value& ebo_get(ebo<Key, Value, false>& x) | |||
{ return x._data; } | |||
template<typename Key, typename Value> | |||
constexpr Value&& ebo_get(ebo<Key, Value, false>&& x) | |||
{ return static_cast<Value&&>(x._data); } | |||
template<typename Key, typename Value> | |||
struct ebo<Key, Value, true> : | |||
public Value | |||
{ | |||
constexpr ebo() | |||
{ } | |||
template<typename T> | |||
explicit constexpr ebo(T&& t) : | |||
Value(std::forward<T>(t)) | |||
{ } | |||
constexpr auto get() const& | |||
{ return ebo_get(*this); } | |||
constexpr auto get() & | |||
{ return ebo_get(*this); } | |||
constexpr auto get() && | |||
{ return ebo_get(std::move(*this)); } | |||
}; | |||
template<typename Key, typename Value> | |||
struct ebo<Key, Value, false> | |||
{ | |||
Value _data; | |||
constexpr ebo() | |||
{ } | |||
template<typename T> | |||
explicit constexpr ebo(T&& t) : | |||
_data(std::forward<T>(t)) | |||
{ } | |||
constexpr auto get() const& | |||
{ return ebo_get(*this); } | |||
constexpr auto get() & | |||
{ return ebo_get(*this); } | |||
constexpr auto get() && | |||
{ return ebo_get(std::move(*this)); } | |||
}; | |||
} | |||
} | |||
} |
@@ -0,0 +1,16 @@ | |||
#pragma once | |||
#include <cpputils/mp/operations/value.h> | |||
namespace utl { | |||
namespace mp { | |||
namespace intern { | |||
template<typename C> | |||
struct has_value | |||
: c_bool<!is_default<__impl::value_impl<tag_of<C>>>::value> | |||
{ }; | |||
} | |||
} | |||
} |
@@ -0,0 +1,36 @@ | |||
#pragma once | |||
#include <utility> | |||
#include <cpputils/mp/core/conditionals.h> | |||
#include <cpputils/mp/util/tag_of.h> | |||
#include <cpputils/mp/operations/compare/equal.h> | |||
#include <cpputils/mp/operations/compare/not_equal.h> | |||
namespace utl { | |||
namespace mp { | |||
namespace intern { | |||
template<typename T> | |||
struct operators_comparable | |||
{ static constexpr bool value = false; }; | |||
} | |||
namespace operators | |||
{ | |||
template<typename X, typename Y, typename = enable_if_c< | |||
intern::operators_comparable<tag_of<X>>::value && | |||
intern::operators_comparable<tag_of<Y>>::value>> | |||
constexpr auto operator == (X&& x, Y&& y) | |||
{ return equal(std::forward<X>(x), std::forward<Y>(y)); } | |||
template<typename X, typename Y, typename = enable_if_c< | |||
intern::operators_comparable<tag_of<X>>::value && | |||
intern::operators_comparable<tag_of<Y>>::value>> | |||
constexpr auto operator != (X&& x, Y&& y) | |||
{ return not_equal(std::forward<X>(x), std::forward<Y>(y)); } | |||
} | |||
} | |||
} |
@@ -0,0 +1,50 @@ | |||
#pragma once | |||
#include <utility> | |||
#include <cpputils/mp/core/conditionals.h> | |||
#include <cpputils/mp/util/tag_of.h> | |||
#include <cpputils/mp/operations/compare/less.h> | |||
#include <cpputils/mp/operations/compare/less_equal.h> | |||
#include <cpputils/mp/operations/compare/greater.h> | |||
#include <cpputils/mp/operations/compare/greater_equal.h> | |||
namespace utl { | |||
namespace mp { | |||
namespace intern { | |||
template<typename T> | |||
struct operators_orderable | |||
{ static constexpr bool value = false; }; | |||
} | |||
namespace operators | |||
{ | |||
template<typename X, typename Y, typename = enable_if_c< | |||
intern::operators_orderable<tag_of<X>>::value && | |||
intern::operators_orderable<tag_of<Y>>::value>> | |||
constexpr auto operator < (X&& x, Y&& y) | |||
{ return less(std::forward<X>(x), std::forward<Y>(y)); } | |||
template<typename X, typename Y, typename = enable_if_c< | |||
intern::operators_orderable<tag_of<X>>::value && | |||
intern::operators_orderable<tag_of<Y>>::value>> | |||
constexpr auto operator <= (X&& x, Y&& y) | |||
{ return less_equal(std::forward<X>(x), std::forward<Y>(y)); } | |||
template<typename X, typename Y, typename = enable_if_c< | |||
intern::operators_orderable<tag_of<X>>::value && | |||
intern::operators_orderable<tag_of<Y>>::value>> | |||
constexpr auto operator > (X&& x, Y&& y) | |||
{ return greater(std::forward<X>(x), std::forward<Y>(y)); } | |||
template<typename X, typename Y, typename = enable_if_c< | |||
intern::operators_orderable<tag_of<X>>::value && | |||
intern::operators_orderable<tag_of<Y>>::value>> | |||
constexpr auto operator >= (X&& x, Y&& y) | |||
{ return greater_equal(std::forward<X>(x), std::forward<Y>(y)); } | |||
} | |||
} | |||
} |
@@ -0,0 +1,6 @@ | |||
#pragma once | |||
#include <cpputils/mp/operations/first.h> | |||
#include <cpputils/mp/operations/second.h> | |||
#include <cpputils/mp/operations/compare.h> | |||
#include <cpputils/mp/operations/logical.h> |
@@ -0,0 +1,4 @@ | |||
#pragma once | |||
#include <cpputils/mp/operations/compare/equal.h> | |||
#include <cpputils/mp/operations/compare/not_equal.h> |
@@ -0,0 +1,73 @@ | |||
#pragma once | |||
#include <cpputils/mp/core/const.h> | |||
#include <cpputils/mp/util/when.h> | |||
#include <cpputils/mp/util/tag_of.h> | |||
#include <cpputils/mp/util/default.h> | |||
#include <cpputils/mp/intern/has_value.h> | |||
#include <cpputils/mp/intern/comparable_equal.h> | |||
#include <cpputils/mp/operations/if.h> | |||
#include <cpputils/mp/operations/value.h> | |||
namespace utl { | |||
namespace mp { | |||
namespace __impl | |||
{ | |||
struct equal_t | |||
{ | |||
template<typename L, typename R> | |||
constexpr auto operator()(const L& l, const R& r) const; | |||
}; | |||
} | |||
constexpr __impl::equal_t equal { }; | |||
namespace __impl | |||
{ | |||
template <typename L, typename R, typename = void> | |||
struct equal_impl | |||
: equal_impl<L, R, when<true>> | |||
{ }; | |||
template <typename L, typename R, bool condition> | |||
struct equal_impl<L, R, when<condition>> | |||
: default_ | |||
{ | |||
template <typename ...Args> | |||
static constexpr auto apply(Args&& ...) | |||
{ return c_false { }; } | |||
}; | |||
template <typename T, typename U> | |||
struct equal_impl<T, U, when<intern::comparable_equal<T, U>::value>> | |||
{ | |||
template <typename X, typename Y> | |||
static constexpr auto apply(X&& x, Y&& y) | |||
{ return static_cast<X&&>(x) == static_cast<Y&&>(y); } | |||
}; | |||
template <typename C> | |||
struct equal_impl<C, C, when<intern::has_value<C>::value>> | |||
{ | |||
template <typename X, typename Y> | |||
static constexpr auto apply(const X&, const Y&) | |||
{ | |||
constexpr auto eq = equal(value<X>(), value<Y>()); | |||
constexpr bool truth_value = if_ (eq, true, false); | |||
return c_bool<truth_value> { }; | |||
} | |||
}; | |||
template<typename L, typename R> | |||
constexpr auto equal_t::operator()(const L& l, const R& r) const | |||
{ | |||
using l_tag_type = tag_of<L>; | |||
using r_tag_type = tag_of<R>; | |||
using equal_type = equal_impl<l_tag_type, r_tag_type>; | |||
return equal_type::apply(l, r); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,51 @@ | |||
#pragma once | |||
#include <cpputils/mp/core/const.h> | |||
#include <cpputils/mp/util/when.h> | |||
#include <cpputils/mp/util/tag_of.h> | |||
#include <cpputils/mp/util/default.h> | |||
#include <cpputils/mp/operations/logical/not.h> | |||
#include <cpputils/mp/operations/compare/less.h> | |||
namespace utl { | |||
namespace mp { | |||
namespace __impl | |||
{ | |||
struct greater_t | |||
{ | |||
template<typename L, typename R> | |||
constexpr auto operator()(const L& l, const R& r) const; | |||
}; | |||
} | |||
constexpr __impl::greater_t greater { }; | |||
namespace __impl | |||
{ | |||
template <typename L, typename R, typename = void> | |||
struct greater_impl | |||
: greater_impl<L, R, when<true>> | |||
{ }; | |||
template <typename T, typename U, bool condition> | |||
struct greater_impl<T, U, when<condition>> | |||
: default_ | |||
{ | |||
template <typename X, typename Y> | |||
static constexpr auto apply(X&& x, Y&& y) | |||
{ return less(static_cast<Y&&>(y), static_cast<X&&>(x)); } | |||
}; | |||
template<typename L, typename R> | |||
constexpr auto greater_t::operator()(const L& l, const R& r) const | |||
{ | |||
using l_tag_type = tag_of<L>; | |||
using r_tag_type = tag_of<R>; | |||
using greater_impl_type = greater_impl<l_tag_type, r_tag_type>; | |||
return greater_impl_type::apply(l, r); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,51 @@ | |||
#pragma once | |||
#include <cpputils/mp/core/const.h> | |||
#include <cpputils/mp/util/when.h> | |||
#include <cpputils/mp/util/tag_of.h> | |||
#include <cpputils/mp/util/default.h> | |||
#include <cpputils/mp/operations/logical/not.h> | |||
#include <cpputils/mp/operations/compare/less.h> | |||
namespace utl { | |||
namespace mp { | |||
namespace __impl | |||
{ | |||
struct greater_equal_t | |||
{ | |||
template<typename L, typename R> | |||
constexpr auto operator()(const L& l, const R& r) const; | |||
}; | |||
} | |||
constexpr __impl::greater_equal_t greater_equal { }; | |||
namespace __impl | |||
{ | |||
template <typename L, typename R, typename = void> | |||
struct greater_equal_impl | |||
: greater_equal_impl<L, R, when<true>> | |||
{ }; | |||
template <typename T, typename U, bool condition> | |||
struct greater_equal_impl<T, U, when<condition>> | |||
: default_ | |||
{ | |||
template <typename X, typename Y> | |||
static constexpr auto apply(X&& x, Y&& y) | |||
{ return not_(less(static_cast<X&&>(x), static_cast<Y&&>(y))); } | |||
}; | |||
template<typename L, typename R> | |||
constexpr auto greater_equal_t::operator()(const L& l, const R& r) const | |||
{ | |||
using l_tag_type = tag_of<L>; | |||
using r_tag_type = tag_of<R>; | |||
using greater_equal_impl_type = greater_equal_impl<l_tag_type, r_tag_type>; | |||
return greater_equal_impl_type::apply(l, r); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,73 @@ | |||
#pragma once | |||
#include <cpputils/mp/core/const.h> | |||
#include <cpputils/mp/util/when.h> | |||
#include <cpputils/mp/util/tag_of.h> | |||
#include <cpputils/mp/util/default.h> | |||
#include <cpputils/mp/intern/has_value.h> | |||
#include <cpputils/mp/intern/comparable_less.h> | |||
#include <cpputils/mp/operations/if.h> | |||
#include <cpputils/mp/operations/value.h> | |||
namespace utl { | |||
namespace mp { | |||
namespace __impl | |||
{ | |||
struct less_t | |||
{ | |||
template<typename L, typename R> | |||
constexpr auto operator()(const L& l, const R& r) const; | |||
}; | |||
} | |||
constexpr __impl::less_t less { }; | |||
namespace __impl | |||
{ | |||
template <typename L, typename R, typename = void> | |||
struct less_impl | |||
: less_impl<L, R, when<true>> | |||
{ }; | |||
template <typename L, typename R, bool condition> | |||
struct less_impl<L, R, when<condition>> | |||
: default_ | |||
{ | |||
template <typename ...Args> | |||
static constexpr auto apply(Args&& ...) | |||
{ return c_false { }; } | |||
}; | |||
template <typename T, typename U> | |||
struct less_impl<T, U, when<intern::comparable_less<T, U>::value>> | |||
{ | |||
template <typename X, typename Y> | |||
static constexpr auto apply(X&& x, Y&& y) | |||
{ return static_cast<X&&>(x) < static_cast<Y&&>(y); } | |||
}; | |||
template <typename C> | |||
struct less_impl<C, C, when<intern::has_value<C>::value>> | |||
{ | |||
template <typename X, typename Y> | |||
static constexpr auto apply(const X&, const Y&) | |||
{ | |||
constexpr auto eq = less(value<X>(), value<Y>()); | |||
constexpr bool truth_value = if_ (eq, true, false); | |||
return c_bool<truth_value> { }; | |||
} | |||
}; | |||
template<typename L, typename R> | |||
constexpr auto less_t::operator()(const L& l, const R& r) const | |||
{ | |||
using l_tag_type = tag_of<L>; | |||
using r_tag_type = tag_of<R>; | |||
using less_type = less_impl<l_tag_type, r_tag_type>; | |||
return less_type::apply(l, r); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,51 @@ | |||
#pragma once | |||
#include <cpputils/mp/core/const.h> | |||
#include <cpputils/mp/util/when.h> | |||
#include <cpputils/mp/util/tag_of.h> | |||
#include <cpputils/mp/util/default.h> | |||
#include <cpputils/mp/operations/logical/not.h> | |||
#include <cpputils/mp/operations/compare/less.h> | |||
namespace utl { | |||
namespace mp { | |||
namespace __impl | |||
{ | |||
struct less_equal_t | |||
{ | |||
template<typename L, typename R> | |||
constexpr auto operator()(const L& l, const R& r) const; | |||
}; | |||
} | |||
constexpr __impl::less_equal_t less_equal { }; | |||
namespace __impl | |||
{ | |||
template <typename L, typename R, typename = void> | |||
struct less_equal_impl | |||
: less_equal_impl<L, R, when<true>> | |||
{ }; | |||
template <typename T, typename U, bool condition> | |||
struct less_equal_impl<T, U, when<condition>> | |||
: default_ | |||
{ | |||
template <typename X, typename Y> | |||
static constexpr auto apply(X&& x, Y&& y) | |||
{ return not_(less(static_cast<Y&&>(y), static_cast<X&&>(x))); } | |||
}; | |||
template<typename L, typename R> | |||
constexpr auto less_equal_t::operator()(const L& l, const R& r) const | |||
{ | |||
using l_tag_type = tag_of<L>; | |||
using r_tag_type = tag_of<R>; | |||
using less_equal_impl_type = less_equal_impl<l_tag_type, r_tag_type>; | |||
return less_equal_impl_type::apply(l, r); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,51 @@ | |||
#pragma once | |||
#include <cpputils/mp/core/const.h> | |||
#include <cpputils/mp/util/when.h> | |||
#include <cpputils/mp/util/tag_of.h> | |||
#include <cpputils/mp/util/default.h> | |||
#include <cpputils/mp/operations/logical/not.h> | |||
#include <cpputils/mp/operations/compare/not_equal.h> | |||
namespace utl { | |||
namespace mp { | |||
namespace __impl | |||
{ | |||
struct not_equal_t | |||
{ | |||
template<typename L, typename R> | |||
constexpr auto operator()(const L& l, const R& r) const; | |||
}; | |||
} | |||
constexpr __impl::not_equal_t not_equal { }; | |||
namespace __impl | |||
{ | |||
template <typename L, typename R, typename = void> | |||
struct not_equal_impl | |||
: not_equal_impl<L, R, when<true>> | |||
{ }; | |||
template <typename L, typename R, bool condition> | |||
struct not_equal_impl<L, R, when<condition>> | |||
: default_ | |||
{ | |||
template <typename X, typename Y> | |||
static constexpr auto apply(X&& x, Y&& y) | |||
{ return not(equal(std::forward<X>(x), std::forward<Y>(y))); } | |||
}; | |||
template<typename L, typename R> | |||
constexpr auto not_equal_t::operator()(const L& l, const R& r) const | |||
{ | |||
using l_tag_type = tag_of<L>; | |||
using r_tag_type = tag_of<R>; | |||
using not_equal_type = not_equal_impl<l_tag_type, r_tag_type>; | |||
return not_equal_type::apply(l, r); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,59 @@ | |||
#pragma once | |||
#include <utility> | |||
#include <cpputils/mp/util/when.h> | |||
#include <cpputils/mp/util/tag_of.h> | |||
#include <cpputils/mp/util/default.h> | |||
namespace utl { | |||
namespace mp { | |||
namespace __impl | |||
{ | |||
struct eval_t | |||
{ | |||
template <typename Expr> | |||
constexpr auto operator()(Expr&& expr) const; | |||
}; | |||
} | |||
constexpr __impl::eval_t eval { }; | |||
namespace __impl | |||
{ | |||
template <typename T, typename = void> | |||
struct eval_impl | |||
: eval_impl<T, when<true>> | |||
{ }; | |||
template <typename T, bool condition> | |||
struct eval_impl<T, when<condition>> | |||
: default_ | |||
{ | |||
template <typename Expr> | |||
static constexpr auto eval_helper(Expr&& expr, int) | |||
{ return static_cast<Expr&&>(expr)(); } | |||
template <typename Expr> | |||
static constexpr auto eval_helper(Expr&&, ...) | |||
{ | |||
static_assert(wrong<Expr> { }, | |||
"eval(expr) requires the expression to be a a nullary Callable"); | |||
} | |||
template <typename Expr> | |||
static constexpr decltype(auto) apply(Expr&& expr) | |||
{ return eval_helper(static_cast<Expr&&>(expr), int { }); } | |||
}; | |||
template <typename Expr> | |||
constexpr auto eval_t::operator()(Expr&& expr) const | |||
{ | |||
using tag_type = tag_of<Expr>; | |||
using eval_impl_type = eval_impl<tag_type>; | |||
return eval_impl_type::apply(std::forward<Expr>(expr));; | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,80 @@ | |||
#pragma once | |||
#include <cpputils/mp/util/when.h> | |||
#include <cpputils/mp/util/tag_of.h> | |||
#include <cpputils/mp/util/default.h> | |||
#include <cpputils/mp/intern/has_value.h> | |||
#include <cpputils/mp/operations/if.fwd.h> | |||
#include <cpputils/mp/operations/eval.h> | |||
namespace utl { | |||
namespace mp { | |||
namespace __impl | |||
{ | |||
struct eval_if_t | |||
{ | |||
template <typename Cond, typename Then, typename Else> | |||
constexpr auto operator()(Cond&& c, Then&& t, Else&& e) const; | |||
}; | |||
} | |||
constexpr __impl::eval_if_t eval_if { }; | |||
namespace __impl | |||
{ | |||
template <typename T, typename = void> | |||
struct eval_if_impl | |||
: eval_if_impl<T, when<true>> | |||
{ }; | |||
template <typename T, bool condition> | |||
struct eval_if_impl<T, when<condition>> | |||
: default_ | |||
{ | |||
template <typename ...Args> | |||
static constexpr auto apply(Args&& ...) = delete; | |||
}; | |||
template <typename T> | |||
struct eval_if_impl<T, when<is_arithmetic<T>::value>> | |||
{ | |||
template <typename Cond, typename Then, typename Else> | |||
static constexpr auto apply(const Cond& cond, Then&& t, Else&& e) | |||
{ | |||
return cond ? eval(std::forward<Then>(t)) | |||
: eval(std::forward<Else>(e)); | |||
} | |||
}; | |||
template <typename T> | |||
struct eval_if_impl<T, when<intern::has_value<T>::value>> | |||
{ | |||
template<typename Then, typename Else> | |||
static constexpr auto eval_if_helper(c_true, Then&& t, Else&& e) | |||
{ return eval(std::forward<Then>(t)); } | |||
template<typename Then, typename Else> | |||
static constexpr auto eval_if_helper(c_false, Then&& t, Else&& e) | |||
{ return eval(std::forward<Else>(e)); } | |||
template <typename Cond, typename Then, typename Else> | |||
static constexpr auto apply(const Cond&, Then&& t, Else&& e) | |||
{ | |||
constexpr auto cond = value<Cond>(); | |||
constexpr bool truth_value = if_(cond, true, false); | |||
return eval_if_helper(c_bool<truth_value> { }, std::forward<Then>(t), std::forward<Else>(e)); | |||
} | |||
}; | |||
template <typename Cond, typename Then, typename Else> | |||
constexpr auto eval_if_t::operator()(Cond&& c, Then&& t, Else&& e) const | |||
{ | |||
using tag_type = tag_of<Cond>; | |||
using eval_if_type = eval_if_impl<tag_type>; | |||
return eval_if_type::apply(std::forward<Cond>(c), std::forward<Then>(t), std::forward<Else>(e)); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,46 @@ | |||
#pragma once | |||
#include <cpputils/mp/util/when.h> | |||
#include <cpputils/mp/util/tag_of.h> | |||
#include <cpputils/mp/util/default.h> | |||
namespace utl { | |||
namespace mp { | |||
namespace __impl | |||
{ | |||
struct first_t | |||
{ | |||
template<typename T> | |||
constexpr auto operator()(T&& t) const; | |||
}; | |||
} | |||
constexpr __impl::first_t first { }; | |||
namespace __impl | |||
{ | |||
template <typename T, typename = void> | |||
struct first_impl | |||
: first_impl<T, when<true>> | |||
{ }; | |||
template <typename T, bool condition> | |||
struct first_impl<T, when<condition>> | |||
: default_ | |||
{ | |||
template <typename ...Args> | |||
static constexpr auto apply(Args&& ...) = delete; | |||
}; | |||
template<typename T> | |||
constexpr auto first_t::operator()(T&& t) const | |||
{ | |||
using tag_type = tag_of<T>; | |||
using first_type = first_impl<tag_type>; | |||
return first_type::apply(std::forward<T>(t)); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,18 @@ | |||
#pragma once | |||
namespace utl { | |||
namespace mp { | |||
namespace __impl | |||
{ | |||
struct if_t | |||
{ | |||
template <typename Cond, typename Then, typename Else> | |||
constexpr auto operator()(Cond&& c, Then&& t, Else&& e) const; | |||
}; | |||
} | |||
constexpr __impl::if_t if_ { }; | |||
} | |||
} |
@@ -0,0 +1,52 @@ | |||
#pragma once | |||
#include <cpputils/mp/operations/if.fwd.h> | |||
#include <cpputils/mp/util/when.h> | |||
#include <cpputils/mp/util/tag_of.h> | |||
#include <cpputils/mp/util/default.h> | |||
#include <cpputils/mp/operations/eval_if.h> | |||
namespace utl { | |||
namespace mp { | |||
namespace __impl | |||
{ | |||
template <typename T> | |||
struct hold { | |||
T value; | |||
constexpr T&& operator()() && | |||
{ return static_cast<T&&>(value); } | |||
}; | |||
template <typename L, typename = void> | |||
struct if_impl | |||
: if_impl<L, when<true>> | |||
{ }; | |||
template <typename L, bool condition> | |||
struct if_impl<L, when<condition>> | |||
: default_ | |||
{ | |||
template <typename Cond, typename Then, typename Else> | |||
static constexpr auto apply(Cond&& c, Then&& t, Else&& e) | |||
{ | |||
return eval_if( | |||
std::forward<Cond>(c), | |||
hold<Then&&> { static_cast<Then&&>(t) }, | |||
hold<Else&&> { static_cast<Else&&>(e) } | |||
); | |||
} | |||
}; | |||
template <typename Cond, typename Then, typename Else> | |||
constexpr auto if_t::operator()(Cond&& c, Then&& t, Else&& e) const | |||
{ | |||
using tag_type = tag_of<Cond>; | |||
using if_type = if_impl<tag_type>; | |||
return if_type::apply(std::forward<Cond>(c), std::forward<Then>(t), std::forward<Else>(e)); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,4 @@ | |||
#pragma once | |||
#include <cpputils/mp/operations/logical/not.h> | |||
#include <cpputils/mp/operations/logical/and.h> |
@@ -0,0 +1,63 @@ | |||
#pragma once | |||
#include <cpputils/mp/core/const.h> | |||
#include <cpputils/mp/util/when.h> | |||
#include <cpputils/mp/util/tag_of.h> | |||
#include <cpputils/mp/util/default.h> | |||
#include <cpputils/mp/operations/if.h> | |||
namespace utl { | |||
namespace mp { | |||
namespace __impl | |||
{ | |||
struct and_t | |||
{ | |||
template<typename X, typename Y> | |||
constexpr auto operator()(X&& x, Y&& y) const; | |||
template<typename X, typename... Y> | |||
constexpr auto operator()(X&& x, Y&&... y) const; | |||
}; | |||
} | |||
constexpr __impl::and_t and_ { }; | |||
namespace __impl | |||
{ | |||
template <typename T, typename = void> | |||
struct and_impl | |||
: and_impl<T, when<true>> | |||
{ }; | |||
template <typename T, bool condition> | |||
struct and_impl<T, when<condition>> | |||
: default_ | |||
{ | |||
template <typename X, typename Y> | |||
static constexpr auto apply(X&& x, Y&& y) | |||
{ return if_(x, static_cast<Y&&>(y), x); } | |||
}; | |||
template <typename X, typename Y> | |||
constexpr auto and_t::operator()(X&& x, Y&& y) const | |||
{ | |||
using tag_type = tag_of<X>; | |||
using and_impl_type = and_impl<tag_type>; | |||
return and_impl_type::apply(std::forward<X>(x), std::forward<Y>(y)); | |||
}; | |||
/* TODO | |||
template <typename X, typename ...Y> | |||
constexpr auto and_t::operator()(X&& x, Y&& ...y) const | |||
{ | |||
return detail::variadic::foldl1( | |||
*this, | |||
static_cast<X&&>(x), | |||
static_cast<Y&&>(y)... | |||
); | |||
} | |||
*/ | |||
} | |||
} | |||
} |
@@ -0,0 +1,55 @@ | |||
#pragma once | |||
#include <cpputils/mp/core/const.h> | |||
#include <cpputils/mp/util/when.h> | |||
#include <cpputils/mp/util/tag_of.h> | |||
#include <cpputils/mp/util/default.h> | |||
namespace utl { | |||
namespace mp { | |||
namespace __impl | |||
{ | |||
struct not_t | |||
{ | |||
template<typename X> | |||
constexpr auto operator()(X&& x) const; | |||
}; | |||
} | |||
constexpr __impl::not_t not_ { }; | |||
namespace __impl | |||
{ | |||
template <typename T, typename = void> | |||
struct not_impl | |||
: not_impl<T, when<true>> | |||
{ }; | |||
template <typename T, bool condition> | |||
struct not_impl<T, when<condition>> | |||
: default_ | |||
{ | |||
template <typename ...Args> | |||
static constexpr auto apply(Args&& ...) = delete; | |||
}; | |||
template <typename T> | |||
struct not_impl<T, when<is_arithmetic<T>::value>> | |||
{ | |||
template <typename X> | |||
static constexpr auto apply(const X& x) | |||
{ return x ? false : true; } | |||
}; | |||
template <typename X> | |||
constexpr auto not_t::operator()(X&& x) const | |||
{ | |||
using tag_type = tag_of<X>; | |||
using not_impl_type = not_impl<tag_type>; | |||
return not_impl_type::apply(std::forward<X>(x)); | |||
}; | |||
} | |||
} | |||
} |
@@ -0,0 +1,63 @@ | |||
#pragma once | |||
#include <cpputils/mp/core/const.h> | |||
#include <cpputils/mp/util/when.h> | |||
#include <cpputils/mp/util/tag_of.h> | |||
#include <cpputils/mp/util/default.h> | |||
#include <cpputils/mp/operations/if.h> | |||
namespace utl { | |||
namespace mp { | |||
namespace __impl | |||
{ | |||
struct or_t | |||
{ | |||
template<typename X, typename Y> | |||
constexpr auto operator()(X&& x, Y&& y) const; | |||
template<typename X, typename... Y> | |||
constexpr auto operator()(X&& x, Y&&... y) const; | |||
}; | |||
} | |||
constexpr __impl::or_t or_ { }; | |||
namespace __impl | |||
{ | |||
template <typename T, typename = void> | |||
struct or_impl | |||
: or_impl<T, when<true>> | |||
{ }; | |||
template <typename T, bool condition> | |||
struct or_impl<T, when<condition>> | |||
: default_ | |||
{ | |||
template <typename X, typename Y> | |||
static constexpr auto apply(X&& x, Y&& y) | |||
{ return if_(x, x, static_cast<Y&&>(y)); } | |||
}; | |||
template <typename X, typename Y> | |||
constexpr auto or_t::operator()(X&& x, Y&& y) const | |||
{ | |||
using tag_type = tag_of<X>; | |||
using or_impl_type = or_impl<tag_type>; | |||
return or_impl_type::apply(std::forward<X>(x), std::forward<Y>(y)); | |||
}; | |||
/* TODO | |||
template <typename X, typename ...Y> | |||
constexpr auto or_t::operator()(X&& x, Y&& ...y) const | |||
{ | |||
return detail::variadic::foldl1( | |||
*this, | |||
static_cast<X&&>(x), | |||
static_cast<Y&&>(y)... | |||
); | |||
} | |||
*/ | |||
} | |||
} | |||
} |
@@ -0,0 +1,46 @@ | |||
#pragma once | |||
#include <cpputils/mp/util/when.h> | |||
#include <cpputils/mp/util/tag_of.h> | |||
#include <cpputils/mp/util/default.h> | |||
namespace utl { | |||
namespace mp { | |||
namespace __impl | |||
{ | |||
struct second_t | |||
{ | |||
template<typename T> | |||
constexpr auto operator()(T&& t) const; | |||
}; | |||
} | |||
constexpr __impl::second_t second { }; | |||
namespace __impl | |||
{ | |||
template <typename T, typename = void> | |||
struct second_impl | |||
: second_impl<T, when<true>> | |||
{ }; | |||
template <typename T, bool condition> | |||
struct second_impl<T, when<condition>> | |||
: default_ | |||
{ | |||
template <typename ...Args> | |||
static constexpr auto apply(Args&& ...) = delete; | |||
}; | |||
template<typename T> | |||
constexpr auto second_t::operator()(T&& t) const | |||
{ | |||
using tag = tag_of<T>; | |||
using second = second_impl<tag>; | |||
return second::apply(std::forward<T>(t)); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,54 @@ | |||
#pragma once | |||
#include <cpputils/mp/util/when.h> | |||
#include <cpputils/mp/util/tag_of.h> | |||
#include <cpputils/mp/util/default.h> | |||
namespace utl { | |||
namespace mp { | |||
namespace __impl | |||
{ | |||
template<typename T, typename = void> | |||
struct value_impl; | |||
} | |||
template <typename T> | |||
constexpr auto value() | |||
{ | |||
using t_clean_type = clean_type<T>; | |||
using tag_type = tag_of<T>; | |||
using value_impl_type = __impl::value_impl<tag_type>; | |||
return value_impl_type::template apply<t_clean_type>(); | |||
} | |||
template <typename T> | |||
constexpr decltype(auto) value(T const&) | |||
{ return value<T>(); } | |||
namespace __impl | |||
{ | |||
template <typename T, typename> | |||
struct value_impl | |||
: value_impl<T, when<true>> | |||
{ }; | |||
template <typename T, bool condition> | |||
struct value_impl<T, when<condition>> | |||
: default_ | |||
{ | |||
template <typename ...Args> | |||
static constexpr auto apply(Args&& ...args) = delete; | |||
}; | |||
template <typename T> | |||
struct value_impl<tag_const<T>> | |||
{ | |||
template <typename C> | |||
static constexpr auto apply() | |||
{ return C::value; } | |||
}; | |||
} | |||
} | |||
} |
@@ -1,151 +1,8 @@ | |||
#include "core.h" | |||
namespace utl { | |||
namespace mp { | |||
namespace __impl /* forward declaration */ | |||
{ | |||
/* when */ | |||
template<typename...> | |||
struct is_valid_impl; | |||
template<typename...> | |||
struct wrong_impl; | |||
/* default */ | |||
template<typename T, typename = void> | |||
struct is_default_impl; | |||
/* tag_of */ | |||
template<typename T, typename = void> | |||
struct tag_of_impl; | |||
/* is_a */ | |||
template<typename Tag, typename ...T> | |||
struct is_a_impl; | |||
/* make */ | |||
template<typename Tag> | |||
struct make_t; | |||
} | |||
/* when */ | |||
template<typename... T> | |||
using is_valid = __impl::is_valid_impl<T...>; | |||
template<typename... T> | |||
using wrong = __impl::wrong_impl<T...>; | |||
template<bool condition> | |||
struct when; | |||
template<typename... T> | |||
using when_valid = when<is_valid<T...>::value>; | |||
/* default */ | |||
template<typename T> | |||
using is_default = __impl::is_default_impl<T>; | |||
/* tag_of */ | |||
template<typename T> | |||
using tag_of = typename __impl::tag_of_impl<T>::type; | |||
/* is_a */ | |||
template<typename Tag, typename... T> | |||
constexpr __impl::is_a_impl<Tag, T...> is_a { }; | |||
template<typename Tag, typename... T> | |||
constexpr __impl::is_a_impl<Tag, T...> is_an { }; | |||
/* make */ | |||
template<typename Tag> | |||
constexpr __impl::make_t<Tag> make { }; | |||
namespace __impl /* implementation */ | |||
{ | |||
/* when */ | |||
template<typename...> | |||
struct is_valid_impl : | |||
public c_true | |||
{ }; | |||
template<typename...> | |||
struct wrong_impl : | |||
public c_false | |||
{ }; | |||
/* default */ | |||
struct default_ { }; | |||
template<typename T, typename> | |||
struct is_default_impl : | |||
public c_false | |||
{ }; | |||
template<typename T> | |||
struct is_default_impl<T, decltype((void)static_cast<default_>(std::declval<T>()))> : | |||
public c_true | |||
{ }; | |||
/* tag_of */ | |||
template<typename T, typename> | |||
struct tag_of_impl : | |||
public tag_of_impl<T, when<true>> | |||
{ }; | |||
template<typename T, bool condition> | |||
struct tag_of_impl<T, when<condition>> | |||
{ using type = T; }; | |||
template<typename T> | |||
struct tag_of_impl<T, when_valid<typename clean_type<T>::tag>> | |||
{ using type = typename clean_type<T>::tag; }; | |||
/* is_a */ | |||
template<typename Tag, typename T> | |||
struct is_a_impl<Tag, T> : | |||
public is_same<Tag, tag_of<T>> | |||
{ }; | |||
template<typename Tag> | |||
struct is_a_impl<Tag> | |||
{ | |||
template<typename T> | |||
constexpr auto operator()(const T&) const noexcept | |||
{ return is_a<Tag, T>; } | |||
}; | |||
/* make */ | |||
template<typename T, typename = void> | |||
struct make_impl : | |||
public make_impl<T, when<true>> | |||
{ }; | |||
template<typename T, bool condition> | |||
struct make_impl<T, when<condition>> : | |||
public default_ | |||
{ | |||
template<typename... X> | |||
static constexpr auto make_helper(int, X&&... x) | |||
-> decltype(T(std::forward<X>(x)...)) | |||
{ return T(std::forward<X>(x)...); } | |||
template<typename... X> | |||
static constexpr auto make_helper(long, X&&... x) | |||
{ static_assert((sizeof...(X), false), "there exists no constructor for the given data type"); } | |||
template <typename... X> | |||
static constexpr auto apply(X&& ...x) | |||
{ return make_helper(int{}, std::forward<X>(x)...); } | |||
}; | |||
template<typename T> | |||
struct make_t | |||
{ | |||
template<typename... X> | |||
constexpr auto operator()(X&&... x) const noexcept | |||
{ return make_impl<T>::apply(std::forward<X>(x)...); } | |||
}; | |||
} | |||
} | |||
} | |||
#pragma once | |||
#include <cpputils/mp/util/default.h> | |||
#include <cpputils/mp/util/is_a.h> | |||
#include <cpputils/mp/util/make.h> | |||
#include <cpputils/mp/util/tag_of.h> | |||
#include <cpputils/mp/util/when.h> | |||
#include <cpputils/mp/util/to.h> |
@@ -0,0 +1,33 @@ | |||
#pragma once | |||
#include <cpputils/mp/core/const.h> | |||
namespace utl { | |||
namespace mp { | |||
namespace __impl /* forward declaration */ | |||
{ | |||
template<typename T, typename = void> | |||
struct is_default_impl; | |||
} | |||
template<typename T> | |||
using is_default = __impl::is_default_impl<T>; | |||
namespace __impl /* implementation */ | |||
{ | |||
struct default_ { }; | |||
template<typename T, typename> | |||
struct is_default_impl : | |||
public c_false | |||
{ }; | |||
template<typename T> | |||
struct is_default_impl<T, decltype((void)static_cast<default_>(std::declval<T>()))> : | |||
public c_true | |||
{ }; | |||
} | |||
} | |||
} |
@@ -0,0 +1,38 @@ | |||
#pragma once | |||
#include <cpputils/mp/util/when.h> | |||
#include <cpputils/mp/util/tag_of.h> | |||
namespace utl { | |||
namespace mp { | |||
namespace __impl /* forward declaration */ | |||
{ | |||
template<typename Tag, typename ...T> | |||
struct is_a_impl; | |||
} | |||
template<typename Tag, typename... T> | |||
constexpr __impl::is_a_impl<Tag, T...> is_a { }; | |||
template<typename Tag, typename... T> | |||
constexpr __impl::is_a_impl<Tag, T...> is_an { }; | |||
namespace __impl /* implementation */ | |||
{ | |||
template<typename Tag, typename T> | |||
struct is_a_impl<Tag, T> : | |||
public is_same<Tag, tag_of<T>> | |||
{ }; | |||
template<typename Tag> | |||
struct is_a_impl<Tag> | |||
{ | |||
template<typename T> | |||
constexpr auto operator()(const T&) const noexcept | |||
{ return is_a<Tag, T>; } | |||
}; | |||
} | |||
} | |||
} |
@@ -0,0 +1,55 @@ | |||
#pragma once | |||
#include <utility> | |||
#include <cpputils/mp/util/when.h> | |||
#include <cpputils/mp/util/default.h> | |||
namespace utl { | |||
namespace mp { | |||
namespace __impl /* forward declaration */ | |||
{ | |||
template<typename Tag> | |||
struct make_t; | |||
} | |||
template<typename Tag> | |||
constexpr __impl::make_t<Tag> make { }; | |||
namespace __impl /* implementation */ | |||
{ | |||
template<typename T, typename = void> | |||
struct make_impl : | |||
public make_impl<T, when<true>> | |||
{ }; | |||
template<typename T, bool condition> | |||
struct make_impl<T, when<condition>> : | |||
public default_ | |||
{ | |||
template<typename... X> | |||
static constexpr auto make_helper(int, X&&... x) | |||
-> decltype(T(std::forward<X>(x)...)) | |||
{ return T(std::forward<X>(x)...); } | |||
template<typename... X> | |||
static constexpr auto make_helper(long, X&&... x) | |||
{ static_assert((sizeof...(X), false), "there exists no constructor for the given data type"); } | |||
template <typename... X> | |||
static constexpr auto apply(X&& ...x) | |||
{ return make_helper(int{}, std::forward<X>(x)...); } | |||
}; | |||
template<typename T> | |||
struct make_t | |||
{ | |||
template<typename... X> | |||
constexpr auto operator()(X&&... x) const noexcept | |||
{ return make_impl<T>::apply(std::forward<X>(x)...); } | |||
}; | |||
} | |||
} | |||
} |
@@ -0,0 +1,35 @@ | |||
#pragma once | |||
#include <cpputils/mp/core/modifier.h> | |||
#include <cpputils/mp/util/when.h> | |||
namespace utl { | |||
namespace mp { | |||
namespace __impl /* forward declaration */ | |||
{ | |||
template<typename T, typename = void> | |||
struct tag_of_impl; | |||
} | |||
template<typename T> | |||
using tag_of = typename __impl::tag_of_impl<clean_type<T>>::type; | |||
namespace __impl /* implementation */ | |||
{ | |||
template<typename T, typename> | |||
struct tag_of_impl : | |||
public tag_of_impl<T, when<true>> | |||
{ }; | |||
template<typename T, bool condition> | |||
struct tag_of_impl<T, when<condition>> | |||
{ using type = T; }; | |||
template<typename T> | |||
struct tag_of_impl<T, when_valid<typename clean_type<T>::tag>> | |||
{ using type = typename clean_type<T>::tag; }; | |||
} | |||
} | |||
} |
@@ -1,39 +1,41 @@ | |||
#include "util.h" | |||
#pragma once | |||
#include <cpputils/mp/util/when.h> | |||
#include <cpputils/mp/util/tag_of.h> | |||
#include <cpputils/mp/core/conditionals.h> | |||
namespace utl { | |||
namespace mp { | |||
namespace __impl /* forward declaration */ | |||
{ | |||
/* to */ | |||
template<typename To> | |||
struct to_t; | |||
template <typename From, typename To, typename = void> | |||
struct is_convertible_impl; | |||
struct is_mp_convertible_impl; | |||
template <typename From, typename To, typename = void> | |||
struct is_embedded_impl; | |||
} | |||
/* to */ | |||
template<typename To> | |||
constexpr __impl::to_t<To> to { }; | |||
template <typename From, typename To> | |||
using is_convertible = __impl::is_convertible_impl<From, To>; | |||
using is_mp_convertible = __impl::is_mp_convertible_impl<From, To>; | |||
template <typename From, typename To> | |||
using is_embedded = __impl::is_embedded_impl<From, To>; | |||
namespace __impl /* implementation */ | |||
{ | |||
/* to */ | |||
struct no_conversion { }; | |||
template <bool = true> | |||
struct embedding { }; | |||
/* to_impl */ | |||
template <typename To, typename From, typename = void> | |||
struct to_impl; | |||
@@ -68,6 +70,15 @@ namespace mp { | |||
{ return std::forward<X&&>(x); } | |||
}; | |||
template <typename T> | |||
struct to_impl<T*, decltype(nullptr)> : | |||
public embedding<> | |||
{ | |||
static constexpr T* apply(decltype(nullptr)) | |||
{ return nullptr; } | |||
}; | |||
/* to_t */ | |||
template <typename To> | |||
struct to_t | |||
{ | |||
@@ -138,24 +149,18 @@ namespace mp { | |||
#undef DEFINE_CHAR_EMBEDDING_IMPL | |||
template <typename T> | |||
struct to_impl<T*, decltype(nullptr)> : | |||
public embedding<> | |||
{ | |||
static constexpr T* apply(decltype(nullptr)) | |||
{ return nullptr; } | |||
}; | |||
/* is_mp_convertible_impl */ | |||
template <typename From, typename To, typename> | |||
struct is_convertible_impl : | |||
struct is_mp_convertible_impl : | |||
public std::true_type | |||
{ }; | |||
template <typename From, typename To> | |||
struct is_convertible_impl<From, To, decltype((void) static_cast<no_conversion>(std::declval<to_impl<To, From>>()))> : | |||
struct is_mp_convertible_impl<From, To, decltype((void) static_cast<no_conversion>(std::declval<to_impl<To, From>>()))> : | |||
public std::false_type | |||
{ }; | |||
/* is_embedded_impl */ | |||
template <typename From, typename To, typename> | |||
struct is_embedded_impl : | |||
public std::false_type |
@@ -0,0 +1,32 @@ | |||
#pragma once | |||
#include <cpputils/mp/core/checker.h> | |||
namespace utl { | |||
namespace mp { | |||
namespace __impl /* forward declaration */ | |||
{ | |||
template<typename...> | |||
struct wrong_impl; | |||
} | |||
template<bool condition> | |||
struct when; | |||
template<typename... T> | |||
using when_valid = when<is_valid<T...>::value>; | |||
template<typename... T> | |||
using wrong = __impl::wrong_impl<T...>; | |||
namespace __impl /* implementation */ | |||
{ | |||
template<typename...> | |||
struct wrong_impl : | |||
public c_false | |||
{ }; | |||
} | |||
} | |||
} |
@@ -0,0 +1,119 @@ | |||
#include <list> | |||
#include <iomanip> | |||
#include "cpputils/logger_impl.h" | |||
using namespace logging; | |||
std::string EmptyString; | |||
consumer::consumer(const std::string& n, bool autoRegister) : | |||
_name(n) | |||
{ | |||
if (autoRegister) | |||
register_consumer(*this); | |||
} | |||
consumer::~consumer() | |||
{ unregister_consumer(*this); } | |||
void consumer_stream::log(data_ptr_s 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 matcher_all::match(const logger& logger) const | |||
{ return true; } | |||
bool matcher_all::match(const consumer& consumer) const | |||
{ return true; } | |||
bool matcher_regex::match(const logger& logger) const | |||
{ return !logger.name().empty() && std::regex_match(logger.name(), _regex) != _invert; } | |||
bool matcher_regex::match(const consumer& consumer) const | |||
{ return !consumer.name().empty() && std::regex_match(consumer.name(), _regex) != _invert; } | |||
bool matcher_default::match(const logger& logger) const | |||
{ return &_defaultLogger == &logger; } | |||
const std::string& logger::name() const | |||
{ return EmptyString; } | |||
bool logger::is_enabled(level level) const | |||
{ return false; } | |||
void logger::log(data_ptr_s data) const | |||
{ /* no op */ } | |||
const std::string& logger_impl::name() const | |||
{ return _name; } | |||
bool logger_impl::is_enabled(level level) const | |||
{ | |||
for (auto& rule : _rules) | |||
{ | |||
if (rule->is_enabled(level)) | |||
return true; | |||
} | |||
return false; | |||
} | |||
void logger_impl::log(data_ptr_s data) const | |||
{ | |||
std::lock_guard<std::mutex> lk(_mutex); | |||
for (auto& r : _rules) | |||
r->log(data); | |||
} | |||
namespace logging | |||
{ | |||
} |
@@ -0,0 +1,14 @@ | |||
#include <cpputils/logging/logger_impl.h> | |||
#include <cpputils/logging/consumer/consumer.h> | |||
using namespace ::utl::logging; | |||
consumer::consumer(const std::string& n, bool autoRegister) : | |||
_name(n) | |||
{ | |||
if (autoRegister) | |||
register_consumer(*this); | |||
} | |||
consumer::~consumer() | |||
{ unregister_consumer(*this); } |
@@ -0,0 +1,66 @@ | |||
#include <cstring> | |||
#include <cpputils/misc/stream.h> | |||
#include <cpputils/logging/consumer/consumer_stream.h> | |||
using namespace ::utl; | |||
using namespace ::utl::logging; | |||
void consumer_stream::log(data_ptr_s 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"; | |||
stream_format_saver format_saver(*_stream); | |||
if (t >= 0) *_stream << "[" << fixed << setfill(' ') << setw(17) << setprecision(6) << t << "] "; | |||
switch(d.level) | |||
{ | |||
case log_level::debug: *_stream << "DEBUG "; break; | |||
case log_level::info: *_stream << "INFO "; break; | |||
case log_level::warn: *_stream << "WARN "; break; | |||
case log_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; | |||
} | |||
consumer_stream::consumer_stream(const std::string& name, std::ostream& stream, bool ownsStream, bool autoRegister) : | |||
consumer (name, autoRegister), | |||
_stream (&stream), | |||
_ownsStream (ownsStream) | |||
{ } | |||
consumer_stream::~consumer_stream() | |||
{ | |||
if (_ownsStream && _stream) | |||
{ | |||
delete _stream; | |||
_stream = nullptr; | |||
} | |||
} |
@@ -0,0 +1,129 @@ | |||
#include <map> | |||
#include <list> | |||
#include <chrono> | |||
#include <thread> | |||
#include <cpputils/logging/rule.h> | |||
#include <cpputils/logging/global.h> | |||
#include <cpputils/logging/logger_impl.h> | |||
#include <cpputils/logging/consumer/consumer.h> | |||
namespace utl { | |||
namespace logging { | |||
struct manager | |||
{ | |||
private: | |||
logger_impl _default_logger; | |||
std::map<std::string, logger_impl_ptr_u> _logger; | |||
std::list<rule> _rules; | |||
std::set<consumer*> _consumer; | |||
logger_impl& initLogger(logger_impl& logger) | |||
{ | |||
for (auto& rule : _rules) | |||
{ | |||
if (rule.logger_matcher->match(logger)) | |||
logger.registerRule(rule); | |||
} | |||
return logger; | |||
} | |||
public: | |||
inline logger& get_logger(const std::string& name) | |||
{ | |||
if (name.empty()) | |||
return _default_logger; | |||
auto it = _logger.find(name); | |||
return (it != _logger.end() | |||
? *it->second | |||
: initLogger(*_logger.emplace(name, logger_impl_ptr_u(new logger_impl(name))).first->second)); | |||
} | |||
void register_consumer(consumer& consumer) | |||
{ | |||
_consumer.insert(&consumer); | |||
for (auto& rule : _rules) | |||
{ | |||
if (rule.consumer_matcher->match(consumer)) | |||
rule.register_consumer(consumer); | |||
} | |||
} | |||
void unregister_consumer(consumer& consumer) | |||
{ | |||
for (auto& rule : _rules) | |||
rule.unregister_consumer(consumer); | |||
} | |||
rule_handle define_rule(matcher_ptr_u logger_matcher, matcher_ptr_u consumer_matcher, log_level min_level, log_level max_level) | |||
{ | |||
_rules.emplace_back(std::move(logger_matcher), std::move(consumer_matcher), min_level, max_level); | |||
auto& rule = _rules.back(); | |||
for (auto& c : _consumer) | |||
{ | |||
if (rule.consumer_matcher->match(*c)) | |||
rule.register_consumer(*c); | |||
} | |||
if (rule.logger_matcher->match(_default_logger)) | |||
_default_logger.registerRule(rule); | |||
for (auto& l : _logger) | |||
{ | |||
if (rule.logger_matcher->match(*l.second)) | |||
l.second->registerRule(rule); | |||
} | |||
return &rule; | |||
} | |||
void undefine_rule(rule_handle handle) | |||
{ | |||
auto r = static_cast<rule*>(handle); | |||
auto it = _rules.begin(); | |||
while (&*it != r && it != _rules.end()) | |||
++it; | |||
if (it == _rules.end()) | |||
return; | |||
_default_logger.unregisterRule(*it); | |||
for (auto& l : _logger) | |||
l.second->unregisterRule(*it); | |||
_rules.erase(it); | |||
} | |||
inline void reset() | |||
{ | |||
_logger.clear(); | |||
_rules.clear(); | |||
_consumer.clear(); | |||
} | |||
manager() : | |||
_default_logger(std::string()) | |||
{ } | |||
}; | |||
manager& get_manager() | |||
{ | |||
static manager value; | |||
return value; | |||
} | |||
logger& get_logger(const std::string& name) | |||
{ return get_manager().get_logger(name); } | |||
void register_consumer(consumer& consumer) | |||
{ return get_manager().register_consumer(consumer); } | |||
void unregister_consumer(consumer& consumer) | |||
{ return get_manager().unregister_consumer(consumer); } | |||
rule_handle define_rule(matcher_ptr_u logger_matcher, matcher_ptr_u consumer_matcher, log_level min_level, log_level max_level) | |||
{ return get_manager().define_rule(std::move(logger_matcher), std::move(consumer_matcher), min_level, max_level); } | |||
void undefine_rule(rule_handle rule) | |||
{ return get_manager().undefine_rule(rule); } | |||
void reset_logging() | |||
{ get_manager().reset(); } | |||
} | |||
} |
@@ -0,0 +1,15 @@ | |||
#include <cpputils/logging/logger.h> | |||
using namespace ::utl::logging; | |||
const std::string& logger::name() const | |||
{ | |||
static std::string name; | |||
return name; | |||
} | |||
bool logger::is_enabled(log_level level) const | |||
{ return false; } | |||
void logger::log(data_ptr_s data) const | |||
{ /* no op */ } |
@@ -0,0 +1,23 @@ | |||
#include <cpputils/logging/logger_impl.h> | |||
using namespace ::utl::logging; | |||
const std::string& logger_impl::name() const | |||
{ return _name; } | |||
bool logger_impl::is_enabled(log_level level) const | |||
{ | |||
for (auto& rule : _rules) | |||
{ | |||
if (rule->is_enabled(level)) | |||
return true; | |||
} | |||
return false; | |||
} | |||
void logger_impl::log(data_ptr_s data) const | |||
{ | |||
std::lock_guard<std::mutex> lk(_mutex); | |||
for (auto& r : _rules) | |||
r->log(data); | |||
} |
@@ -0,0 +1,41 @@ | |||
#include <cpputils/logging/matcher.h> | |||
using namespace ::utl::logging; | |||
/* matcher */ | |||
bool matcher::match(const logger& logger) const | |||
{ return false; } | |||
bool matcher::match(const consumer& consumer) const | |||
{ return false; } | |||
/* matcher_all */ | |||
bool matcher_all::match(const logger& logger) const | |||
{ return true; } | |||
bool matcher_all::match(const consumer& consumer) const | |||
{ return true; } | |||
/* matcher_regex */ | |||
bool matcher_regex::match(const logger& logger) const | |||
{ return !logger.name().empty() && std::regex_match(logger.name(), _regex) != _invert; } | |||
bool matcher_regex::match(const consumer& consumer) const | |||
{ return !consumer.name().empty() && std::regex_match(consumer.name(), _regex) != _invert; } | |||
matcher_regex::matcher_regex(const std::string expression, bool invert) : | |||
_regex (expression), | |||
_invert (invert) | |||
{ } | |||
/* matcher_default */ | |||
bool matcher_default::match(const logger& logger) const | |||
{ return &_defaultLogger == &logger; } | |||
matcher_default::matcher_default() : | |||
_defaultLogger(get_logger(std::string())) | |||
{ } |
@@ -8,6 +8,7 @@ If ( __COTIRE_INCLUDED ) | |||
EndIf ( ) | |||
Target_Link_Libraries ( | |||
test_cpputils | |||
cpputils | |||
gtest | |||
gmock | |||
gmock_main | |||
@@ -1,61 +0,0 @@ | |||
#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); | |||
} |
@@ -1,76 +0,0 @@ | |||
#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)); | |||
} |
@@ -1,120 +0,0 @@ | |||
#include <gtest/gtest.h> | |||
#include "../src/cpputils/StringHelper.h" | |||
#include "../src/cpputils/HandleManager.h" | |||
using namespace utl; | |||
using HandleManagerInt = utl::HandleManager<int>; | |||
TEST(HandleManagerTest, fromString_toString) | |||
{ | |||
Handle handle; | |||
EXPECT_FALSE(tryFromString("11-22-3344-5566778", handle)); | |||
EXPECT_TRUE (tryFromString("11-22-3344-55667788", handle)); | |||
EXPECT_EQ (std::string("11-22-3344-55667788"), toString(handle)); | |||
EXPECT_TRUE (tryFromString("1122-3344-55667788", handle)); | |||
EXPECT_EQ (std::string("11-22-3344-55667788"), toString(handle)); | |||
EXPECT_TRUE (tryFromString("11-223344-55667788", handle)); | |||
EXPECT_EQ (std::string("11-22-3344-55667788"), toString(handle)); | |||
EXPECT_TRUE (tryFromString("11-22-334455667788", handle)); | |||
EXPECT_EQ (std::string("11-22-3344-55667788"), toString(handle)); | |||
EXPECT_TRUE (tryFromString("1122334455667788", handle)); | |||
EXPECT_EQ (std::string("11-22-3344-55667788"), toString(handle)); | |||
} | |||
TEST(HandleManagerTest, insert) | |||
{ | |||
HandleManagerInt manager; | |||
auto handle = manager.insert(0, 0, 123); | |||
EXPECT_TRUE(static_cast<bool>(handle)); | |||
} | |||
TEST(HandleManagerTest, remove) | |||
{ | |||
HandleManagerInt manager; | |||
auto handle = manager.insert(0, 0, 123); | |||
EXPECT_FALSE(manager.remove(156161)); | |||
EXPECT_FALSE(manager.remove(1136)); | |||
EXPECT_FALSE(manager.remove(627624)); | |||
EXPECT_FALSE(manager.remove(0)); | |||
EXPECT_TRUE (manager.remove(handle)); | |||
} | |||
TEST(HandleManagerTest, clear) | |||
{ | |||
HandleManagerInt manager; | |||
auto handle1 = manager.insert(0, 0, 123); | |||
manager.clear(); | |||
auto handle2 = manager.insert(0, 0, 555); | |||
EXPECT_EQ(handle1, handle2); | |||
} | |||
TEST(HandleManagerTest, isValid) | |||
{ | |||
HandleManagerInt manager; | |||
EXPECT_FALSE(manager.isValid(0)); | |||
EXPECT_FALSE(manager.isValid(123)); | |||
EXPECT_FALSE(manager.isValid(51627)); | |||
EXPECT_FALSE(manager.isValid(1513)); | |||
EXPECT_FALSE(manager.isValid(16621)); | |||
auto handle = manager.insert(0, 0, 123); | |||
EXPECT_TRUE(manager.isValid(handle)); | |||
} | |||
TEST(HandleManagerTest, tryGet) | |||
{ | |||
int val; | |||
HandleManagerInt manager; | |||
EXPECT_FALSE(manager.tryGet(123, val)); | |||
EXPECT_FALSE(manager.tryGet(5132, val)); | |||
EXPECT_FALSE(manager.tryGet(6216, val)); | |||
EXPECT_FALSE(manager.tryGet(15616724, val)); | |||
EXPECT_FALSE(manager.tryGet(12353, val)); | |||
auto handle = manager.insert(1, 2, 555); | |||
EXPECT_TRUE(manager.tryGet(handle, val)); | |||
EXPECT_EQ (555, val); | |||
} | |||
TEST(HandleManagerTest, get) | |||
{ | |||
HandleManagerInt manager; | |||
EXPECT_ANY_THROW(manager.get(123)); | |||
EXPECT_ANY_THROW(manager.get(5132)); | |||
EXPECT_ANY_THROW(manager.get(6216)); | |||
EXPECT_ANY_THROW(manager.get(15616724)); | |||
EXPECT_ANY_THROW(manager.get(12353)); | |||
auto handle = manager.insert(1, 2, 555); | |||
EXPECT_EQ(555, manager.get(handle)); | |||
} | |||
TEST(HandleManagerTest, update) | |||
{ | |||
HandleManagerInt manager; | |||
EXPECT_FALSE(manager.update(123, 555)); | |||
EXPECT_FALSE(manager.update(5132, 555)); | |||
EXPECT_FALSE(manager.update(6216, 555)); | |||
EXPECT_FALSE(manager.update(15616724, 555)); | |||
EXPECT_FALSE(manager.update(12353, 555)); | |||
auto handle = manager.insert(1, 2, 555); | |||
EXPECT_TRUE(manager.update(handle, 554)); | |||
EXPECT_EQ (554, manager.get(handle)); | |||
} | |||
TEST(HandleManagerTest, set) | |||
{ | |||
HandleManagerInt manager; | |||
auto handle = fromString<utl::Handle>("00-00-0001-00000000"); | |||
EXPECT_TRUE (manager.set(handle, 555)); | |||
EXPECT_EQ (555, manager.get(handle)); | |||
handle = fromString<utl::Handle>("00-00-0001-00000000"); | |||
EXPECT_TRUE (manager.set(handle, 554)); | |||
EXPECT_EQ (554, manager.get(handle)); | |||
EXPECT_FALSE(manager.set(fromString<utl::Handle>("00-00-0002-00000000"), 553)); | |||
EXPECT_EQ (554, manager.get(handle)); | |||
} |
@@ -1,135 +0,0 @@ | |||
#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"; | |||
} | |||
@@ -1,22 +0,0 @@ | |||
#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)); | |||
} |
@@ -1,97 +0,0 @@ | |||
#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,120 @@ | |||
#include <gtest/gtest.h> | |||
#include <cpputils/misc/string.h> | |||
#include <cpputils/container/handle_manager.h> | |||
using namespace utl; | |||
using handle_manager_int = utl::handle_manager<int>; | |||
TEST(handle_manager_tests, from_string_to_string) | |||
{ | |||
handle h; | |||
EXPECT_FALSE(try_from_string("11-22-3344-5566778", h)); | |||
EXPECT_TRUE (try_from_string("11-22-3344-55667788", h)); | |||
EXPECT_EQ (std::string("11-22-3344-55667788"), to_string(h)); | |||
EXPECT_TRUE (try_from_string("1122-3344-55667788", h)); | |||
EXPECT_EQ (std::string("11-22-3344-55667788"), to_string(h)); | |||
EXPECT_TRUE (try_from_string("11-223344-55667788", h)); | |||
EXPECT_EQ (std::string("11-22-3344-55667788"), to_string(h)); | |||
EXPECT_TRUE (try_from_string("11-22-334455667788", h)); | |||
EXPECT_EQ (std::string("11-22-3344-55667788"), to_string(h)); | |||
EXPECT_TRUE (try_from_string("1122334455667788", h)); | |||
EXPECT_EQ (std::string("11-22-3344-55667788"), to_string(h)); | |||
} | |||
TEST(handle_manager_tests, insert) | |||
{ | |||
handle_manager_int manager; | |||
auto handle = manager.insert(0, 0, 123); | |||
EXPECT_TRUE(static_cast<bool>(handle)); | |||
} | |||
TEST(handle_manager_tests, remove) | |||
{ | |||
handle_manager_int manager; | |||
auto handle = manager.insert(0, 0, 123); | |||
EXPECT_FALSE(manager.remove(156161)); | |||
EXPECT_FALSE(manager.remove(1136)); | |||
EXPECT_FALSE(manager.remove(627624)); | |||
EXPECT_FALSE(manager.remove(0)); | |||
EXPECT_TRUE (manager.remove(handle)); | |||
} | |||
TEST(handle_manager_tests, clear) | |||
{ | |||
handle_manager_int manager; | |||
auto handle1 = manager.insert(0, 0, 123); | |||
manager.clear(); | |||
auto handle2 = manager.insert(0, 0, 555); | |||
EXPECT_EQ(handle1, handle2); | |||
} | |||
TEST(handle_manager_tests, is_valid) | |||
{ | |||
handle_manager_int manager; | |||
EXPECT_FALSE(manager.is_valid(0)); | |||
EXPECT_FALSE(manager.is_valid(123)); | |||
EXPECT_FALSE(manager.is_valid(51627)); | |||
EXPECT_FALSE(manager.is_valid(1513)); | |||
EXPECT_FALSE(manager.is_valid(16621)); | |||
auto handle = manager.insert(0, 0, 123); | |||
EXPECT_TRUE(manager.is_valid(handle)); | |||
} | |||
TEST(handle_manager_tests, try_get) | |||
{ | |||
int val; | |||
handle_manager_int manager; | |||
EXPECT_FALSE(manager.try_get(123, val)); | |||
EXPECT_FALSE(manager.try_get(5132, val)); | |||
EXPECT_FALSE(manager.try_get(6216, val)); | |||
EXPECT_FALSE(manager.try_get(15616724, val)); | |||
EXPECT_FALSE(manager.try_get(12353, val)); | |||
auto handle = manager.insert(1, 2, 555); | |||
EXPECT_TRUE(manager.try_get(handle, val)); | |||
EXPECT_EQ (555, val); | |||
} | |||
TEST(handle_manager_tests, get) | |||
{ | |||
handle_manager_int manager; | |||
EXPECT_ANY_THROW(manager.get(123)); | |||
EXPECT_ANY_THROW(manager.get(5132)); | |||
EXPECT_ANY_THROW(manager.get(6216)); | |||
EXPECT_ANY_THROW(manager.get(15616724)); | |||
EXPECT_ANY_THROW(manager.get(12353)); | |||
auto handle = manager.insert(1, 2, 555); | |||
EXPECT_EQ(555, manager.get(handle)); | |||
} | |||
TEST(handle_manager_tests, update) | |||
{ | |||
handle_manager_int manager; | |||
EXPECT_FALSE(manager.update(123, 555)); | |||
EXPECT_FALSE(manager.update(5132, 555)); | |||
EXPECT_FALSE(manager.update(6216, 555)); | |||
EXPECT_FALSE(manager.update(15616724, 555)); | |||
EXPECT_FALSE(manager.update(12353, 555)); | |||
auto handle = manager.insert(1, 2, 555); | |||
EXPECT_TRUE(manager.update(handle, 554)); | |||
EXPECT_EQ (554, manager.get(handle)); | |||
} | |||
TEST(handle_manager_tests, set) | |||
{ | |||
handle_manager_int manager; | |||
auto handle = from_string<utl::handle>("00-00-0001-00000000"); | |||
EXPECT_TRUE (manager.set(handle, 555)); | |||
EXPECT_EQ (555, manager.get(handle)); | |||
handle = from_string<utl::handle>("00-00-0001-00000000"); | |||
EXPECT_TRUE (manager.set(handle, 554)); | |||
EXPECT_EQ (554, manager.get(handle)); | |||
EXPECT_FALSE(manager.set(from_string<utl::handle>("00-00-0002-00000000"), 553)); | |||
EXPECT_EQ (554, manager.get(handle)); | |||
} |
@@ -1,50 +1,44 @@ | |||
#include <memory> | |||
#include <type_traits> | |||
#include <gtest/gtest.h> | |||
#include <cpputils/container/nullable.h> | |||
namespace nullable_tests | |||
struct TestData | |||
{ | |||
#include "../src/cpputils/Nullable.h" | |||
static int ctorCount; | |||
static int dtorCount; | |||
struct TestData | |||
{ | |||
static int ctorCount; | |||
static int dtorCount; | |||
TestData() | |||
{ ++ctorCount; } | |||
TestData() | |||
{ ++ctorCount; } | |||
~TestData() | |||
{ ++dtorCount; } | |||
}; | |||
~TestData() | |||
{ ++dtorCount; } | |||
}; | |||
struct NonCopyableTestData | |||
{ | |||
int value; | |||
struct NonCopyableTestData | |||
{ | |||
int value; | |||
NonCopyableTestData(int v) : | |||
value(v) | |||
{ } | |||
NonCopyableTestData(int v) : | |||
value(v) | |||
{ } | |||
NonCopyableTestData(NonCopyableTestData&& other) : | |||
value(0) | |||
{ std::swap(value, other.value); } | |||
NonCopyableTestData(NonCopyableTestData&& other) : | |||
value(0) | |||
{ std::swap(value, other.value); } | |||
NonCopyableTestData(const NonCopyableTestData&) = delete; | |||
}; | |||
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>; | |||
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; | |||
} | |||
int TestData::ctorCount = 0; | |||
int TestData::dtorCount = 0; | |||
using namespace nullable_tests; | |||
using namespace nullable_tests::utl; | |||
using namespace ::utl; | |||
using namespace ::testing; | |||
TEST(NullableTest, ctor_empty) | |||
{ | |||
@@ -102,7 +96,7 @@ TEST(NullableTest, movable_object) | |||
NonCopyableTestData data(5); | |||
NullableNonCopyableTestData tmp; | |||
tmp = std::move(data); | |||
ASSERT_TRUE ( tmp.hasValue() ); | |||
ASSERT_TRUE ( tmp.has_value() ); | |||
ASSERT_EQ ( 5, tmp.value().value ); | |||
ASSERT_EQ ( 0, data.value ); | |||
} | |||
@@ -110,18 +104,18 @@ TEST(NullableTest, movable_object) | |||
TEST(NullableTest, hasValue_operatorBool) | |||
{ | |||
EXPECT_FALSE(static_cast<bool>(NullableInt())); | |||
EXPECT_FALSE(NullableInt().hasValue()); | |||
EXPECT_FALSE(NullableInt().has_value()); | |||
EXPECT_TRUE (static_cast<bool>(NullableInt(5))); | |||
EXPECT_TRUE (NullableInt(5).hasValue()); | |||
EXPECT_TRUE (NullableInt(5).has_value()); | |||
} | |||
TEST(NullableTest, reset) | |||
{ | |||
NullableTestData n(TestData{}); | |||
EXPECT_TRUE (n.hasValue()); | |||
EXPECT_TRUE (n.has_value()); | |||
int tmp = TestData::dtorCount; | |||
n.reset(); | |||
EXPECT_FALSE(n.hasValue()); | |||
EXPECT_FALSE(n.has_value()); | |||
EXPECT_EQ (tmp + 1, TestData::dtorCount); | |||
} | |||
@@ -0,0 +1,124 @@ | |||
#include <gtest/gtest.h> | |||
#include <gmock/gmock.h> | |||
#include <cpputils/logging/matcher.h> | |||
#include <cpputils/logging/consumer.h> | |||
#include <cpputils/logging/logger_impl.h> | |||
using namespace ::testing; | |||
using namespace ::utl::logging; | |||
struct LoggingReset | |||
{ | |||
~LoggingReset() | |||
{ reset_logging(); } | |||
}; | |||
struct consumer_mock : public consumer | |||
{ | |||
MOCK_METHOD1(log, void (data_ptr_s data)); | |||
consumer_mock(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, matcher_all) | |||
{ | |||
LoggingReset loggingReset; | |||
consumer_stream c0("TestConsumer1", std::cout, false, false); | |||
consumer_stream c1("TestConsumer2", std::cout, false, false); | |||
auto& l0 = get_logger(); | |||
auto& l1 = get_logger("TestLogger"); | |||
matcher_all matcher; | |||
EXPECT_TRUE(matcher.match(c0)); | |||
EXPECT_TRUE(matcher.match(c1)); | |||
EXPECT_TRUE(matcher.match(l0)); | |||
EXPECT_TRUE(matcher.match(l1)); | |||
} | |||
TEST(LoggingTests, matcher_default) | |||
{ | |||
LoggingReset loggingReset; | |||
consumer_stream c0("TestConsumer1", std::cout, false, false); | |||
consumer_stream c1("TestConsumer2", std::cout, false, false); | |||
auto& l0 = get_logger(); | |||
auto& l1 = get_logger("TestLogger"); | |||
matcher_default matcher; | |||
EXPECT_FALSE(matcher.match(c0)); | |||
EXPECT_FALSE(matcher.match(c1)); | |||
EXPECT_TRUE (matcher.match(l0)); | |||
EXPECT_FALSE(matcher.match(l1)); | |||
} | |||
TEST(LoggingTests, matcher_regex) | |||
{ | |||
LoggingReset loggingReset; | |||
consumer_stream c0("TestConsumer1", std::cout, false, false); | |||
consumer_stream c1("TestConsumer2", std::cout, false, false); | |||
auto& l0 = get_logger(); | |||
auto& l1 = get_logger("TestLogger"); | |||
matcher_regex matcher0("TestConsumer1"); | |||
matcher_regex 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<consumer_mock> c0("consumer0"); | |||
StrictMock<consumer_mock> c1("Consumer1"); | |||
EXPECT_CALL(c0, log(MatchLogData( | |||
log_level::info, | |||
(void*)12, | |||
std::this_thread::get_id(), | |||
std::string("logger0"), | |||
std::string("test1 info")))); | |||
EXPECT_CALL(c0, log(MatchLogData( | |||
log_level::warn, | |||
(void*)13, | |||
std::this_thread::get_id(), | |||
std::string("logger0"), | |||
std::string("test1 warn")))); | |||
define_rule(matcher_ptr_u(new matcher_regex("logger0")), matcher_ptr_u(new matcher_regex("consumer0")), log_level::info, log_level::warn); | |||
auto& l0 = get_logger("logger0"); | |||
auto& l1 = get_logger("logger1"); | |||
log_message(l0, debug, (void*)11, "test1 ") << "debug"; | |||
log_message(l0, info, (void*)12, "test1 ") << "info"; | |||
log_message(l0, warn, (void*)13, "test1 ") << "warn"; | |||
log_message(l0, error, (void*)14, "test1 ") << "error"; | |||
log_message(l1, debug, (void*)21, "test2 ") << "debug"; | |||
log_message(l1, info, (void*)22, "test2 ") << "info"; | |||
log_message(l1, warn, (void*)23, "test2 ") << "warn"; | |||
log_message(l1, error, (void*)24, "test2 ") << "error"; | |||
} | |||