flatten 20260225

This commit is contained in:
Timothy Prepscius
2026-02-25 12:43:06 -05:00
commit 996151a56a
521 changed files with 46722 additions and 0 deletions

68
tjp/core/log/Color.cpp Executable file
View File

@@ -0,0 +1,68 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#if defined(TJP_CORE_HEADER_ONLY)
#pragma once
#endif
#include <iosfwd>
#include "Color.hpp"
namespace tjp::core::log::detail {
TJP_CORE_HEADER_ONLY_INLINE
bool isColorAvailable(std::ostream &o)
{
return o.iword(SupportsColorFlag::id());
}
TJP_CORE_HEADER_ONLY_INLINE
std::ostream &operator <<(std::ostream &o, const Color &color)
{
if (isColorAvailable(o))
o << color.value;
return o;
}
template<>
TJP_CORE_HEADER_ONLY_INLINE
std::ostream &operator <<(std::ostream &o, const Colored<const char *> &s)
{
if (!s.v)
return o;
if (!isColorAvailable(o))
return o << s.v;
auto sum = 0;
for (auto i=0; i<128; ++i)
{
if (s.v[i] == 0)
break;
sum += s.v[i];
}
auto code = sum % 128 + 1;
o
<< "\033[38;5;" << code << "m"
<< s.v
<< "\033[0m";
return o;
}
TJP_CORE_HEADER_ONLY_INLINE
std::ostream &operator <<(std::ostream &o, const ColoredStory &v)
{
if (v.story)
o << "[" << color(v.story) << "] ";
return o;
}
} // namespace

95
tjp/core/log/Color.hpp Executable file
View File

@@ -0,0 +1,95 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#pragma once
#include "../header_only/compile.h"
#include <ios>
namespace tjp::core::log::detail {
struct ColoredStory
{
const char *story;
} ;
template<typename T>
struct Colored
{
const T &v;
} ;
template<typename T>
struct SupportsColorFlag_
{
static inline int id_ = -1;
} ;
struct SupportsColorFlag : SupportsColorFlag_<void>
{
static int id()
{
if (id_ < 0)
id_ = std::ios_base::xalloc();
return id_;
}
} ;
template<typename T>
Colored<T> color(const T &v) { return { v }; }
std::ostream &operator <<(std::ostream &o, const ColoredStory &v);
template<typename T>
std::ostream &operator <<(std::ostream &o, const Colored<T> &);
template<>
std::ostream &operator <<(std::ostream &o, const Colored<const char *> &);
// --------------------------------------
#define xLogColorStory(story) tjp::core::log::detail::ColoredStory { story }
struct Color
{
const char *value;
Color(const char *value_) :
value(value_)
{}
};
std::ostream &operator <<(std::ostream &o, const Color &color);
} // namespace
namespace tjp::core::log {
using detail::Color;
using detail::color;
//the following are UBUNTU/LINUX, and MacOS ONLY terminal color codes.
const Color RESET ="\033[0m";
const Color BLACK = "\033[30m";
const Color RED = "\033[31m";
const Color GREEN = "\033[32m";
const Color YELLOW = "\033[33m";
const Color BLUE = "\033[34m";
const Color MAGENTA = "\033[35m";
const Color CYAN = "\033[36m";
const Color WHITE = "\033[37m";
const Color BOLDBLACK = "\033[1m\033[30m";
const Color BOLDRED = "\033[1m\033[31m";
const Color BOLDGREEN = "\033[1m\033[32m";
const Color BOLDYELLOW = "\033[1m\033[33m";
const Color BOLDBLUE = "\033[1m\033[34m";
const Color BOLDMAGENTA = "\033[1m\033[35m";
const Color BOLDCYAN = "\033[1m\033[36m";
const Color BOLDWHITE = "\033[1m\033[37m";
}
#include "Color.inl"

21
tjp/core/log/Color.inl Executable file
View File

@@ -0,0 +1,21 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#include "Log.h"
#include <sstream>
namespace tjp::core::log::detail {
template<typename T>
std::ostream &operator <<(std::ostream &o, const Colored<T> &v)
{
std::ostringstream s;
s << v.v;
o << color(s.str().c_str());
return o;
}
} // namespace

244
tjp/core/log/Detail.cpp Executable file
View File

