Let us start with the required includes.
#include <cassert> #include <iostream> #include <map>
Providing the log level as a string, can lead to errors, since the compiler cannot check for typos. Hence an enum::class would be a better choice for determining the log level.
enum class LogLevel { DEBUG, INFO, WARNING, ERROR };
C++ does not offer a way to obtain a function pointer given a string. After compiling and linking the function names all have been replaced by their appropriate addresses in memory. Hence, we first need to store the function pointers, in a way that allows us to look them up as needed. For this purpose you can use a static class attribute, and store the function pointers in a std::map.
class Logger { public: Logger(); ~Logger(); void complain(LogLevel level); private: void debug() const; void info() const; void warning() const; void error() const; using HandlerMap = std::map<LogLevel, void (Logger::*)(void) const>; static HandlerMap handlers; }; Logger::HandlerMap Logger::handlers{ {LogLevel::DEBUG, &Logger::debug}, {LogLevel::INFO, &Logger::info}, {LogLevel::WARNING, &Logger::warning}, {LogLevel::ERROR, &Logger::error} };
The complain method then just needs to look up the correct function pointer and call the method.
void Logger::complain(LogLevel level) { assert(handlers.find(level) != handlers.end()); (this->*handlers[level])(); }
The remaining functions look as follows.
Logger::Logger() {} Logger::~Logger() {} void Logger::debug() const { std::cout << "debug" << std::endl; } void Logger::info() const { std::cout << "info" << std::endl; } void Logger::warning() const { std::cout << "warning" << std::endl; } void Logger::error() const { std::cout << "error" << std::endl; } int main(int argc, char* argv[]) { Logger k; k.complain(LogLevel::DEBUG); k.complain(LogLevel::INFO); k.complain(LogLevel::WARNING); k.complain(LogLevel::ERROR); }
Note, if you insist on using strings, you can replace LogLevel by std::string and LogLevel::<member> by the corresponding string.
The same can be achived using C++98. However, you will need a bit more bootstrapping.
#include <cassert> #include <iostream> #include <map> enum LogLevel { LL_DEBUG, LL_INFO, LL_WARNING, LL_ERROR }; class Logger { public: Logger(); ~Logger(); void complain(LogLevel level); typedef std::map<LogLevel, void (Logger::*)() const> HandlerMap; friend struct LoggerInit; private: void debug() const; void info() const; void warning() const; void error() const; static HandlerMap handlers; }; Logger::HandlerMap Logger::handlers = Logger::HandlerMap(); struct LoggerInit { LoggerInit() { Logger::handlers[LL_DEBUG] = &Logger::debug; Logger::handlers[LL_INFO] = &Logger::info; Logger::handlers[LL_WARNING] = &Logger::warning; Logger::handlers[LL_ERROR] = &Logger::error; } } logger_init; void Logger::complain(LogLevel level) { assert(handlers.find(level) != handlers.end()); (this->*handlers[level])(); } Logger::Logger() {} Logger::~Logger() {} void Logger::debug() const { std::cout << "debug" << std::endl; } void Logger::info() const { std::cout << "info" << std::endl; } void Logger::warning() const { std::cout << "warning" << std::endl; } void Logger::error() const { std::cout << "error" << std::endl; } int main(int argc, char* argv[]) { Logger k; k.complain(LL_DEBUG); k.complain(LL_INFO); k.complain(LL_WARNING); k.complain(LL_ERROR); }
Karen::debugis not actually nameddebugin the end. To that point, you cannot select a function by string name to call at runtime; you need to resolve it at compile-time. Probably your teacher wants your method to receive a function pointer instead of a string, and it's the caller's job to specify the correct one.complainis really supposed to receive astd::string, then you are going to need some sort of lookup mechanism to determine which function to call based on the contents of the string. There is no way to convert a string into a pointer to member.std::(unordered_)mapwhich would map strings to functions, if that counts as "not being a forest of if/elseif/else".