存储可变参数模板参数并将其传递给 lambda

Store variadic template arguments and pass them to lambda

本文关键字:参数 lambda 变参 存储      更新时间:2023-10-16

我试图用C++做一些可怕的事情。

我有一个函数f(void*, ...)需要接受指向intdouble等的指针。我有很多这样的结构:

void whatever() {
  somelogicHere();
  int a;
  double b;
  char c;
  f(&a, &b, &c);
  some(a);
  actions(b);
  onabc(c);
}

我想将大量的操作和变量定义包装到某个模板中,并像这样称呼它:

myTemplate([](int a, double b, char c) {      
      some(a);
      actions(b);
      onabc(c);
});

必须做什么的小例子:

template<int S, typename ...A> 
void magicTemplate(const char(&fmt)[S], std::function<void(A...)> callback)
{
    char format[2 + S];
    format[0] = 'O';
    format[1 + S] = '';
    int s = S;
    memcpy(format + 1, fmt, S);
    // next lines is just a random stuff that does not work
    std::tuple<A...> local_arguments;
    extractArgs(format, &local_arguments...); //pointers to arguments must be passed
    callback(local_arguments...);  // arguments must be passed
}

。并调用我的模板:

magicTemplate("iii", [](int a, double b, char c)
{
    std::cout < a << std::endl;
    std::cout < b << std::endl;
    std::cout < c << std::endl;
});

第一件事 - 我的模板与这个论点不匹配,第二件事 - 我不知道magicTemplate身体里面一定有什么。

我从该参数检测代码中获得了一些不错的里程:)

让我们先完成此操作 - 我们希望从传递给magicTemplate的任何类型中获取参数类型的列表:

namespace glk {
    namespace tmp {
        template <class T>
        struct type_is {
            using type = T;
        };
        template <class...>
        using void_t = void;
        // Pack of arbitrary types
        template <class...>
        struct pack { };
        namespace detail_parameters {
            template <class F, class = void_t<>>
            struct parameters { };
            template <class F>
            struct parameters<F, void_t<decltype(&F::operator ())>>
            : parameters<decltype(&F::operator ())> { };
            template <class R, class... Params>
            struct parameters<R(Params...)> : type_is<pack<Params...>>{ };
            template <class R, class... Params>
            struct parameters<R(*)(Params...)> : type_is<pack<Params...>>{ };
            template <class T, class R, class... Params>
            struct parameters<R(T::*)(Params...)> : type_is<pack<Params...>>{ };
            template <class T, class R, class... Params>
            struct parameters<R(T::*)(Params...) const> : type_is<pack<Params...>>{ };
        }
        // Retrieve the parameter list from a functionoid
        template <class F>
        using parameters = typename detail_parameters::parameters<
            std::remove_reference_t<F>
        >::type;
    }
}

现在glk::tmp::parameters<F>给了我们一个glk::tmp::pack<T...>,其中每个T对应于一个参数。现在,让我们假设我们有这个,并实现magicTemplate的实际主体:

template <std::size_t FmtSize, class AndThen, class... Args, std::size_t... ArgsIdx>
void magicTemplate(
    char const (&fmt)[FmtSize], AndThen &&andThen,
    glk::tmp::pack<Args...>,
    std::index_sequence<ArgsIdx...>
) {
    std::array<char, FmtSize + 1> fmtStr;
    fmtStr[0] = 'O';
    std::copy(fmt, fmt + FmtSize, fmtStr.data() + 1);
    std::tuple<Args...> args;
    std::scanf(fmtStr.data(), &std::get<ArgsIdx>(args)...);
    std::forward<AndThen>(andThen)(std::get<ArgsIdx>(args)...);
}

(出于测试目的,我已将extractArgs替换为std::scanf,因为它们似乎非常相似(

现在只需一点管道即可实际产生所需的std::index_sequence

template <std::size_t FmtSize, class AndThen, class... Args>
void magicTemplate(
    char const (&fmt)[FmtSize], AndThen &&andThen,
    glk::tmp::pack<Args...>
) {
    return magicTemplate(
        fmt, std::forward<AndThen>(andThen),
        glk::tmp::pack<Args...>{},
        std::index_sequence_for<Args...>{}
    );
}
template <std::size_t FmtSize, class AndThen>
void magicTemplate(char const (&fmt)[FmtSize], AndThen &&andThen) {
    return magicTemplate(
        fmt, std::forward<AndThen>(andThen),
        glk::tmp::parameters<AndThen>{}
    );
}

瞧!我们可以用你想要的确切语法来称呼这个东西。当然,所有看起来像错误检查的东西都留给了读者:)

科里鲁的现场演示

你可以使用std::apply(c++17,但在c++11中实现(

template<int N, typename ... Ts>
void magicTemplate(const char(&fmt)[N], std::function<void(Ts...)> callback)
{
    char format[2 + N];
    format[0] = 'O';
    format[N + 1] = '';
    memcpy(format + 1, fmt, N);
    std::tuple<Ts...> local_arguments;
    std::apply([&](auto& ...args){ extractArgs(format, &args...); }, local_arguments);
    std::apply(callback, local_arguments);
}

然后,要将 lambda 转换为 std::function ,您可能有类似的东西:

template <typename C> struct helper : helper<decltype(&C::operator())> {};
template <typename Ret, typename C, typename ...Ts>
struct helper<Ret (C::*)(Ts...) const> {
    using type = Ret(Ts...);
};
template <typename Ret, typename C, typename ...Ts>
struct helper<Ret (C::*)(Ts...)> {
    using type = Ret(Ts...);
};
template <typename F>
std::function<typename helper<std::decay_t<F>>::type> as_std_function(F&& f) { return f; }

所以最后:

template<int N, typename F>
void magicTemplateFinal(const char(&fmt)[N], F&& f)
{
    magicTemplate(fmt, as_std_function(f));
}