Implementing a robust logging system using RTTI can greatly enhance debugging by providing detailed type information. Here's an approach to create such a system:
Define a base Loggable
class:
#include <typeinfo>
#include <string>
class Loggable {
public:
virtual ~Loggable() = default;
virtual std::string getLogInfo() const {
return std::string("Type: ") +
typeid(*this).name();
}
};
Implement a Logger
class:
#include <typeinfo>
#include <string>
#include <chrono>
#include <iostream>
class Loggable {/*...*/}
class Logger {
public:
enum class LogLevel {
DEBUG, INFO, WARNING, ERROR
};
static void log(
LogLevel level,
const Loggable& obj,
const std::string& message
) {
std::ostringstream oss;
oss << getCurrentTimestamp() << " ["
<< getLevelString(level) << "] "
<< obj.getLogInfo() << " - " << message;
std::cout << oss.str() << std::endl;
}
static std::string getCurrentTimestamp() {
using namespace std::chrono;
auto now = system_clock::now();
auto time = system_clock::to_time_t(now);
std::stringstream ss;
ss << std::put_time(
std::localtime(&time), "%Y-%m-%d %H:%M:%S"
);
return ss.str();
}
static std::string getLevelString(
LogLevel level) {
switch (level) {
case LogLevel::DEBUG:
return "DEBUG";
case LogLevel::INFO:
return "INFO";
case LogLevel::WARNING:
return "WARNING";
case LogLevel::ERROR:
return "ERROR";
default:
return "UNKNOWN";
}
}
};
Create loggable classes:
#include <typeinfo>
#include <string>
#include <chrono>
#include <iostream>
class Loggable {/*...*/}
class Logger {/*...*/}
class Player : public Loggable {
public:
Player(const std::string& name, int health)
: name_(name), health_(health) {}
std::string getLogInfo() const override {
std::ostringstream oss;
oss << Loggable::getLogInfo() << ", Name: "
<< name_ << ", Health: " << health_;
return oss.str();
}
void takeDamage(int Damage) {
// ...
}
private:
std::string name_;
int health_;
};
class Item : public Loggable {
public:
Item(const std::string& name, int value)
: name_(name), value_(value) {}
std::string getLogInfo() const override {
std::ostringstream oss;
oss << Loggable::getLogInfo() << ", Name: "
<< name_ << ", Value: " << value_;
return oss.str();
}
private:
std::string name_;
int value_;
};
Implement a template function for logging types that do not inherit from Loggable
:
#include <typeinfo>
#include <string>
#include <chrono>
#include <iostream>
class Loggable {/*...*/}
class Logger {/*...*/}
class Player : public Loggable {/*...*/}
class Item : public Loggable {/*...*/}
template <typename T>
void logValue(
Logger::LogLevel level,
const T& value,
const std::string& message
) {
std::ostringstream oss;
oss << "Type: " << typeid(value).name()
<< ", Value: " << value;
std::cout << Logger::getCurrentTimestamp()
<< " [" << Logger::getLevelString(level)
<< "] " << oss.str() << " - "
<< message << std::endl;
}
Use the logging system:
#include <typeinfo>
#include <string>
#include <chrono>
#include <iostream>
class Loggable {/*...*/}
class Logger {/*...*/}
class Player : public Loggable {/*...*/}
class Item : public Loggable {/*...*/}
void logValue(/*...*/) {/*...*/}
int main() {
Player player("Alice", 100);
Logger::log(Logger::LogLevel::INFO,
player, "Player created");
Item sword("Excalibur", 1000);
Logger::log(Logger::LogLevel::DEBUG,
sword, "Item acquired");
player.takeDamage(20);
Logger::log(Logger::LogLevel::WARNING,
player, "Player took damage");
int score = 5000;
logValue(Logger::LogLevel::INFO, score,
"Current score");
std::string gameState = "Level 2";
logValue(Logger::LogLevel::DEBUG, gameState,
"Game state changed");
try {
throw std::runtime_error("Game crash");
} catch (const std::exception& e) {
Logger::log(Logger::LogLevel::ERROR,
player, std::string("Exception: ") + e.what());
}
}
2024-06-24 03:54:24 [INFO] Type: class Player, Name: Alice, Health: 100 - Player created
2024-06-24 03:54:24 [DEBUG] Type: class Item, Name: Excalibur, Value: 1000 - Item acquired
2024-06-24 03:54:24 [WARNING] Type: class Player, Name: Alice, Health: 100 - Player took damage
2024-06-24 03:54:24 [INFO] Type: int, Value: 5000 - Current score
2024-06-24 03:54:24 [DEBUG] Type: class std::string, Value: Level 2 - Game state changed
2024-06-24 03:54:24 [ERROR] Type: class Player, Name: Alice, Health: 100 - Exception: Game crash
This logging system uses RTTI in several ways:
Loggable
class uses typeid
to provide the type name of derived classes.logValue
template function uses typeid
to get type information for non-Loggable types.Logger
class provides a consistent interface for logging both Loggable and non-Loggable types.Benefits of this approach:
getLogInfo
method.Considerations:
typeid().name()
output is implementation-defined and might not be human-readable. You might need to use a demangling function for better readability.To further enhance this system, you could:
By using RTTI in your logging system, you can create a powerful debugging tool that provides detailed type information at runtime, greatly aiding in the development and maintenance of complex C++ applications.
Answers to questions are automatically generated and may not have been reviewed.
typeid()
Learn to identify and react to object types at runtime using RTTI, dynamic casting and the typeid()
operator