@@ -0,0 +1,244 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#if defined(TJP_CORE_HEADER_ONLY)
#pragma once
#endif
#include "Detail.hpp"
#include "Util.inl"
#include "../io/memstream.hpp"
#include "../algorithm/set_has.hpp"
#include "../algorithm/unused.hpp"
#include "../string/as_string_view.hpp"
#include <sstream>
#include <fstream>
namespace tjp::core::log::detail {
TJP_CORE_HEADER_ONLY_INLINE
bool operator==(const Story &lhs, const Story &rhs)
{
return
lhs.hash == rhs.hash &&
lhs.name == rhs.name;
}
TJP_CORE_HEADER_ONLY_INLINE
bool operator<(const Story &lhs, const Story &rhs)
{
if (lhs.hash == rhs.hash)
return lhs.name < rhs.name;
return lhs.hash < rhs.hash;
}
TJP_CORE_HEADER_ONLY_INLINE
Manager::Manager() :
file(this),
termSupportsColor(detectIfColorIsAvailable())
{
}
TJP_CORE_HEADER_ONLY_INLINE
Manager &getManager()
{
static Manager manager;
return manager;
}
TJP_CORE_HEADER_ONLY_INLINE
void Stories::clear()
{
all = false;
names.clear();
activated.clear();
}
TJP_CORE_HEADER_ONLY_INLINE
void Stories::activate(const Story &s)
{
if (s.name == "*")
{
all = true;
return;
}
if (!set_has(activated, s))
{
names.push_back(String(s.name));
Story s_ = s;
s_.name = names.back();
activated.insert (s_);
}
}
TJP_CORE_HEADER_ONLY_INLINE
bool Stories::isActivated (const Story &s)
{
return all || set_has(activated, s);
}
// ---------------
struct File::I {
std::ofstream file;
} ;
TJP_CORE_HEADER_ONLY_INLINE
File::File(Manager *manager_) :
manager(manager_)
{
i = new I();
}
TJP_CORE_HEADER_ONLY_INLINE
File::~File()
{
delete i;
}
TJP_CORE_HEADER_ONLY_INLINE
Settings &File::getSettings()
{
return manager->settings;
}
TJP_CORE_HEADER_ONLY_INLINE
void File::start()
{
if (i->file.is_open())
i->file.close();
auto &settings = getSettings();
i->file.open(
settings.LogFileName,
settings.AppendToLogFile ?
std::ios::app :
std::ios::out
);
}
TJP_CORE_HEADER_ONLY_INLINE
void File::write(const StringView &s)
{
if (!i->file)
return;
sizeWritten += s.size();
i->file << s;
i->file.flush();
checkRotate();
}
TJP_CORE_HEADER_ONLY_INLINE
void File::checkRotate()
{
auto &settings = getSettings();
if (settings.RotateSize != 0 && sizeWritten > settings.RotateSize)
rotate();
}
TJP_CORE_HEADER_ONLY_INLINE
void File::rotate()
{
char data[256];
GetTimeStamp(data, std::size(data), '_');
rotateTo(getSettings().LogFileName + "." + data);
}
TJP_CORE_HEADER_ONLY_INLINE
void File::rotateTo(const std::string_view &name_)
{
i->file.close();
auto name = std::string(name_);
remove(name.c_str());
rename(getSettings().LogFileName.c_str(), name.c_str());
sizeWritten = 0;
start();
}
// -------
TJP_CORE_HEADER_ONLY_INLINE
void Manager::writeToConsole (const StringView &s)
{
auto lock = std::lock_guard<Mutex>(mutex);
char data[256];
core::io::omemstream_ out;
out << "XD " << __GetLogStamp__(data, termSupportsColor) << s;
auto externalConsolePrint = external.console;
if (externalConsolePrint)
return externalConsolePrint(core::as_string_view(out.data));
fwrite((void *)out.data.data(), 1, out.data.size(), stdout);
}
TJP_CORE_HEADER_ONLY_INLINE
void Manager::writeToDebugger (const StringView &s)
{
auto lock = std::lock_guard<Mutex>(mutex);
char data[256];
unused(data);
auto externalDebuggerPrint = external.debugger;
if (externalDebuggerPrint)
{
#ifdef SYS_ANDROID
core::io::omemstream_ out_;
out_ << __GetLogStamp__(data, false) << s;
auto out = as_string_view(out_.data);
#else
auto &out = s;
#endif
externalDebuggerPrint(out);
}
#ifdef WIN32
OutputDebugString (__GetLogStamp__(data, false));
OutputDebugString (s.c_str());
#endif
}
TJP_CORE_HEADER_ONLY_INLINE
void Manager::writeToLog (const StringView &s)
{
auto externalLogPrint = external.log;
bool willWrite = externalLogPrint || settings.LogToFileActivated;
if (willWrite)
{
char data[256];
core::io::omemstream_ out;
out << __GetLogStamp__(data, false) << s;
auto outs = as_string_view(out.data);
if (externalLogPrint)
externalLogPrint(outs);
if (settings.LogToFileActivated)
file.write(outs);
#ifdef USE_OSX_LOG
// Define a custom log category
static os_log_t os_log_ = os_log_create("com.timprepscius.crown", "std");
// Log a message with default log level
os_log(os_log_, "%s", s.c_str());
#endif
}
}
} // namespace

