#pragma once // ------------------------------------------------------------------------------------------------------------------- // _ _ // _ __ _ __ _ __ ___ __ __ ___ _ __ _ __ _ __ (_) _ __ | |_ // | '_ \ | '_ \ _____ | '_ \ / _ \ \ \ /\ / / / _ \ | '__| | '_ \ | '__| | | | '_ \ | __| // | |_) | | |_) | |_____| | |_) | | (_) | \ V V / | __/ | | | |_) | | | | | | | | | | |_ // | .__/ | .__/ | .__/ \___/ \_/\_/ \___| |_| | .__/ |_| |_| |_| |_| \__| // |_| |_| |_| |_| // // ------------------------------------------------------------------------------------------------------------------- // pp - powerprint by Olaf Kliche ( okl.de ) // tested systems: linux g++ (10/11), windows (vc2019/vc2022) // -------------------------------------------------------------------- // Output format: // [message number] [time,microsec] [thread-id] message // // Examples: // // pp("Velocity:$ after $ sec", 9.81*3, 3); // Output: // [000008] [16:41:25,247854] [310405] Velocity:29.43 after 3 sec // // Point pt {300,400}; // If stream << operator is overwritten, you can ... // pp("my point: $", pt); // [000009] [16:41:25,247855] [310405] my point: 300,400 // // $ is the general placeholder for all kind of variables. // // If you want a more precise ouput than combine pp(..) with xprf(..). // xprf - also in the header - is like c's sprintf(...) except return value is a std::string // // ---------------------------------------------------------------------- // One header only - freeware for developers by heart - (*_*) // ---------------------------------------------------------------------- // #include #include #include #include #include #include #include #include #include #ifdef _WIN32 #define NOMINMAX #define WIN32_LEAN_AND_MEAN #include #elif __linux__ #include #include #endif namespace okl { void pp(); void pp(const char * pStr); void pp(unsigned int space, const char * pStr); template void pp(const char * pFormat, const First & first, const Rest & ... rest); template void pp(unsigned int space, const char * pFormat, const First & first, const Rest & ... rest); // some string-format helpers std::string xprintF(const char * pFormat=nullptr); template std::string xprintF(unsigned int space, const char * pFormat, const First & first, const Rest & ... rest); template std::string xprintF(const char * pFormat, const First & first, const Rest & ... rest); std::string xprf(const char * format, ...); // format is like c's int sprintf ( char * str, const char * format, ... ); enum class FORM_SECOND { MICROSECOND, MILLISECOND, NO_DECIMAL_PLACES }; // default like this "16:46:24,205523" - Microseconds std::string get_time_stamp(const std::string & sFormat="%H:%M:%S", FORM_SECOND formSecond = FORM_SECOND::MICROSECOND); void pp_set_thread_ID_main(); } // -------------------------------------------------------------------------------------------------------------------------- // Implementation // -------------------------------------------------------------------------------------------------------------------------- namespace okl { template void streamIt(STREAM & stream, const char * pStr); template void streamIt(STREAM & ss, const char * format, T value, Targs... Fargs); void xSleep(unsigned long long milliSeconds); class LogPrinter { public: static void log(const std::string & s); // thread safe - called by pp(..) static void set_main_thread_id(); // optional: if called in main(..), "main thread id" is 000000 always! private: inline static std::mutex mutexWorker; inline static unsigned long long counter = 0; inline static unsigned int th_id_main = 0; static unsigned int get_current_threadID(); // want native thread id, not std::this_thread::get_id() monster id! static std::string trailler(); }; template void streamIt(STREAM & stream, const char * pStr) { if (pStr) { stream << pStr; } } template void streamIt(STREAM & ss, const char * format, T value, Targs... Fargs) { for (; *format; format++) { if (*format == '$') { //objectToStringStream(ss, value); ss << value; streamIt(ss, format + 1, Fargs...); // recursive call return; } ss << *format; } } inline std::string xprintF(const char * pFormat) { return pFormat ? pFormat : ""; } inline std::string xprf(const char * format, ...) { if (!format) { return "<<>>"; } if (strlen(format) >= 4 * 1024) { return ""; } #ifdef _WIN32 // about _CRT_SECURE_NO_WARNINGS #pragma warning( push ) #pragma warning(disable: 4996) #endif char dest[4 * 1024]; va_list argptr; va_start(argptr, format); vsprintf(dest, format, argptr); va_end(argptr); #ifdef _WIN32 #pragma warning( pop ) #endif return dest; } template std::string xprintF(unsigned int spaces, const char * pFormat, const First & first, const Rest & ... rest) { if (!pFormat) { return ""; } std::stringstream ss; ss << std::string(spaces, ' '); for (; *pFormat; pFormat++) { if (*pFormat == '$') { ss << first; pFormat++; break; } ss << *pFormat; } streamIt(ss, pFormat, rest ...); return ss.str(); } template std::string xprintF(const char * pFormat, const First & first, const Rest & ... rest) { if (!pFormat) { return ""; } std::stringstream ss; for (; *pFormat; pFormat++) { if (*pFormat == '$') { ss << first; pFormat++; break; } ss << *pFormat; } streamIt(ss, pFormat, rest ...); return ss.str(); } inline std::string get_time_stamp(const std::string & sFormat, FORM_SECOND formSecond) { std::chrono::time_point timeNow = std::chrono::system_clock::now(); const char * pDateTimeFormat = sFormat.c_str(); auto duration = timeNow.time_since_epoch(); std::chrono::system_clock::time_point tp = std::chrono::system_clock::time_point(duration); auto ttime_t = std::chrono::system_clock::to_time_t(tp); auto tp_sec = std::chrono::system_clock::from_time_t(ttime_t); #ifdef _WIN32 // about _CRT_SECURE_NO_WARNINGS #pragma warning( push ) #pragma warning(disable: 4996) #endif std::tm * ttm = localtime(&ttime_t); #ifdef _WIN32 #pragma warning( pop ) #endif char time_str[512]; memset(time_str, 0, sizeof(time_str)); // %H:%M:%S strftime(time_str, sizeof(time_str) - 1, pDateTimeFormat, ttm); std::string result(time_str); std::stringstream ssAfter; if (formSecond == FORM_SECOND::MICROSECOND) { result.append(","); std::chrono::microseconds mcc = std::chrono::duration_cast(tp - tp_sec); uint64_t m = mcc.count(); ssAfter << std::setw(6) << std::setfill('0') << (long long) m; } else if (formSecond == FORM_SECOND::MILLISECOND) { result.append(","); std::chrono::milliseconds mcc = std::chrono::duration_cast(tp - tp_sec); uint64_t m = mcc.count(); ssAfter << std::setw(3) << std::setfill('0') << (long long) m; } result.append(ssAfter.str()); return result.c_str(); } inline void pp() { LogPrinter::log(""); } inline void pp(const char * pStr) { LogPrinter::log(pStr); } inline void pp(unsigned int space, const char * pStr) { std::string s = xprintF(space, "$", pStr); LogPrinter::log(s); } template inline void pp(const char * pFormat, const First & first, const Rest & ... rest) { LogPrinter::log(xprintF(pFormat, first, rest ...)); } template inline void pp(unsigned int space, const char * pFormat, const First & first, const Rest & ... rest) { LogPrinter::log(xprintF(space, pFormat, first, rest ...)); } inline void LogPrinter::set_main_thread_id() { using SCOPE_LOCKER = std::scoped_lock; // in all other situations SCOPE_LOCKER locker(mutexWorker); #ifdef _WIN32 LogPrinter::th_id_main = (unsigned int) GetCurrentThreadId(); #else LogPrinter::th_id_main = (unsigned int) syscall(SYS_gettid); #endif } inline void pp_set_thread_ID_main() { LogPrinter::set_main_thread_id(); } inline unsigned int LogPrinter::get_current_threadID() { unsigned int thID = 0; #ifdef _WIN32 thID = (unsigned int) GetCurrentThreadId(); #else thID = (unsigned int) syscall(SYS_gettid); #endif // if (thID =) return LogPrinter::th_id_main == thID ? 0 : thID; } inline std::string LogPrinter::trailler() { std::stringstream ss; ss << xprf("[%06d] ", counter++); ss << xprintF("[$] ", get_time_stamp("%H:%M:%S", FORM_SECOND::MICROSECOND)); ss << xprf("[%06d]", get_current_threadID()); // 6 digits ar normally enough // ss << std::this_thread::get_id(); // I DO NOT WANT 140139344544704 ! ss << " "; return ss.str(); } inline void LogPrinter::log(const std::string & s) { using SCOPE_LOCKER = std::scoped_lock; // in all other situations SCOPE_LOCKER locker(mutexWorker); std::cout << trailler(); std::cout << s; std::cout << "\n"; } }