【问题标题】:Possible to use a variadic functions/templates in this way?可以以这种方式使用可变参数函数/模板吗?
【发布时间】:2021-10-29 16:11:59
【问题描述】:

我想知道是否可以使用可变参数函数/模板来将可变数量的参数(它们本身是函数的返回值)传递给可变参数函数。

约束:

  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;

}

【问题讨论】:

  • 您的描述对我来说有点不清楚。您能否在呼叫站点添加您希望能够编写的代码?即,如果它可用,你能准确地展示你将如何使用这样的功能吗?
  • @cigien 赋值将是该函数的调用点。例如,用户输入一个 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 个值。希望这会有所帮助:)
  • 请将所有相关信息添加到问题本身,而不是作为评论。此外,您的描述仍然不清楚,例如pop_t 是什么?试着做一个minimal reproducible example,当然不能写的函数除外。
  • @cigien 刚刚添加了这些 :) 谢谢!

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


【解决方案1】:

您似乎想根据参数的数量多次重复一个语句,这种情况下,您可以借助 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: 由于 cmets 中的 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

【讨论】:

  • @Nullpoint 是的,可以做到。给我一分钟。
  • @Nullpoint repeat_for_args(fn, ...); 没有意义,因为fn 是一种类型,您的意思是:repeat_for_args(func, ...)
  • @Nullpoint 如果需要,您可以将函数转换为所需的参数数量:repeat_for_args(reinterpret_cast&lt;float(*)(float)&gt;(func), ...) 会提示编译器将函数视为只有一个参数。见here
  • @Nullpoint 我编辑了答案。现在看看吧。
  • @Nullpoint 你说的是倒转函数签名中参数类型的顺序吗?
猜你喜欢
  • 2017-07-01
  • 2023-03-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-01
  • 2018-07-31
  • 2014-06-07
  • 1970-01-01
相关资源
最近更新 更多