diff --git a/examples/simple/simple.cpp b/examples/simple/simple.cpp index 6dc00e8..8e55581 100644 --- a/examples/simple/simple.cpp +++ b/examples/simple/simple.cpp @@ -1,29 +1,29 @@ /******************************************************************************* * * File: simple.cpp * * Contents: A very simple example to use RoSA Core library. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #include "rosa/config/version.h" #include "rosa/core/Unit.h" #include "rosa/support/log.h" #include "rosa/support/terminal_colors.h" #include using namespace rosa; using namespace rosa::terminal; int main(void) { LOG_INFO_STREAM << library_string() << " -- " << Color::Red << "simple example" << Color::Default << std::endl; Unit Unit1, Unit2("Second"), Unit3; - LOG_TRACE_STREAM << Unit2 << std::endl; + LOG_TRACE_STREAM << "Dumping Unit2" << std::endl << Unit2 << std::endl; return 0; } diff --git a/include/rosa/config/config.h b/include/rosa/config/config.h index 7e627a5..e340325 100644 --- a/include/rosa/config/config.h +++ b/include/rosa/config/config.h @@ -1,56 +1,56 @@ /******************************************************************************* * * File: config.h * * Contents: Configuration nformation on the build of the library. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #ifndef ROSA_CONFIG_CONFIG_H #define ROSA_CONFIG_CONFIG_H #include // Various preprocessor macros containing config information. #include "rosa/config/rosa_config.h" // This OS-specific block defines one of the following: // - ROSA_LINUX // - ROSA_WINDOWS -// It also defines ROSA_POSIX for POSIX-compatible systems +// It also defines ROSA_POSIX for POSIX-compatible systems. #if defined(__linux__) #define ROSA_LINUX #elif defined(WIN32) || defined(_WIN32) #define ROSA_WINDOWS #else #error Platform and/or compiler not supported #endif #if defined(ROSA_LINUX) #define ROSA_POSIX #endif // Defining filenames in a project-relative way based on absolute paths. #include "rosa/config/project_path.hpp" #define __FILENAME__ (__FILE__ + project_relative_path_index(__FILE__)) // Convenience macros. #define ROSA_VOID_STMT static_cast(0) #define ROSA_IGNORE_UNUSED(x) static_cast(x) #define ROSA_CRITICAL(error) \ do { \ std::cerr << "[FATAL] " << __func__ << "@" << __FILENAME__ << ":" \ << __LINE__ << ": critical error: '" << (error) << "'" \ << std::endl; \ ::abort(); \ } while (false) #define ROSA_RAISE_ERROR(msg) throw std::runtime_error(msg) #endif // ROSA_CONFIG_CONFIG_H diff --git a/include/rosa/core/Unit.h b/include/rosa/core/Unit.h index 355912c..c2c43ba 100644 --- a/include/rosa/core/Unit.h +++ b/include/rosa/core/Unit.h @@ -1,47 +1,59 @@ +/******************************************************************************* + * + * File: Unit.h + * + * Contents: Declaration of Unit base-class. + * + * Copyright 2017 + * + * Author: David Juhasz (david.juhasz@tuwien.ac.at) + * + ******************************************************************************/ + #ifndef ROSA_CORE_UNIT_H #define ROSA_CORE_UNIT_H #include #include #include namespace rosa { // Base class for every entity in the system that has to be identified and // traced. class Unit { private: // Number of Units constructed in the system. static std::atomic CountUnits; // Number of live units, those having been constructed and not yet destroyed. static std::atomic LiveUnits; public: // System-assigned unique identifier of the Unit instance, // based on the static member field CountUnits. const uint64_t Id; // Textual identifier of the Unit instance. Defaults to a text referring to // the Id value of the Unit, unless otherwise defined via a constructor // argument. The Name of a Unit is not necessarily unique in the system. const std::string Name; // Ctor. Unit(const std::string &Name = std::string()) noexcept; // Dtor. virtual ~Unit(void) noexcept; // Dumping the object into a string for tracing purposes, // subclasses are supposed to override this function. virtual std::string dump(void) const noexcept; }; // Helper function dumping the given Unit instance to the given output stream. std::ostream &operator<<(std::ostream &os, const Unit &unit); } // End namespace rosa #endif // ROSA_CORE_UNIT_H diff --git a/include/rosa/support/log.h b/include/rosa/support/log.h index 7f1cce6..0a4e905 100644 --- a/include/rosa/support/log.h +++ b/include/rosa/support/log.h @@ -1,148 +1,150 @@ /******************************************************************************* * * File: log.h * * Contents: Facility for logging * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #ifndef ROSA_SUPPORT_LOG_H #define ROSA_SUPPORT_LOG_H #include "rosa/config/config.h" #include "rosa/support/terminal_colors.h" #include #include /* **************************************************************************** * Log Levels * * ****************************************************************************/ namespace rosa { // Type-safe definition of log levels, use this in code. // NOTE: Keep values in sync with the corresponding preprocessor definitions // below. enum class LogLevel { Error, Warning, Info, Debug, Trace, // Number of log levels: NumLogLevels }; // Converts a LogLevel to its string representation. +// PRE: logLevel != LogLevel::NumLogLevels std::string logLevelToString(const LogLevel logLevel); // Prints colorized tag for the given LogLevel. +// PRE: logLevel != LogLevel::NumLogLevels std::ostream &operator<<(std::ostream &os, const LogLevel logLevel); } // End namespace rosa // Valid log levels, only for preprocessor definitions below. // NOTE: Keep in sync with the values of enum class rosa::LogLevel. #define ROSA_LOG_LEVEL_ERROR 0 #define ROSA_LOG_LEVEL_WARNING 1 #define ROSA_LOG_LEVEL_INFO 2 #define ROSA_LOG_LEVEL_DEBUG 3 #define ROSA_LOG_LEVEL_TRACE 4 /* **************************************************************************** * Logger Implementation * * ****************************************************************************/ // Stream to print logs to. // FIXME: Make it configurable, e.g. printing into a file. #define ROSA_LOG_OSTREAM std::clog // Simple logging implementation printing a string message. // FIXME: Make logging thread-safe. #define ROSA_LOG_IMPL(level, output) \ do { \ ROSA_LOG_OSTREAM << level << " " << __func__ << "@" << __FILENAME__ << ":" \ << __LINE__ << ": " << (output) << std::endl; \ } while (false) // Simple logging implementation providing a stream to print to. // FIXME: Make logging thread-safe. #define ROSA_LOG_STREAM_IMPL(level) \ do { \ ROSA_LOG_OSTREAM << level << " " << __func__ << "@" << __FILENAME__ << ":" \ << __LINE__ << ": "; \ } while (false); \ ROSA_LOG_OSTREAM namespace rosa { // Dummy ostream printing to nowhere. extern std::ostream LogSink; } // End namespace rosa // A stream ignoring all its input. #define ROSA_LOG_STREAM_IGNORE rosa::LogSink /* **************************************************************************** * Logging Interface * * ****************************************************************************/ // Define logging macros if logging is enabled. #ifdef ROSA_LOG_LEVEL #define LOG_ERROR_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Error) #define LOG_ERROR(output) ROSA_LOG_IMPL(rosa::LogLevel::Error, output) #if ROSA_LOG_LEVEL >= ROSA_LOG_LEVEL_WARNING #define LOG_WARNING_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Warning) #define LOG_WARNING(output) ROSA_LOG_IMPL(rosa::LogLevel::Warning, output) #endif #if ROSA_LOG_LEVEL >= ROSA_LOG_LEVEL_INFO #define LOG_INFO_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Info) #define LOG_INFO(output) ROSA_LOG_IMPL(rosa::LogLevel::Info, output) #endif #if ROSA_LOG_LEVEL >= ROSA_LOG_LEVEL_DEBUG #define LOG_DEBUG_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Debug) #define LOG_DEBUG(output) ROSA_LOG_IMPL(rosa::LogLevel::Debug, output) #endif #if ROSA_LOG_LEVEL >= ROSA_LOG_LEVEL_TRACE #define LOG_TRACE_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Trace) #define LOG_TRACE(output) ROSA_LOG_IMPL(rosa::LogLevel::Trace, output) #endif #endif // defined ROSA_LOG_LEVEL // Define all disabled logging features as void. #ifndef LOG_ERROR #define LOG_ERROR_STREAM ROSA_LOG_STREAM_IGNORE #define LOG_ERROR(output) ROSA_IGNORE_UNUSED(output) #endif #ifndef LOG_WARNING #define LOG_WARNING_STREAM ROSA_LOG_STREAM_IGNORE #define LOG_WARNING(output) ROSA_IGNORE_UNUSED(output) #endif #ifndef LOG_INFO #define LOG_INFO_STREAM ROSA_LOG_STREAM_IGNORE #define LOG_INFO(output) ROSA_IGNORE_UNUSED(output) #endif #ifndef LOG_DEBUG #define LOG_DEBUG_STREAM ROSA_LOG_STREAM_IGNORE #define LOG_DEBUG(output) ROSA_IGNORE_UNUSED(output) #endif #ifndef LOG_TRACE #define LOG_TRACE_STREAM ROSA_LOG_STREAM_IGNORE #define LOG_TRACE(output) ROSA_IGNORE_UNUSED(output) #endif #endif // ROSA_SUPPORT_LOG_H diff --git a/include/rosa/support/terminal_colors.h b/include/rosa/support/terminal_colors.h index fee3b4e..e39e6e6 100644 --- a/include/rosa/support/terminal_colors.h +++ b/include/rosa/support/terminal_colors.h @@ -1,51 +1,52 @@ /******************************************************************************* * * File: terminal_colors.h * * Contents: Facility for printing colorized text to terminals supporting it. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #ifndef ROSA_SUPPORT_TERMINAL_COLORS_H #define ROSA_SUPPORT_TERMINAL_COLORS_H #include namespace rosa { namespace terminal { // Text colors for colorizable terminals. enum class Color { Default, Black, Red, Green, Yellow, Blue, Magenta, Cyan, Lightgrey, Darkgrey, Lightred, Lightgreen, Lightyellow, Lightblue, LightMagenta, Lightcyan, White, // Number of Color values: NumColors }; // Operator handling Color commands sent to output streams. +// PRE: color != Color::NumColors std::ostream &operator<<(std::ostream &os, const Color color); } // End namespace terminal } // End namespace rosa #endif // ROSA_SUPPORT_TERMINAL_COLORS_H diff --git a/lib/core/Unit.cpp b/lib/core/Unit.cpp index cafdc51..a9fc962 100644 --- a/lib/core/Unit.cpp +++ b/lib/core/Unit.cpp @@ -1,40 +1,52 @@ +/******************************************************************************* + * + * File: Unit.cpp + * + * Contents: Implementation of Unit base-class. + * + * Copyright 2017 + * + * Author: David Juhasz (david.juhasz@tuwien.ac.at) + * + ******************************************************************************/ + + #include "rosa/core/Unit.h" #include "rosa/support/debug.hpp" #include "rosa/support/log.h" namespace rosa { - // Zero-initialized definitions of static members. // NOTE: Initialization is not atomic. std::atomic Unit::CountUnits(0); std::atomic Unit::LiveUnits(0); // Ctor. Incrementing static counters and initializing member fields. Unit::Unit(const std::string &Name) noexcept : Id(++CountUnits), Name(Name.empty() ? "Unit_" + std::to_string(Id) : Name) { LOG_TRACE("Constructing Unit (" + this->Name + ")"); ++LiveUnits; } // Dtor. Decrementing static live counter. Unit::~Unit(void) noexcept { LOG_TRACE("Destroying Unit (" + Name + ")"); --LiveUnits; ASSERT(LiveUnits >= 0); } // Default dump function, emitting the Name of the Unit. std::string Unit::dump(void) const noexcept { LOG_TRACE("Dumping Unit (" + Name + ")"); return "[Unit] " + Name; } std::ostream &operator<<(std::ostream &os, const Unit &unit) { os << unit.dump(); return os; } } // End namespace rosa diff --git a/lib/support/log.cpp b/lib/support/log.cpp index dcfe85c..52743f0 100644 --- a/lib/support/log.cpp +++ b/lib/support/log.cpp @@ -1,50 +1,52 @@ /******************************************************************************* * * File: log.cpp * * Contents: Facility for logging * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #include "rosa/support/debug.hpp" #include "rosa/support/log.h" #include "rosa/support/terminal_colors.h" #include namespace rosa { // Textual representation of LogLevels. +// NOTE: Do not index with LogLevel::NumLogLevels! constexpr std::array(LogLevel::NumLogLevels)> LogLevelStrings{{"ERROR", "WARNING", "INFO", "DEBUG", "TRACE"}}; // Terminal colors associated to LogLevels. +// NOTE: Do not index with LogLevel::NumLogLevels! constexpr std::array(LogLevel::NumLogLevels)> LogLevelColors{{ terminal::Color::Red, // LogLevel::Error terminal::Color::Yellow, // LogLevel::Warning terminal::Color::Green, // LogLevel::Info terminal::Color::Blue, // LogLevel::Debug terminal::Color::Default, // LogLevel::Trace }}; std::string logLevelToString(const LogLevel logLevel) { ASSERT(logLevel != LogLevel::NumLogLevels); return LogLevelStrings[static_cast(logLevel)]; } std::ostream &operator<<(std::ostream &os, const LogLevel logLevel) { ASSERT(logLevel != LogLevel::NumLogLevels); os << LogLevelColors[static_cast(logLevel)] << "[" << logLevelToString(logLevel) << "]" << terminal::Color::Default; return os; } std::ostream LogSink(nullptr); } // End namespace rosa diff --git a/lib/support/terminal_colors.cpp b/lib/support/terminal_colors.cpp index 9a4c84f..c4b3e4d 100644 --- a/lib/support/terminal_colors.cpp +++ b/lib/support/terminal_colors.cpp @@ -1,63 +1,73 @@ /******************************************************************************* * * File: terminal_colors.cpp * * Contents: Facility for printing colorized text to terminals supporting it. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #include "rosa/support/terminal_colors.h" #include "rosa/support/debug.hpp" #include "rosa/config/config.h" #include +#ifdef ROSA_POSIX +#include +#endif // defined(ROSA_POSIX) + namespace rosa { namespace terminal { -#ifdef ROSA_POSIX // ANSI color codes for POSIX terminals. // NOTE: Use std::array for runtime efficiency, though static casting is // necessary for indexing. +// NOTE: Do not index with Color::NumColors! constexpr std::array(Color::NumColors)> ANSIColorCodes{{ "0", // Default - Attribute reset "30", // Black "31", // Red "32", // Green "33", // Yelow "34", // Blue "35", // Magenta "36", // Cyan "37", // Lightgrey "90", // Darkgrey "91", // Lightred "92", // Lightgreen "93", // Lightyellow "94", // Lightblue "95", // Lightmagenta "96", // Lightcyan "97" // White }}; -#endif // defined ROSA_POSIX std::ostream &operator<<(std::ostream &os, const Color color) { ASSERT(color != Color::NumColors); // Handle color only when printing to terminal and it supports colors. #ifdef ROSA_POSIX - // FIXME: Make terminal identification more robust! What if rdbuf of a - // standard stream is changed in the software? - if (os.rdbuf() == std::cout.rdbuf() || os.rdbuf() == std::cerr.rdbuf() || - os.rdbuf() == std::clog.rdbuf()) { + // Identify terminal by checking if the stream is buffered as one of the + // standard outputs, and the corresponding C standard output stream is + // pointing to a terminal. + // NOTE: This implementation may be tricked so that it does not print color + // to terminal, but should never print color codes to a non-terminal output. + if ((os.rdbuf() == std::cout.rdbuf() && isatty(fileno(stdin))) || + ((os.rdbuf() == std::cerr.rdbuf() || os.rdbuf() == std::clog.rdbuf()) && + isatty(fileno(stderr)))) { os << "\033[" << ANSIColorCodes[static_cast(color)] << "m"; } -#endif // defined ROSA_POSIX +#elif defined(ROSA_WINDOWS) + // FIXME: Windows terminals support ANSI color codes from Windows 10 + // Threshold 2, custom stuff is required for earlier versions. +#endif // defined(ROSA_POSIX) || defined(ROSA_WINDOWS) return os; } } // End namespace terminal } // End namespace rosa