10
tjp/core/log/Detail.h Executable file
View File

@@ -0,0 +1,10 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#pragma once
namespace tjp::core::log::detail {
} // namespace

126
tjp/core/log/Detail.hpp Executable file
View File

@@ -0,0 +1,126 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#pragma once
#include "../header_only/compile.h"
#include "../string/String.hpp"
#include "../string/StringView.hpp"
#include "../types/Types.h"
#include "../containers/List.hpp"
#include "../containers/Set.hpp"
#include "../const_hash/Hash.hpp"
#include <mutex>
namespace tjp::core::log::detail {
struct Manager;
struct Story
{
using Hash = u32;
Hash hash;
StringView name;
constexpr Story(const char *s) :
hash(hash_compile<Hash>(s)),
name(s)
{}
} ;
struct Settings
{
String LogFileName = "log.txt";
bool AppendToLogFile = false;
bool LogToFileActivated = false;
bool LogToDebuggerActivated = false;
bool LogToConsoleActivated = true;
bool LogInExecutableDirectory = false;
// zero for never rotate
u64 RotateSize = 10 * (1024 * 1024);
} ;
typedef void (*TimPrepsciusCoreLogExternalPrintPtr)(const StringView &);
struct External
{
TimPrepsciusCoreLogExternalPrintPtr debugger = nullptr;
TimPrepsciusCoreLogExternalPrintPtr log = nullptr;
TimPrepsciusCoreLogExternalPrintPtr console = nullptr;
} ;
struct Stories
{
bool all = false;
Set<Story> activated;
List<String> names;
void clear();
void activate(const Story &s);
bool isActivated (const Story &s);
} ;
struct File
{
struct I;
Manager *manager;
Settings &getSettings();
File (Manager *);
~File();
I *i;
u64 sizeWritten = 0;
void start();
void write(const StringView &s);
void checkRotate();
void rotate();
void rotateTo(const std::string_view &name);
} ;
struct Manager
{
using Mutex = std::mutex;
Mutex mutex;
External external;
Settings settings;
Stories stories;
File file;
bool termSupportsColor = false;
Manager();
void start();
void rotate();
void rotateTo(const StringView &);
void writeToLog(const StringView &);
void writeToDebugger(const StringView &);
void writeToConsole(const StringView &);
template<typename F>
auto with_lock(F &&f)
{
std::lock_guard<Mutex> lock(mutex);
f(*this);
}
} ;
Manager &getManager();
std::string methodName(const char *prettyFunction);
} // namespace

14
tjp/core/log/Log+Precision.h Executable file
View File

@@ -0,0 +1,14 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#pragma once
/**
* Debug and Error logging utility methods and macros used throughout
* the application
*/
#include <iomanip>
#define LOG_PRECISION(x) std::fixed << std::setprecision(x)

171
tjp/core/log/Log.cpp Executable file
View File

