Created
January 9, 2014 17:06
-
-
Save Fiona-J-W/8337840 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <atomic> | |
#include <chrono> | |
#include <ctime> | |
#include <fstream> | |
#include <iomanip> | |
#include <ios> | |
#include "Log.h" | |
namespace Aux { namespace Log { | |
namespace Settings { | |
namespace { | |
bool printTime = false; | |
bool printLocation = false; | |
Priority priority = Priority::info; | |
std::ofstream logfile; | |
std::atomic_bool logfileIsOpen{false}; | |
std::mutex logfileMutex; | |
} | |
Priority getPriority() {return priority;} | |
void setPriority(Priority p) {priority = p;} | |
void setPrintTime(bool b) {printTime = b;} | |
bool getPrintTime() {return printTime;} | |
void setPrintLocation(bool b) {printLocation = b;} | |
bool getPrintLocation() {return printLocation;} | |
void setLogfile(const std::string& filename) { | |
std::lock_guard<std::mutex> guard{logfileMutex}; | |
if(logfile.is_open()) { | |
logfile.close(); | |
} | |
if(filename.empty()) { | |
logfile.open(filename, std::ios_base::out | std::ios_base::app); | |
logfileIsOpen = logfile.is_open(); | |
} else { | |
logfileIsOpen = false; | |
} | |
} | |
} // namespace Settings | |
void printPriority(std::ostream& stream, Priority p) { | |
switch(p) { | |
case Priority::fatal: | |
stream << "[Fatal]"; break; | |
case Priority::error: | |
stream << "[Error]"; break; | |
case Priority::warn: | |
stream << "[Warn ]"; break; | |
case Priority::info: | |
stream << "[Info ]"; break; | |
case Priority::debug: | |
stream << "[Debug]"; break; | |
case Priority::trace: | |
stream << "[Trace]"; break; | |
} | |
} | |
void printTime(std::ostream& stream, | |
const std::chrono::time_point<std::chrono::system_clock>& timePoint) { | |
stream << "[" << timePoint.time_since_epoch().count() << "]"; | |
} | |
void printLocation(std::ostream& stream, const Location& loc) { | |
stream << "[“" << loc.file << "”, " << loc.line << ": " << loc.function << "]"; | |
} | |
std::tuple<std::string, std::string> getTerminalFormat(Priority p) { | |
switch (p) { | |
case Priority::fatal: | |
return std::make_tuple("\033[1;31m", "\033[0m"); | |
case Priority::error: | |
return std::make_tuple("\033[31m", "\033[0m"); | |
case Priority::warn: | |
return std::make_tuple("\033[33m", "\033[0m"); | |
case Priority::info: | |
case Priority::debug: | |
case Priority::trace: | |
return std::make_tuple("", ""); | |
} | |
} | |
static void logToTerminal(const Location& loc, Priority p, | |
const std::chrono::time_point<std::chrono::system_clock>& timePoint, | |
const std::string msg) { | |
std::stringstream stream; | |
if(Settings::getPrintTime()) { | |
printTime(stream, timePoint); | |
} | |
std::string termFormatOpen, termFormatClose; | |
std::tie(termFormatOpen, termFormatClose) = getTerminalFormat(p); | |
stream << termFormatOpen; | |
printPriority(stream, p); | |
stream <<termFormatClose; | |
if(Settings::getPrintLocation()) { | |
printLocation(stream, loc); | |
} | |
stream << ": "; | |
stream << termFormatOpen; | |
stream << msg; | |
stream << termFormatClose; | |
stream.put('\n'); | |
if (p < Priority::warn) { | |
static std::mutex cout_mutex; | |
{ | |
std::lock_guard<std::mutex> guard{cout_mutex}; | |
std::cout << stream.str(); | |
} | |
} else { | |
static std::mutex cerr_mutex; | |
{ | |
std::lock_guard<std::mutex> guard{cerr_mutex}; | |
std::cerr << stream.str(); | |
} | |
} | |
} | |
static void logToFile(const Location& loc, Priority p, | |
const std::chrono::time_point<std::chrono::system_clock>& timePoint, | |
const std::string& msg) { | |
if(!Settings::logfileIsOpen) { | |
return; | |
} | |
std::stringstream stream; | |
printTime(stream, timePoint); | |
stream << ' '; | |
printPriority(stream, p); | |
if(Settings::getPrintLocation()) { | |
stream << ' '; | |
printLocation(stream, loc); | |
} | |
stream << ": " << msg << '\n'; | |
{ | |
std::lock_guard<std::mutex> guard{Settings::logfileMutex}; | |
if(!Settings::logfileIsOpen) { | |
return; | |
} | |
Settings::logfile << stream.str() << std::flush; | |
} | |
} | |
namespace Impl { | |
void log(const Location& loc, Priority p, const std::string msg) { | |
auto time =std::chrono::system_clock::now(); | |
logToTerminal(loc, p, time, msg); | |
logToFile(loc, p, time, msg); | |
} | |
} // namespace impl | |
}} // namespace Aux::Log |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef AUXILIARY_LOG_H_ | |
#define AUXILIARY_HOG_H_ | |
#include <iostream> | |
#include <mutex> | |
#include "StringBuilder.h" | |
#ifdef NOLOGGING | |
#define FATAL(...) do{}while(false) | |
#define ERROR(...) do{}while(false) | |
#define WARN(...) do{}while(false) | |
#define INFO(...) do{}while(false) | |
#define DEBUG(...) do{}while(false) | |
#define TRACE(...) do{}while(false) | |
#define FATALF(...) do{}while(false) | |
#define ERRORF(...) do{}while(false) | |
#define WARNF(...) do{}while(false) | |
#define INFOF(...) do{}while(false) | |
#define DEBUGF(...) do{}while(false) | |
#define TRACEF(...) do{}while(false) | |
#define TRACEPOINT do{}while(false) | |
#else // NOLOGGING | |
#define FATAL(...) ::Aux::Log::log({__FILE__, __PRETTY_FUNCTION__, __LINE__},\ | |
::Aux::Log::Priority::fatal, __VA_ARGS__) | |
#define ERROR(...) ::Aux::Log::log({__FILE__, __PRETTY_FUNCTION__, __LINE__},\ | |
::Aux::Log::Priority::error, __VA_ARGS__) | |
#define WARN(...) ::Aux::Log::log({__FILE__, __PRETTY_FUNCTION__, __LINE__},\ | |
::Aux::Log::Priority::warn, __VA_ARGS__) | |
#define INFO(...) ::Aux::Log::log({__FILE__, __PRETTY_FUNCTION__, __LINE__},\ | |
::Aux::Log::Priority::info, __VA_ARGS__) | |
#define DEBUG(...) ::Aux::Log::log({__FILE__, __PRETTY_FUNCTION__, __LINE__},\ | |
::Aux::Log::Priority::debug, __VA_ARGS__) | |
#define TRACE(...) ::Aux::Log::log({__FILE__, __PRETTY_FUNCTION__, __LINE__},\ | |
::Aux::Log::Priority::trace, __VA_ARGS__) | |
#define FATALF(...) ::Aux::Log::logF({__FILE__, __PRETTY_FUNCTION__, __LINE__},\ | |
::Aux::Log::Priority::fatal, __VA_ARGS__) | |
#define ERRORF(...) ::Aux::Log::logF({__FILE__, __PRETTY_FUNCTION__, __LINE__},\ | |
::Aux::Log::Priority::error, __VA_ARGS__) | |
#define WARNF(...) ::Aux::Log::logF({__FILE__, __PRETTY_FUNCTION__, __LINE__},\ | |
::Aux::Log::Priority::warn, __VA_ARGS__) | |
#define INFOF(...) ::Aux::Log::logF({__FILE__, __PRETTY_FUNCTION__, __LINE__},\ | |
::Aux::Log::Priority::info, __VA_ARGS__) | |
#define DEBUGF(...) ::Aux::Log::logF({__FILE__, __PRETTY_FUNCTION__, __LINE__},\ | |
::Aux::Log::Priority::debug, __VA_ARGS__) | |
#define TRACEF(...) ::Aux::Log::logF({__FILE__, __PRETTY_FUNCTION__, __LINE__},\ | |
::Aux::Log::Priority::trace, __VA_ARGS__) | |
#define TRACEPOINT ::Aux::Log::log({__FILE__, __PRETTY_FUNCTION__, __LINE__},\ | |
::Aux::Log::Priority::trace, "tracepoint") | |
#endif // NOLOGGING | |
namespace Aux { namespace Log { | |
struct Location { | |
const char* file; | |
const char* function; | |
const int line; | |
}; | |
enum class Priority { | |
trace, | |
debug, | |
info, | |
warn, | |
error, | |
fatal | |
}; | |
namespace Settings { | |
Priority getPriority(); | |
void setPriority(Priority p); | |
void setPrintTime(bool b); | |
bool getPrintTime(); | |
void setPrintLocation(bool b); | |
bool getPrintLocation(); | |
void setLogfile(const std::string& filename); | |
} | |
namespace Impl { | |
void log(const Location& loc, Priority p, const std::string msg); | |
} //namespace impl | |
template<typename...T> | |
void log(const Location& loc, Priority p, const T&...args) { | |
if(p >= Settings::getPriority()) { | |
std::stringstream stream; | |
printToStream(stream, args...); | |
Impl::log(loc, p, stream.str()); | |
} | |
} | |
template<typename...T> | |
void logF(const Location& loc, Priority p, const std::string& format, const T&...args) { | |
if(p >= Settings::getPriority()) { | |
std::stringstream stream; | |
printToStreamF(stream, format, args...); | |
Impl::log(loc, p, stream.str()); | |
} | |
} | |
}} // namespace Aux::Log | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "StringBuilder.h" | |
#include "Log.h" | |
#include <vector> | |
#include <map> | |
#include <set> | |
#include <tuple> | |
#include <iostream> | |
static_assert(Aux::Impl::isIteratable<std::vector<int>>(), "vector is iteratable"); | |
static_assert(!Aux::Impl::isStreamable<std::vector<int>>(), "vector is not streamable"); | |
static_assert(!Aux::Impl::isPair<std::vector<int>>(), "vector is no pair"); | |
struct Unprintable {}; | |
void someOtherFunc() { | |
TRACEPOINT; | |
} | |
int main() { | |
//Aux::Log::Settings::setPriority(Aux::Log::Priority::trace); | |
//Aux::Log::Settings::setPrintTime(true); | |
//Aux::Log::Settings::setPrintLocation(true); | |
//Aux::Log::Settings::setLogfile("foobar.log"); | |
TRACE("starting program"); | |
DEBUG("test debug"); | |
INFO("test info"); | |
WARN("test warn"); | |
ERROR("test error"); | |
FATAL("test fatal"); | |
std::vector<int> simple_vec{0,1,2,3}; | |
INFO("simple vec: ", simple_vec); | |
TRACEPOINT; | |
std::vector<std::pair<int, std::string>> vec = {{1,"foo"},{2,"bar"},{3,"baz"}}; | |
INFO("complex vec: ", vec); | |
TRACEPOINT; | |
std::set<float> set{1.4, 2.5, 3.7}; | |
INFO("set: ", set); | |
TRACEPOINT; | |
INFO("tuple: ", std::make_tuple(3, "foo", 'b', 'a', 'r')); | |
TRACEPOINT; | |
std::map<int, std::string> map{{1, "a"}, {2,"b"}}; | |
INFO("map: ", map); | |
TRACEPOINT; | |
INFO("deeply nested: ", std::vector<std::vector<std::tuple<int>>>{{std::tuple<int>{1}}}); | |
TRACEPOINT; | |
INFOF("testformatting: foo%s, bar%s, baz%%", 3, 4); | |
TRACEPOINT; | |
someOtherFunc(); | |
TRACEPOINT; | |
// Those are for testing of error-messages: | |
//std::cout << "Unprintable: " << Aux::toString(std::vector<Unprintable>{}) << '\n'; | |
//std::cout << "Unprintable: " << Aux::toString(std::vector<std::vector<std::tuple<Unprintable>>>{}) << '\n'; | |
// And those are for comaprission: | |
//std::cout << "Unprintable: " << Unprintable{} << '\n'; | |
//std::cout << "Unprintable: " << std::vector<std::vector<std::tuple<Unprintable>>>{} << '\n'; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef AUXILIARY_STRING_BUILDER_H_ | |
#define AUXILIARY_STRING_BUILDER_H_ | |
#include <algorithm> | |
#include <cassert> | |
#include <string> | |
#include <sstream> | |
#include <stdexcept> | |
#include <iterator> | |
#include <type_traits> | |
#include <tuple> | |
#include <utility> | |
namespace Aux { | |
template<typename...T> | |
std::string toString(const T&...args); | |
template<typename...T> | |
std::ostream& printToStream(std::ostream& stream, const T&...args); | |
template<typename...T> | |
std::string toStringF(const std::string& format, const T&...args); | |
template<typename...T> | |
std::ostream& printToStreamF(std::ostream& stream, const std::string& format, const T&...args); | |
// Implementation | |
///////////////// | |
#define AUX_REQUIRE(what, ...) class what = typename ::std::enable_if<__VA_ARGS__>::type | |
namespace Impl { | |
// First: some helpers for template-metaprogramming: | |
template<typename T> using decay = typename std::decay<T>::type; | |
template<bool B> using boolToType = std::integral_constant<bool, B>; | |
template<typename T1, typename T2> | |
constexpr bool isSame() { | |
return std::is_same<T1, T2>::value; | |
} | |
template<typename Base, typename Derived> | |
constexpr bool isBaseOrSame() { | |
return isSame<Base, Derived>() || std::is_base_of<Base, Derived>::value; | |
} | |
// Categories of how a type might be printable | |
enum class PrintableCategory { | |
Unprintable, | |
Iteratable, | |
Pair, | |
Tuple, | |
Streamable, | |
}; | |
template<typename T> constexpr bool isStreamable(); | |
template<typename T> constexpr bool isPair(); | |
template<typename T> constexpr bool isTuple(); | |
template<typename T> constexpr bool isIteratable(); | |
template<typename T> | |
constexpr PrintableCategory getPrintableCategory() { | |
return | |
isStreamable<T>() ? PrintableCategory::Streamable : | |
isPair<T>() ? PrintableCategory::Pair : | |
isTuple<T>() ? PrintableCategory::Tuple : | |
isIteratable<T>() ? PrintableCategory::Iteratable : | |
/* else: */ PrintableCategory::Unprintable ; | |
} | |
template<PrintableCategory Tag> struct PrintableCategoryTag{}; | |
using IteratableTag = PrintableCategoryTag< PrintableCategory::Iteratable >; | |
using PairTag = PrintableCategoryTag< PrintableCategory::Pair >; | |
using TupleTag = PrintableCategoryTag< PrintableCategory::Tuple >; | |
using StreamableTag = PrintableCategoryTag< PrintableCategory::Streamable >; | |
using UnprintableTag = PrintableCategoryTag< PrintableCategory::Unprintable >; | |
template<typename T, typename...Args> void printToStream(std::ostream& stream, const T&, const Args&...); | |
inline void printToStream(std::ostream&) {} | |
template<typename T> void printToStreamTagged(std::ostream& stream, const T&, IteratableTag); | |
template<typename T> void printToStreamTagged(std::ostream& stream, const T&, PairTag); | |
template<typename T> void printToStreamTagged(std::ostream& stream, const T&, TupleTag); | |
template<typename T> void printToStreamTagged(std::ostream& stream, const T&, StreamableTag); | |
// Claim that this function exists somewhere else to keep the errors clean | |
// (calling this function is not a problem since the error is already caught earlier | |
// in printToStream, and calling it directly will result in a linker-error) | |
template<typename T> extern void printToStreamTagged(std::ostream& , const T&, UnprintableTag); | |
template<typename T, typename...Args> | |
void printToStream(std::ostream& stream, const T& arg, const Args&...args) { | |
static_assert(getPrintableCategory<T>() != PrintableCategory::Unprintable, | |
"printToStream must not be called with an unprintable argument"); | |
printToStreamTagged(stream, arg, PrintableCategoryTag<getPrintableCategory<T>()>{}); | |
printToStream(stream, args...); | |
} | |
inline std::tuple<std::string::const_iterator, bool> printFormatPartToStream(std::ostream& stream, | |
std::string::const_iterator begin, std::string::const_iterator end); | |
inline void printToStreamF(std::ostream& stream, std::string::const_iterator format_begin, | |
std::string::const_iterator format_end) { | |
bool printArgument; | |
using iterator = std::string::const_iterator; | |
iterator it; | |
std::tie(it, printArgument) = printFormatPartToStream(stream, format_begin, format_end); | |
if (printArgument) { | |
throw std::invalid_argument{"formatstring requests more arguments then provided"}; | |
} | |
} | |
template<typename T, typename...Args> | |
void printToStreamF(std::ostream& stream, std::string::const_iterator format_begin, | |
std::string::const_iterator format_end, const T& arg, const Args&...args) { | |
bool printArgument; | |
using iterator = std::string::const_iterator; | |
iterator it; | |
std::tie(it, printArgument) = printFormatPartToStream(stream, format_begin, format_end); | |
if(printArgument) { | |
printToStream(stream, arg); | |
printToStreamF(stream, it, format_end, args...); | |
} else { | |
assert(it == format_end); | |
return; | |
} | |
} | |
inline std::tuple<std::string::const_iterator, bool> printFormatPartToStream(std::ostream& stream, | |
std::string::const_iterator begin, std::string::const_iterator end) { | |
if (begin == end) { | |
return std::make_tuple(end, false); | |
} | |
while (true) { | |
auto nextPercent = std::find(begin, end, '%'); | |
stream.write(&*begin, nextPercent-begin); | |
if(nextPercent == end) { | |
return std::make_tuple(end, false); | |
} else { | |
begin = ++nextPercent; | |
if(begin == end) { | |
throw std::invalid_argument{"formatstrings must not end on unmatched '%'"}; | |
} else if (*begin == '%') { | |
stream.put('%'); | |
++begin; | |
} else if (*begin == 's') { | |
++begin; | |
return std::make_tuple(begin, true); | |
} else { | |
throw std::invalid_argument{"formatstring contains illegal format-specifier"}; | |
} | |
} | |
} | |
} | |
// Brace Yourself: Templatemetaprogramming is comming | |
///////////////////////////////////////////////////// | |
// Iteratable | |
struct IsIteratableHelper { | |
static std::false_type isIteratable(...); | |
template<typename T, | |
class Iterator = decltype(std::begin(std::declval<T>())), | |
AUX_REQUIRE(EndIteratorValid, isSame<Iterator, decltype(std::end(std::declval<T>()))>()), | |
AUX_REQUIRE(HasInputIterator, isBaseOrSame<std::input_iterator_tag, | |
typename std::iterator_traits<Iterator>::iterator_category>()) | |
> static std::true_type isIteratable(const T&); | |
}; | |
template<typename T> constexpr bool isIteratable() { | |
return decltype(IsIteratableHelper::isIteratable(std::declval<T>()))::value; | |
} | |
// Pair | |
template<typename T > struct IsPairHelper : std::false_type {}; | |
template<typename T1, typename T2> struct IsPairHelper<std::pair<T1, T2>> : std::true_type {}; | |
template<typename T> constexpr bool isPair() { | |
return IsPairHelper<T>::value; | |
} | |
// Tuple | |
template<typename T> struct IsTupleHelper : std::false_type {}; | |
template<typename...T> struct IsTupleHelper<std::tuple<T...>> : std::true_type {}; | |
template<typename T> constexpr bool isTuple() { | |
return IsTupleHelper<T>::value; | |
} | |
// Streamable | |
struct IsStreamableHelper { | |
static std::false_type isStreamable(...); | |
template<typename T, | |
AUX_REQUIRE(Streamable, isBaseOrSame<std::ostream, | |
decay<decltype(std::declval<std::ostream&>() << std::declval<const T&>())>>()) | |
> | |
static std::true_type isStreamable(const T&); | |
}; | |
template<typename T> constexpr bool isStreamable() { | |
return decltype(IsStreamableHelper::isStreamable(std::declval<T>()))::value; | |
} | |
// And now: implement the actual printing: | |
////////////////////////////////////////// | |
// Streamable | |
template<typename T> void printToStreamTagged(std::ostream& stream, const T& arg, StreamableTag) { | |
stream << arg; | |
} | |
// Pair | |
template<typename T> void printToStreamTagged(std::ostream& stream, const T& arg, PairTag) { | |
stream << '('; | |
printToStream(stream, arg.first); | |
stream << ", "; | |
printToStream(stream, arg.second); | |
stream << ')'; | |
} | |
// Tuple | |
template<typename Tuple, int I, int TupleSize> struct printTupleHelper { | |
static void print(std::ostream& stream, const Tuple& arg) { | |
printToStream(stream, std::get<I-1>(arg)); | |
stream << ", "; | |
printTupleHelper<Tuple, I+1, TupleSize>::print(stream, arg); | |
} | |
}; | |
template<typename Tuple, int I> struct printTupleHelper<Tuple, I, I>{ | |
static void print(std::ostream& stream, const Tuple& arg) { | |
printToStream(stream, std::get<I-1>(arg)); | |
} | |
}; | |
template<typename T> void printToStreamTagged(std::ostream& stream, const T& arg, TupleTag) { | |
stream << '('; | |
printTupleHelper<T, 1, std::tuple_size<T>::value>::print(stream, arg); | |
stream << ')'; | |
} | |
// Iteratable | |
template<typename T> void printToStreamTagged(std::ostream& stream, const T& arg, IteratableTag) { | |
auto it = std::begin(arg); | |
auto end = std::end(arg); | |
bool firstpass = true; | |
stream << '['; | |
while(it != end) { | |
if(firstpass) { | |
firstpass = false; | |
} | |
else { | |
stream << ", "; | |
} | |
printToStream(stream, *it); | |
++it; | |
} | |
stream << ']'; | |
} | |
} // namespace Impl | |
// Finally: put together the public interface: | |
////////////////////////////////////////////// | |
template<typename...T> | |
std::string toString(const T&...args) { | |
std::stringstream stream; | |
printToStream(stream, args...); | |
return stream.str(); | |
} | |
template<typename...T> | |
std::ostream& printToStream(std::ostream& stream, const T&...args) { | |
Impl::printToStream(stream, args...); | |
return stream; | |
} | |
template<typename...T> | |
std::string toStringF(const std::string& format, const T&...args) { | |
std::stringstream stream; | |
printToStreamF(stream, format, args...); | |
return stream.str(); | |
} | |
template<typename...T> | |
std::ostream& printToStreamF(std::ostream& stream, const std::string& format, const T&...args) { | |
Impl::printToStreamF(stream, format.begin(), format.end(), args...); | |
return stream; | |
} | |
} //namespace Aux | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment