Skip to content

Instantly share code, notes, and snippets.

@Fiona-J-W
Created January 9, 2014 17:06
Show Gist options
  • Save Fiona-J-W/8337840 to your computer and use it in GitHub Desktop.
Save Fiona-J-W/8337840 to your computer and use it in GitHub Desktop.
#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
#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
#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';
}
#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