@@ -0,0 +1,171 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#if defined(TJP_CORE_HEADER_ONLY)
#pragma once
#endif
#include "Log.h"
#include <fstream>
#include "../system/System.h"
#if defined(SYS_IOS) || defined(SYS_MAC)
// #define USE_OSX_LOG
#endif
#if defined(USE_OSX_LOG)
#include <os/log.h>
#endif
namespace tjp::core::log {
TJP_CORE_HEADER_ONLY_INLINE
void ActivateStory (const Story &s)
{
getManager().stories.activate(s);
}
TJP_CORE_HEADER_ONLY_INLINE
void setExternalDebugPrint(TimPrepsciusCoreLogExternalPrintPtr f)
{
getManager().with_lock([&](auto &manager) {
manager.external.debugger = f;
});
}
TJP_CORE_HEADER_ONLY_INLINE
void setExternalLogPrint(TimPrepsciusCoreLogExternalPrintPtr f)
{
getManager().with_lock([&](auto &manager) {
manager.external.log = f;
});
}
TJP_CORE_HEADER_ONLY_INLINE
void setExternalConsolePrint(TimPrepsciusCoreLogExternalPrintPtr f)
{
getManager().with_lock([&](auto &manager) {
manager.external.console = f;
});
}
// ------------------------------
TJP_CORE_HEADER_ONLY_INLINE
void Start ()
{
getManager().with_lock([&](auto &manager) {
if (manager.settings.LogToFileActivated)
manager.file.start();
});
}
TJP_CORE_HEADER_ONLY_INLINE
void RotateTo(const std::string_view &fileName)
{
getManager().with_lock([&](auto &manager) {
if (manager.settings.LogToFileActivated)
manager.file.rotateTo(fileName);
});
}
TJP_CORE_HEADER_ONLY_INLINE
void Rotate()
{
getManager().with_lock([&](auto &manager) {
if (manager.settings.LogToFileActivated)
manager.file.rotate();
});
}
TJP_CORE_HEADER_ONLY_INLINE
void ActivateStory_(const Story &s)
{
getManager().with_lock([&](auto &manager) {
manager.stories.activate(s);
});
}
//---------------------------------------------------
TJP_CORE_HEADER_ONLY_INLINE
void ActivateStories (const std::string &filename)
{
std::ifstream in(filename.c_str());
if (!in)
return;
while (!in.eof())
{
char line[80];
in.getline(line, 80);
line[79]='\0';
for (int i=0;i<80; i++)
{
if (line[i] == '\r' || line[i]=='\n' || line[i]=='#' || line[i] =='\0')
{
line[i] = '\0';
break;
}
}
if (line[0] == 0)
continue;
xLogActivateStory(line);
}
}
// ---
TJP_CORE_HEADER_ONLY_INLINE
void Initialize (const Settings &settings_)
{
getManager().with_lock([&](auto &manager) {
manager.settings = settings_;
});
Start();
}
TJP_CORE_HEADER_ONLY_INLINE
void Initialize (const std::string &fileName)
{
getManager().with_lock([&](auto &manager) {
auto &settings = manager.settings;
settings.LogFileName = fileName;
settings.LogToFileActivated = !fileName.empty();
settings.LogToConsoleActivated = !settings.LogToFileActivated;
settings.LogToDebuggerActivated = false;
settings.LogInExecutableDirectory = false;
});
Start();
}
} // namespace
TJP_CORE_HEADER_ONLY_INLINE
void setExternalDebuggerPrint(TimPrepsciusCoreLogExternalPrintPtr f)
{
tjp::core::log::setExternalDebugPrint(f);
}
TJP_CORE_HEADER_ONLY_INLINE
void setExternalLogPrint(TimPrepsciusCoreLogExternalPrintPtr f)
{
tjp::core::log::setExternalLogPrint(f);
}
TJP_CORE_HEADER_ONLY_INLINE
void setExternalConsolePrint(TimPrepsciusCoreLogExternalPrintPtr f)
{
tjp::core::log::setExternalConsolePrint(f);
}

242
tjp/core/log/Log.h Executable file
View File

