因此,基本上,我正在寻找一种方法来创建具有针对不同平台的各种实现的接口。通常,这很简单,但是我想要为记录器创建一个接口,并且能够具有以下功能是我的目标:
string
现在显然这不能用,因为您不能拥有纯虚拟模板功能。
我看到的一种方法是使Log类成为CRTP(奇怪地重复出现的模板模式)类,并使用类似以下的方法来获得所需的行为:
class Log {
public:
template<typename ...Args>
virtual void log(const char* fmt, const Args&... args) const = 0;
template<typename ...Args>
virtual void warn(const char* fmt, const Args&... args) const = 0;
template<typename ...Args>
virtual void error(const char* fmt, const Args&... args) const = 0;
};
不利之处在于,这使该界面难以使用,经常需要使用//Log.h (template<typename T> class Log {...})
template<typename ...Args>
void log(const char* fmt, const Args&... args) const {
//the following line would essentially downcast the 'this' pointer and
//call it's version of log instead...
reinterpret_cast<T*>(this)->log(fmt, args...);
}
//ExampleLog.h (class ExampleLog : public Log<ExampleLog> {...})
template<typename ...Args>
void log(const char* fmt, const Args&... args) const {
m_logger->log(fmt, args...);
}
(实现)可能不为人知/暴露的Log<Impl>
。
我真的迷失了如何创建一个接口,该接口包含具有可变数量和参数类型的函数...?
答案 0 :(得分:2)
无论您是否有错误,警告或其他原因,所有这些都归结为一个记录下来的文本字符串。您的虚函数仅需要将单个std::string
作为参数,并且Log
基类中的所有这些函数将负责将参数格式化为单个std::string
,然后调用真正的虚拟功能:
class Log {
public:
template<typename ...Args>
void log(const char* fmt, Args && ...args) const
{
do_log(do_format(std::forward<Args>(args)...));
}
template<typename ...Args>
void warn(const char* fmt, Args && ...args) const
{
do_warn(do_format(std::forward<Args>(args)...));
}
template<typename ...Args>
void error(const char* fmt, Args && ...args) const
{
do_error(do_format(std::forward<Args>(args)...));
}
private:
template<typename ...Args>
inline std::string do_format(const char *fmt, Args && ...args)
{
// Your homework assignment goes here
}
virtual void do_log(const std::string &) const = 0;
virtual void do_warn(const std::string &) const = 0;
virtual void do_error(const std::string &) const = 0;
};
因此,现在剩下要做的就是实现do_format()
。无论如何,这是您必须在日志记录类中执行的操作,但是需要在此处完成,有效地对所有模板参数进行类型擦除,并用单个std::string
替换它们。如果您的日志记录功能归结为记录std::string
以外的内容,那么请构造此处的内容,这就是将其投入虚拟函数的原因。
关于您的可变参数模板参数应使用&&
而不是const &
的原因,该主题在其他地方被广泛介绍,该主题称为"perfect forwarding",因此我在此引用您可以从众多的stackoverflow和Google搜索结果中了解更多信息。
此外,正如其他地方所广泛介绍的那样,您必须declare and define do_format
() inline。这将导致明显的代码膨胀。有多种技术可以减少代码膨胀,但这又是另一个讨论的话题。
答案 1 :(得分:1)
因此,基本上,我正在寻找一种方法来创建具有针对不同平台的各种实现的接口。
因此您可以创建相同的类,但对不同的文件使用不同的实现,而根据平台/ OS / ...或通过预定义的宏仅使用正确的类。
类似
log_windows.h
template<typename ...Args>
void log(const char* fmt, const Args&... args)
{
// Windows implementation
}
template<typename ...Args>
void warn(const char* fmt, const Args&... args)
{
// Windows implementation
}
template<typename ...Args>
void error(const char* fmt, const Args&... args)
{
// Windows implementation
}
log_nix.h
template<typename ...Args>
void log(const char* fmt, const Args&... args)
{
// *nix implementation
}
template<typename ...Args>
void warn(const char* fmt, const Args&... args)
{
// *nix implementation
}
template<typename ...Args>
void error(const char* fmt, const Args&... args)
{
// *nix implementation
}
log.h
#ifdef (_WIN32)
#include "log_windows.h"
#else
#include "log_nix.h"
#endif