Виртуальный метод с переменными аргументами

Я пытаюсь реализовать систему ведения журнала, абстрагирующуюся от службы локатора (в стиле это руководство) таким образом, что система регистрации может быть разделена на подклассы для различных различных ситуаций регистрации. Я предпочел бы иметь возможность использовать строки формата printf вместо <<, но это означает поддержку переменного количества аргументов. Шаблоны Variadic могут легко решить эту проблему, однако они не могут быть виртуальными, что означает, что базовый класс ведения журнала не может быть абстрактным (как интерфейс).

В идеале мне нужен какой-то способ, чтобы родительский метод не был шаблонным, а просто принимал пакет параметров, который он передавал бы правильному (шаблонному) дочернему методу. В основном я видел два способа сделать это, один из которых использует va_list, который, по-видимому, небезопасен, сложен и на самом деле не предназначен для легкого взаимодействия с переменными шаблонами, и CRTP, который будет работать, но означает, что нельзя указывать указатель на абстрактный базовый класс в объекте локатора, не зная тип подкласса, который побеждает цель.

Вот пример кода, предполагающий, что виртуальные шаблоны были чем-то особенным:

class Logger
{
public:
template <typename ... Args>
virtual void print(char *filename, int line, std::string &&msg, Args&& ... args) = 0;

protected:
template <typename ... Args>
std::string format(char *filename, int line, std::string &msg, Args&& ... args)
{
std::string output = "%s at line %i: " + msg;
int size = std::snprintf(nullptr, 0, output.c_str());
// +1 for null termination
std::vector<char> buf(size + 1);
std::snprintf(&buf[0], buf.size(), output.c_str(), filename, line, args...);
return std::string(&buf[0]);
}};

class PrintLogger : public Logger
{
public:
template <typename ... Args>
void print(char *filename, int line, std::string &&msg, Args&& ... args)
{
std::cout << format(filename, line, msg, args...);
}
};

Вот пример кода с решением CRTP (которое ломает локатор):

template <typename Loggertype>
class Logger
{
public:
template <typename ... Args>
void print(char *filename, int line, std::string &&msg, Args&& ... args)
{
Loggertype::print(filename, line, msg, args...);
}

protected:
template <typename ... Args>
std::string format(char *filename, int line, std::string &msg, Args&& ... args)
{
std::string output = "%s at line %i: " + msg;
int size = std::snprintf(nullptr, 0, output.c_str());
// +1 for null termination
std::vector<char> buf(size + 1);
std::snprintf(&buf[0], buf.size(), output.c_str(), filename, line, args...);
return std::string(&buf[0]);
}};

class PrintLogger : public Logger<PrintLogger>
{
public:
template <typename ... Args>
void print(char *filename, int line, std::string &&msg, Args&& ... args)
{
std::cout << format(filename, line, msg, args...);
}
};

А вот и локатор:

class Global {
public:
static void initialize() { logger = nullptr; }
static Logger& logging()
{
if (logger == nullptr)
{
throw new std::runtime_error("Attempt to log something before a logging instance was created!");
}
return *logger;
}
static void provide_logging(Logger *new_logger) { logger = new_logger; }
private:
static Logger *logger;
};

1

Решение

Вы должны решить 2 вопроса:

  1. итерация на упакованном аргумент
  2. Вызовите виртуальную функцию с аргументом шаблона.

Первая проблема, которую вам нужно, это обработать с помощью функции рекурсии
Второй я использовал с boost::any

class Logger
{
public:template <typename ... Args>
std::string print(Args&& ... args)
{
start_format();
interate_on(args...);
return end_format();
}
protected:template <typename Arg>
void interate_on(Arg&& arg)
{
print(arg);
}template <typename Arg, typename ... Args>
void interate_on(Arg&& arg, Args&& ... args)
{
print(arg);
interate_on(std::forward<Args>(args)...);
}

virtual void start_format() = 0;

virtual std::string end_format() = 0;

virtual void print(boost::any& value) = 0;
};

class PrinterLogger : public Logger
{
protected:
virtual void start_format()
{

}

virtual std::string end_format()
{
return std::string();
}

virtual void print(boost::any& value)
{
// accumulate printf

}
};
1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]