@@ -0,0 +1,242 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#pragma once
#include "../header_only/compile.h"
#include "../debug/Debug.h"
#include "Detail.hpp"
#include "Color.hpp"
#include "../io/memstream.hpp"
#include "../string/as_string_view.hpp"
typedef void (*TimPrepsciusCoreLogExternalPrintPtr)(const std::string_view &);
void setExternalDebugPrint(TimPrepsciusCoreLogExternalPrintPtr f);
void setExternalLogPrint(TimPrepsciusCoreLogExternalPrintPtr f);
void setExternalConsolePrint(TimPrepsciusCoreLogExternalPrintPtr f);
namespace tjp::core::log {
using detail::Settings;
using detail::Story;
using detail::getManager;
#define xLogManager() tjp::core::log::getManager()
void Initialize(const std::string &fileName = {});
void Initialize(const Settings &);
#define xLogInitialize(...) tjp::core::log::Initialize(__VA_ARGS__)
/**
* the functions actually write strings
*/
void WriteToLog (const std::string &);
void WriteToDebugger (const std::string &);
void WriteToConsole (const std::string &);
/**
* functions to turn on/off logs and query their state
*/
void ActivateStory (const Story &s);
void RotateTo (const std::string_view &rotateTo);
void Rotate ();
void Start();
void Stop ();
void ActivateStories (const std::string &fileName);
//-----------------------------------------------------------------------------
#if defined(_DEBUG) || defined(DEBUG)
#ifndef LOG_DEBUG
#define LOG_DEBUG
#endif
#else
#ifndef LOG_DEBUG
// #define LOG_DEBUG
#endif
#endif
#define LOG_RELEASE
#define LOG_ERROR
#ifdef TESTING
#define LOG_TEST
#endif
#if defined(LOG_DEBUG) || defined(LOG_RELEASE)
#define xLogActivateStories(s) tjp::core::log::ActivateStories(s)
#define xLogActivateStory(s) \
tjp::core::log::ActivateStory(s); \
sLogRelease(s, "logging activated")
#define xLogStart() tjp::core::log::Start()
#define xLogStop() tjp::core::log::Stop()
#else
#define xLogActivateStories(s)
#define xLogActivateStory(x)
#define xLogStoryActivated(x)
#define xLogStart(x)
#define xLogStop(x)
#endif
//-----------------------------------------------------------------------------
#define xLogDebugStreamName "Debug"
#define xLogReleaseStreamName "Release"
#define xLogErrorStreamName "Error"
#define xLogSettings tjp::core::log::Settings::global
// ------------------------------------
#define xLogMethodName(x) tjp::core::log::detail::methodName(x)
// ------------------------------------
#define xLogNoStory "_"
#define xLogAllStories "*"
// ------------------------------------
#ifdef LOG_DEBUG
#define xFormatDebugger(p,n,story,...) __FILE__ << "(" << __LINE__ << ") : " << xLogColorStory(story) << n << " " << __VA_ARGS__ << std::endl
#else
#define xFormatDebugger(p,n,story,...) story << n << " " << __VA_ARGS__ << std::endl
#endif
#define xFormatLog(p,n,story,...) "[" << story << "] " << n << " " << __VA_ARGS__ << std::endl
#define xFormatConsole(p,n,story,...) xLogColorStory(story) << n << " " << __VA_ARGS__ << std::endl
#define xLogTo(useColor, where, ...) \
{ \
::tjp::core::io::omemstream_ _out; \
if (useColor) \
_out.iword(::tjp::core::log::detail::SupportsColorFlag::id()) = 1; \
_out << __VA_ARGS__; \
where(::tjp::core::as_string_view(_out.data)); \
}
#define _xLog__(manager, n_,condition,story,...) \
{ \
if (condition) \
{ \
auto n = xLogMethodName(n_); \
if (manager.settings.LogToDebuggerActivated) \
xLogTo(false, manager.writeToDebugger, xFormatDebugger(xLogDebugStreamName,n,story,__VA_ARGS__)) \
if (manager.settings.LogToFileActivated) \
xLogTo(false, manager.writeToLog, xFormatLog(xLogDebugStreamName,n,story,__VA_ARGS__)); \
if (manager.settings.LogToConsoleActivated) \
xLogTo(manager.termSupportsColor, manager.writeToConsole, xFormatConsole(xLogDebugStreamName,n,story,__VA_ARGS__)); \
} \
}
#define _xLog_(n_,condition,story,...) \
{ \
auto &manager = xLogManager(); \
_xLog__(manager, n_,condition,story, __VA_ARGS__); \
} \
#define _xLog(n_,condition,story,...) \
{ \
auto &manager = xLogManager(); \
if (manager.stories.isActivated(story)) \
_xLog__(manager, n_,condition,story, __VA_ARGS__); \
} \
// --------------------------------------
#define _xLogDebug(condition,story,...) _xLog(__PRETTY_FUNCTION__,condition,story,__VA_ARGS__)
#define _xLogTest(condition,story,...) _xLog(__PRETTY_FUNCTION__,condition,story,__VA_ARGS__)
#ifdef _DEBUG
#define LOG_USE_FUNCTION_NAMES
#else
// #define LOG_USE_FUNCTION_NAMES
#endif
#ifdef LOG_USE_FUNCTION_NAMES
#define _xLogRelease(condition,story,...) _xLog(__PRETTY_FUNCTION__,condition,story,__VA_ARGS__)
#define _xLogError(condition,story,...) _xLog_(__PRETTY_FUNCTION__,condition,story,__VA_ARGS__)
#else
#define _xLogRelease(condition,story,...) _xLog(nullptr,condition,story,__VA_ARGS__)
#define _xLogError(condition,story,...) _xLog_(nullptr,condition,story,__VA_ARGS__)
#endif
// --------------------------------------
#ifdef LOG_DEBUG
#define xLogDebug(...) _xLogDebug(true,xLogNoStory,__VA_ARGS__)
#define xLogDebugIf(condition,...) _xLogDebug(condition,xLogNoStory,__VA_ARGS__)
#define sLogDebug(story,...) _xLogDebug(true,story,__VA_ARGS__)
#define sLogDebugIf(condition,story,...) _xLogDebug(condition,story,__VA_ARGS__)
#define fLogDebug(x) xLogDebug(x)
#define LOG_DEBUG_ONLY(...) __VA_ARGS__
#else
#define xLogDebug(...)
#define xLogDebugIf(...)
#define sLogDebug(...)
#define sLogDebugIf(...)
#define fLogDebug(...)
#define LOG_DEBUG_ONLY(...)
#endif
#ifdef LOG_RELEASE
#define xLogRelease(...) _xLogRelease(true,xLogNoStory,__VA_ARGS__)
#define xLogReleaseIf(condition,...) _xLogRelease(condition,xLogNoStory,__VA_ARGS__)
#define sLogRelease(story,...) _xLogRelease(true,story,__VA_ARGS__)
#define sLogReleaseIf(condition,story,...) _xLogRelease(condition,story,__VA_ARGS__)
#else
#define xLogRelease(...)
#define xLogReleaseIf(...)
#define sLogRelease(...)
#define sLogReleaseIf(...)
#endif
#ifdef LOG_TEST
#define xLogTest(x) _xLogTest(true,xLogNoStory,x)
#define xLogTestIf(condition,x) _xLogTest(condition,xLogNoStory,x)
#define sLogTest(story,x) _xLogTest(true,story,x)
#define sLogTestIf(condition,story,x) _xLogTest(condition,story,x)
#else
#define xLogTest(...)
#define xLogTestIf(...)
#define sLogTest(...)
#define sLogTestIf(...)
#endif
#ifdef LOG_ERROR
#define xLogError(...) _xLogError(true,xLogNoStory,__VA_ARGS__)
#define sLogError(story,...) _xLogError(true,story,__VA_ARGS__)
#define sLogErrorIf(condition,story,...) _xLogError(condition,story,__VA_ARGS__)
#else
#define xLogError(...)
#define sLogError(...)
#define sLogErrorIf(...)
#endif
// --------------------------------------
#define nLogDebug(...)
#define nLogRelease(...)
#define nLogError(...)
} // namespace
#ifdef TJP_CORE_HEADER_ONLY_COMPILE
#include "Detail.cpp"
#include "Color.cpp"
#include "Log.cpp"
#endif

