c++ - 可以通过这种方式使用可变参数函数/模板吗?

标签 c++ c++11 function-pointers variadic-templates variadic-functions

我想知道在给定以下约束的情况下,是否可以使用可变参数函数/模板来将可变数量的参数(它们本身就是函数的返回值)传递给可变参数函数。

约束:

  1. 没有 STL/Boost/其他库。
  2. 没有循环/递归。
  3. 符合 C++17 或更早版本(不超过 C++17)。

简要示例:

using fn = F(...);
fn* func = (fn*) addr;
value = (*func)(pop_t(outputstack,o_stackidx,o_stacksize)...);
push_t(outputstack,o_stackidx,o_stacksize, value);

fn* 是一个函数指针(从函数的已知用户定义内存地址转换而来),它接受可变数量的 pop_t 参数(从堆栈弹出的值),这些值( outputstack,o_stackidx,o_stacksize) 是静态的,不需要本身是可变的。本质上,我想知道是否有可能让 pop_t 函数重复可变次数 A.) 取决于适当数量的参数 fn 能够接受或 B.) 使用用户- 指定重复次数的定义整数。

举个例子,假设用户要输入一个 sin 或 atan2 函数,这两个函数采用不同数量的参数,分别是 sin(x) 和 atan(y,x)。对于这两个各自的函数,函数调用表达式如下所示:

sin -> (*func)(pop_t(outputstack,o_stackidx,o_stacksize)); 

atan2 -> (*func)(pop_t(outputstack,o_stackidx,o_stacksize),pop_t(outputstack,o_stackidx,o_stacksize)); 

具有 N 个参数的函数通过调用 pop_t N 次从堆栈中弹出 N 个值。

可重现的例子:

template<class U, class I>
U pop_t(U* stack, I &stackidx, I &stacksize) {
    if(stacksize>0) {
        stacksize--;
        stackidx = stackidx--;
        return stack[stackidx];
    }
    else {
        return stack[stackidx];
    }
}

int main() {
    float outputstack[2] = {3.141/2,1};
    int o_stackidx = 2;
    int o_stacksize = 2;
    long addr = (long)&atan2;
    using fn = float(...);
    fn* func = (fn*) addr;
    // Unknown variadic function pointer
    float value = (*func)(pop_t(outputstack,o_stackidx,o_stacksize,nt)...);

    return 0;

}

最佳答案

看来你想根据参数的个数多次重复一条语句,在这种情况下,你可以借助C++模板:

#include <cstddef>
#include <utility>

template <size_t N>
struct repeater {
    template <typename F, typename ...Args>
    static void do_work(F&& f, Args&&... args) {
        f(std::forward<Args>(args)...);
        repeater<N - 1>::do_work(f, args...);
    }
};

template <>
struct repeater<0> {
    template <typename F, typename ...Args>
    static void do_work(F&&, Args&&...) {}
};

template <size_t N, typename F, typename ...Args2>
void repeat_for_n(F&& to_rep, Args2&&... args) {
    repeater<N>::do_work(to_rep, args...);
}

template <typename T1, typename ...Args1, typename F, typename ...Args2>
void repeat_for_args(T1(Args1...), F&& to_rep, Args2&&... args) {
    repeat_for_n<sizeof...(Args1)>(to_rep, args...);
}

然后你可以像这样使用它:

repeat_for_args(some_function, [&]() {
    (*func)(pop_t(outputstack,o_stackidx,o_stacksize))
});

如果您想先自己尝试一下:

编辑 1: 要在模板的帮助下生成参数中具有 N 重复类型的函数指针类型,您可以这样做:

首先,你需要定义这个辅助类:

#include <tuple>

template <typename, typename>
struct make_sig_from_tuple;

template <typename R, typename ...Args>
struct make_sig_from_tuple<R, std::tuple<Args...>> {
    using type = R(*)(Args...);
};

template <typename R, typename ...Args>
using make_sig_from_tuple_t = typename make_sig_from_tuple<R, Args...>::type;

然后我们可以这样做,

template <typename T, size_t N>
struct generate_sig_impl {
    using type = decltype(std::tuple_cat(std::declval<std::tuple<T>&>(), std::declval<typename generate_sig_impl<T, N - 1>::type&>()));
};

template <typename T>
struct generate_sig_impl<T, 0> {
    using type = std::tuple<>;
};

template <typename R, typename T, size_t N>
struct generate_sig {
    using type = make_sig_from_tuple_t<R, typename generate_sig_impl<T, N>::type>;
};

template <typename R, typename T, size_t N>
using generate_sig_t = typename generate_sig<R, T, N>::type;

现在可以使用它来仅使用 N 的值来转换可变参数函数,而无需多次显式声明类型中的参数:

repeat_for_args(reinterpret_cast<generate_sig_t<float, float, 3>>(function_with_variadic_arguments), /* ... */);
// Equivalent to: repeat_for_args(reinterpret_cast<float(*)(float, float, float)>(function_with_variadic_arguments), /* ... */);

编辑 2: 由于评论中的 OP 也想颠倒参数的顺序,因此可以使用:

template <typename Arg1, typename ...Args>
struct reverse_func_sig_impl {
    using type = decltype(std::tuple_cat(std::declval<typename reverse_func_sig_impl<Args...>::type&>(), std::declval<std::tuple<Arg1>&>()));
};

template <typename Arg1>
struct reverse_func_sig_impl<Arg1> {
    using type = std::tuple<Arg1>;
};

template <typename>
struct reverse_func_sig;

template <typename R, typename... Args>
struct reverse_func_sig<R(Args...)> {
    using type = make_sig_from_tuple_t<R, typename reverse_func_sig_impl<Args...>::type>;
};

template <typename FuncPtr>
using reverse_func_sig_t = typename reverse_func_sig<FuncPtr>::type;

然后像这样使用它:

repeat_for_args(reinterpret_cast<generate_sig_t<int(float, int)>>(function_with_variadic_arguments), /* ... */);
// Equivalent to: repeat_for_args(reinterpret_cast<int(int, float)>(function_with_variadic_arguments), /* ... */);

Demo

关于c++ - 可以通过这种方式使用可变参数函数/模板吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68992061/

相关文章:

c++ - C++/boost/thread 中可能出现死锁

c++ - 对运算符进行试验会导致运算符错误不明确

c++ - 为什么 shared_ptr 删除器必须是 CopyConstructible 的?

c++ - 在 C++ 中将(可能)虚方法作为函数指针传递

c++ - 在 STL make_heap c++​​ 中将函数指针作为比较器传递

c++ - 井字游戏 while and ||

c++ - 如何在其他文件(foo.h、foo.cpp)中使用主文件 (main.cpp) 中的变量?

c++ - 为什么结构化绑定(bind)禁用 RVO 并继续返回语句?

c++ - 消除函数参数的复制

c - 解释这个 gcc 函数的类型化行为