使用C的'...'参数包作为C++模板值?

Mit*_*oft 6 c++ parameters templates function-pointers

我花了最近几天尝试在C++中为函数指针创建一个通用的包装器,我已经设法解决了几乎每一个问题.我的主要目标是能够简单地将对象作为函数调用内部存储的函数指针.如果指针指向某处,那么它会将其称为正常,而空指针只是不会调用该函数,它会继续,就好像什么都没发生一样.我打算主要用于回调函数,我可能不关心函数是否被调用,只是想要执行一个动作.它与以下几乎完美配合:

template<typename T>
class Action;

template<typename TReturn, typename ... TArgs>
class Action<TReturn(TArgs...)> {
public:
    //! Define a type for the Action object
    typedef TReturn(*signature)(TArgs...);

    //! Constructors
    inline Action(const signature& pFunc = nullptr) : mPointer(pFunc) {}
    inline Action(const Action& pCopy) : mPointer(pCopy.mPointer) {}

    //! Operator Call
    inline bool operator() (TReturn& pReturn, TArgs ... pArgs) const { if (!mPointer) return false; pReturn = mPointer(pArgs...); return true; }

    //! Operators
    inline Action& operator=(const Action& pCopy) { mPointer = pCopy.mPointer; return *this; }
    inline Action& operator=(const signature& pFunc) { mPointer = pFunc; return *this; }
    inline operator bool() const { return (mPointer != nullptr); }

private:
    //! Store a pointer to the callback function
    signature mPointer;
};

template<typename ... TArgs>
class Action<void(TArgs...)> {
public:
    //! Define a type for the Action object
    typedef void(*signature)(TArgs...);

    //! Constructors
    inline Action(const signature& pFunc = nullptr) : mPointer(pFunc) {}
    inline Action(const Action& pCopy) : mPointer(pCopy.mPointer) {}

    //! Operator Call
    inline bool operator() (TArgs ... pArgs) const { if (!mPointer) return false; mPointer(pArgs...); return true; }

    //! Operators
    inline Action& operator=(const Action& pCopy) { mPointer = pCopy.mPointer; return *this; }
    inline Action& operator=(const signature& pFunc) { mPointer = pFunc; return *this; }
    inline operator bool() const { return (mPointer != nullptr); }

private:
    //! Store a pointer to the callback function
    signature mPointer;
};
Run Code Online (Sandbox Code Playgroud)

但是,我觉得最有可能使用此包装器的情况是调试信息或格式化文本的输出.这可以通过用户定义的函数或内置函数(如printf)实现.要与printf的签名匹配,将创建一个Action,如:

Action<int(const char*, ...)> callback = printf;
Run Code Online (Sandbox Code Playgroud)

并且它能够以与任何其他Action行为相同的方式运行.我发现的问题是'...'会强制模板签名不与任何一个特化对齐,而是与第一个只是原型的一起.

我完全可以理解为什么这不起作用以及为什么编译器无法处理所需类的生成但我希望这里有人知道任何偷偷摸摸的方法来实现这个或类似的东西.任何帮助将不胜感激,谢谢:)

Aki*_*ira 1

以下示例适用于所有函数类型和不带捕获的 lambda:

#include <utility>
#include <cstdio>
#include <cmath>

template<typename Fn>
class Action {
    Fn* function_ptr;        
public:
    Action() noexcept : function_ptr(nullptr) {}    
    Action(std::nullptr_t) noexcept : function_ptr(nullptr) {}    
    Action(const Action& other) : function_ptr(other.function_ptr) {}    
    Action(Fn f) : function_ptr(f) {}

    Action& operator=(const Action& other) {
        return (function_ptr = other.function_ptr, *this);
    }            
    Action& operator=(std::nullptr_t ) {
        return (function_ptr = nullptr, *this);
    }    
    Action& operator=(Fn f) {
        return (function_ptr = f, *this);
    }

    template<typename... Params>
    auto operator()(Params&&... params) {
        return function_ptr(std::forward<Params>(params)...);
    }
};
Run Code Online (Sandbox Code Playgroud)

Live Demo

正如您的问题下的评论所提到的,使用std::function比为函数指针编写包装器更好。唯一的问题std::function是它不能与包含省略号的函数签名一起使用。如果您明确指定签名,您可以存储例如printf如下:

std::function<int(const char*, int, double, double)> fn = printf;
Run Code Online (Sandbox Code Playgroud)

如果您使用 C++17 或 Boost,您可以使用 Boost.Any 实现您自己的类似printf函数,可以按如下方式分配:std::anystd::function

#include <iostream>
#include <string>
#include <vector>
#include <any>
#include <functional>

using namespace std;

void any_printf(string&& format, vector<any>&& args) {
    int arg_index = 0; enum { NORMAL, CONVERT } mode;

    for(auto& chr : format) {
        if(mode == CONVERT) {
            switch(chr) {
            case 'd': cout << any_cast<int>(args[arg_index++]); break;
            case 'f': cout << any_cast<float>(args[arg_index++]); break;
            case 's': cout << any_cast<string>(args[arg_index++]); break;
            /* ... */
            default: cout << chr;
            };
            mode = NORMAL;
        }
        else {
            chr == '%' ? (mode = CONVERT, 0) : (cout << chr, 0);
        }
    }
}

int main() {
    using namespace string_literals;
    function<void(string&&, vector<any>&&)> f_ptr { any_printf };

    f_ptr("Cuboid: %smm x %dmm x %fmm.\n", { any("3"s), any(4), any(6.67f) });
    return 0;
}
Run Code Online (Sandbox Code Playgroud)