192
tjp/core/log/LogOf.h Executable file
View File

@@ -0,0 +1,192 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#pragma once
#include "../type_traits/is_iterable.hpp"
#include <sstream>
#include <string>
#include "demangle.h"
#include "../types/Types+IO.h"
namespace tjp::core {
template<typename T>
struct LogOf
{
const T &t;
explicit LogOf(const T &t_) : t(t_) {}
std::ostream &operator()(std::ostream &o) const
{
return o << t;
}
} ;
template<typename T>
struct LogOfV
{
T t;
explicit LogOfV(T t_) : t(t_) {}
std::ostream &operator()(std::ostream &o) const
{
return o << t;
}
} ;
template<typename T>
auto logOfV(T t) { return LogOfV<T>(t); }
template<typename T>
std::ostream &operator <<(std::ostream &o, const LogOf<T> &v)
{
return v(o);
}
template<typename T>
std::ostream &operator <<(std::ostream &o, const LogOfV<T> &v)
{
return v(o);
}
template<typename T, typename std::enable_if<!is_iterable<T>::value, T>::type* = nullptr>
auto logOf(const T &t);
template<typename T, typename std::enable_if<is_iterable<T>::value, T>::type* = nullptr>
auto logOf(const T &t);
inline
auto logOf(const std::string &t) { return t; }
inline
auto logOf(bool v) { return v ? "true" : "false"; }
template<typename A, typename B>
auto logOf(const std::pair<A,B> &t) {
std::ostringstream o;
o << "(" << logOf(t.first) << "," << logOf(t.second) << ")";
return o.str();
}
template<typename T, typename std::enable_if<!is_iterable<T>::value, T>::type*>
auto logOf(const T &t) { return LogOf<T>(t); }
template<typename T, typename std::enable_if<is_iterable<T>::value, T>::type*>
auto logOf(const T &t) {
std::ostringstream o;
o << "[";
auto first = true;
for (auto v : t)
{
if (!first)
{
o << ",";
}
o << logOf(v);
first = false;
}
o << "]";
return o.str();
}
inline
auto logOf(const std::string_view &v)
{
return v;
}
template<typename T>
struct NullOrValue
{
const T *t;
explicit NullOrValue(const T *t_) : t(t_) {}
} ;
template<typename T>
std::ostream &operator <<(std::ostream &o, const NullOrValue<T> &v)
{
if (v.t)
return o << logOf(*v.t);
return o << "null";
}
template<typename T>
auto logOfRef(T *t)
{
return NullOrValue<T>(t);
}
template<typename T>
struct NullOrPtrValue
{
const T *t;
explicit NullOrPtrValue(const T *t_) : t(t_) {}
} ;
template<typename T>
std::ostream &operator <<(std::ostream &o, const NullOrPtrValue<T> &v)
{
if (v.t)
return o << "[" << v.t << "] " << logOf(*v.t);
return o << "null";
}
template<typename T>
auto logPtrValue(T *t)
{
return NullOrPtrValue<T>(t);
}
template<typename T>
auto logPtr(T *t)
{
return t;
}
template<typename T>
struct SpaceBefore
{
const T &t;
} ;
template<typename T>
std::ostream &operator <<(std::ostream &o, const SpaceBefore<T> &v)
{
return o << " " << logOf(v.t);
}
template<typename T>
SpaceBefore<T> _logOf(const T &t)
{
return SpaceBefore<T> { t };
}
template<typename T>
auto logOfDispatch(T &&t)
{
return logOf(std::forward<T>(t));
}
template<typename T>
auto logOfDispatchV(T t)
{
return logOfV(t);
}
#define logOfThis(x) "[" << tjp::core::logPtr(x) << "] "
#define logVar(x) #x << " " << tjp::core::logOfDispatch(x) << " "
#define logVarV(x) #x << " " << tjp::core::logOfDispatchV(x) << " "
#define logType(x) #x << " " << tjp::core::type_id<x>().name() << " "
#define logLabel(l) l << " "
#define logLabelVar(l, x) l << " " << tjp::core::logOfDispatch(x) << " "
#define logLabelVarV(l, x) l << " " << tjp::core::logOfDispatchV(x) << " "
} // namespace

