C ++创建具有可变参数模板方法的接口

时间:2019-07-20 11:00:00

标签: c++ generics interface variadic-functions

因此,基本上,我正在寻找一种方法来创建具有针对不同平台的各种实现的接口。通常,这很简单,但是我想要为记录器创建一个接口,并且能够具有以下功能是我的目标:

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>

我真的迷失了如何创建一个接口,该接口包含具有可变数量和参数类型的函数...?

2 个答案:

答案 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