170
tjp/core/log/Util.inl Executable file
View File

@@ -0,0 +1,170 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#pragma once
#include <unistd.h>
#include <chrono>
#include <ctime>
#include <stdio.h>
#include "../algorithm/set_has.hpp"
#include "../algorithm/is_in.hpp"
namespace tjp::core::log::detail {
TJP_CORE_HEADER_ONLY_INLINE
long timeSinceEpochMillisec()
{
using namespace std::chrono;
return duration_cast<microseconds>(system_clock::now().time_since_epoch()).count() % 1000000;
}
TJP_CORE_HEADER_ONLY_INLINE
bool detectIfColorIsAvailable()
{
// is the stdout redirected, if so no color
if (getenv("FORCE_COLOR"))
return true;
if (!isatty(fileno(stdout)))
return false;
const char* term = getenv("TERM");
if (!term)
return false;
// Some terminals support color
std::string_view term_(term);
if (term_ == "dumb")
return false;
return true;
}
using ThreadID = uint64_t;
ThreadID getCurrentThreadID();
TJP_CORE_HEADER_ONLY_INLINE
ThreadID getCurrentThreadID ()
{
#ifdef WIN32
return GetCurrentThreadId();
#else
return (ThreadID)pthread_self();
#endif
}
TJP_CORE_HEADER_ONLY_INLINE
char * GetTimeStamp (char *data, int size, char marker=0)
{
#ifdef WIN32
__time64_t ltime;
_time64( &ltime );
char *timestr = _ctime64(&ltime);
// auto threadID = getCurrentThreadID();
#else
std::time_t t = std::time(nullptr);
char timestr[128];
std::strftime(timestr, size, "%Y%m%d_%H:%M:%S", std::localtime(&t));
// auto threadID = getCurrentThreadID();
#endif
snprintf (data, size, "%s.%06ld", timestr, timeSinceEpochMillisec());
if (marker)
for (auto *c=data; *c; ++c)
if (*c == ':' || *c == '.')
*c = marker;
return data;
}
TJP_CORE_HEADER_ONLY_INLINE
char * __GetLogStamp__ (char *data, bool colorize)
{
char timeStamp[128];
GetTimeStamp(timeStamp, std::size(timeStamp));
auto threadID = getCurrentThreadID();
auto dataSize = 256;
if (colorize)
{
// all of the threadIDs are divisible by 128, so we use 127
auto color = (int)threadID % 127 + 1;
#if defined(SYS_LINUX) || defined(SYS_ANDROID)
snprintf (data, dataSize, "%s \033[38;5;%dm%lx\033[0m | ", timeStamp, color, threadID);
#else
snprintf (data, dataSize, "%s \033[38;5;%dm%llx\033[0m | ", timeStamp, color, threadID);
#endif
}
else
{
#if defined(SYS_LINUX) || defined(SYS_ANDROID)
snprintf (data, dataSize, "%s %lx | ", timeStamp, threadID);
#else
snprintf (data, dataSize, "%s %llx | ", timeStamp, threadID);
#endif
}
return data;
}
TJP_CORE_HEADER_ONLY_INLINE
std::string methodName(const char *prettyFunction_)
{
if (!prettyFunction_)
return "[]";
std::string_view prettyFunction(prettyFunction_);
auto end = prettyFunction.find('(');
if (end == std::string_view::npos)
end = 0;
auto begin = end;
auto stack = 0;
decltype(begin) skipEnd = 0;
decltype(begin) skipBegin = 0;
for (begin = end; begin > 0; begin--)
{
if (prettyFunction[begin] == '>')
{
if (stack == 0)
skipEnd = begin;
stack++;
}
if (prettyFunction[begin] == '<')
{
stack--;
if (stack == 0)
skipBegin = begin;
}
if (prettyFunction[begin] == ' ' && stack == 0)
break;
}
while (begin < end && is_in(prettyFunction[begin], ' ', '&', '*'))
begin = begin+1;
std::string part;
if (skipBegin != skipEnd)
part = std::string(prettyFunction.substr(begin,skipBegin-begin+1)) + std::string(prettyFunction.substr(skipEnd, end-skipEnd));
else
part = prettyFunction.substr(begin,end-begin);
if (part.size() > 30)
return part.substr(part.size() - 30);
return part;
}
} // namespace

96
tjp/core/log/_tests/LogOf.cpp Executable file
View File

@@ -0,0 +1,96 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#include <tjp/core/log/LogOf.h>
#include <tjp/core/testing/catch.hpp>
#include <tjp/core/string/String.hpp>
#include <tjp/core/containers/Map.hpp>
#include <tjp/core/containers/Vector.hpp>
#include <tjp/core/containers/Optional+IO.hpp>
namespace tjp::core::log {
namespace {
struct S {
int i = 0;
};
std::ostream &operator <<(std::ostream &o, const S &s)
{
return o << "S: " << s.i;
}
SCENARIO("core::log::LogOf")
{
GIVEN("a struct")
{
S x { 42 };
WHEN("serialize with logOf")
{
std::ostringstream s;
s << logOf(x);
String str = s.str();
REQUIRE(str == "S: 42");
}
}
GIVEN("a vector")
{
Vector<int> x = { 1, 2, 3, 4 };
WHEN("serialize with logOf")
{
std::ostringstream s;
s << logOf(x);
String str = s.str();
REQUIRE(str == "[1,2,3,4]");
}
}
GIVEN("a map")
{
std::map<int, int> x = { {1,1}, {2,2}, {3,3}, {4,4} };
WHEN("serialize with logOf")
{
std::ostringstream s;
s << logOf(x);
String str = s.str();
REQUIRE(str == "[(1,1),(2,2),(3,3),(4,4)]");
}
}
GIVEN("an optional")
{
std::optional<int> x;
WHEN("serialize with logOf")
{
std::ostringstream s;
s << logOf(x);
String str = s.str();
REQUIRE(str == "null");
}
WHEN("serialize value with logOf")
{
x = 1;
std::ostringstream s;
s << logOf(x);
String str = s.str();
REQUIRE(str == "1");
}
}
}
} // namespace
} // namespace

16
tjp/core/log/demangle.cpp Executable file
View File

@@ -0,0 +1,16 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#include <cxxabi.h>
namespace tjp::core {
char * demangle(const char *mangled)
{
int status;
char * demangled = abi::__cxa_demangle(mangled,0,0,&status);
return demangled;
}
} // namespace

11
tjp/core/log/demangle.h Executable file
View File

@@ -0,0 +1,11 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#pragma once
namespace tjp::core {
char *demangle(const char *mangled);
} // namespace