繁体   English   中英

如何使用可变参数模板参数保存可变数量的参数?

[英]How to save variable number of arguments using variadic template arguments?

我想创建一个模板类,该模板类可以存储此函数的函数指针和参数,以便以后可以使用此参数调用该函数。

我想将此写成通用格式,而不要依赖于参数类型或数字。

这是使用c ++ 11的可变参数模板的一个想法:

template<class T, typename... Params>
class LazyEvaluation {
private:
    // Function to be invoked later
    T (*f)(Params...);
    // Params for function f
    Params... storedParams; // This line is not compilable!
    bool evaluated;
    T result;
public:
    // Constructor remembers function pointer and parameters
    LazyEvaluation(T (*f)(Params...),Params... params)
    : f(f),
    storedParams(params) //this line also cannot be compiled
    {}
    // Method which can be called later to evaluate stored function with stored arguments
    operator T&() {
            // if not evaluated then evaluate
            if (! evaluated) {
                    result = f(storedParams...);
                    evaluated = true;
            }
            return result;
     }
}

如果可能的话,我至少希望此类的公共接口安全。 尽管至少以某种方式获得这项工作更为重要。

我设法以某种方式保存了可变数量的参数。 但是我无法将它们传递给函数f。 我会把它写成答案,但是我希望您在看到我的丑陋不起作用的尝试之前考虑一下自己的解决方案。

我正在尝试使用Microsoft Visual C ++编译器2012年11月CTP(v120_CTP_Nov2012)编译上面的代码,但是最好是存在一个独立于编译器的解决方案。

谢谢

这是我尝试解决的方法:

可以递归扩展参数包并保存每个参数。 功能存储应该做到这一点。 它使用一个(两次重载)辅助函数。

template<typename T>
void storeHelperFunction(void*& memory, T last) {
    *((T*)memory) = last;
    memory = (void*)((char*)memory + sizeof(T));
}

template<typename T, typename... Params>
void storeHelperFunction(void*& memory, T first, Params... rest) {
    storeHelperFunction(memory, first);
    storeHelperFunction(memory, rest...);
}

template<typename... Params>
void store(void* memory, Params... args) {
    // Copy of pointer to memory was done when passing it to this function
    storeHelperFunction(memory, args...);
}

函数存储使用指向内存的指针,该内存中应保存各种参数。

指针可以指向一些动态分配的内存,也可以指向大小等于sizeof...(Params) 可以使用模板元编程来构造具有完全不同大小的结构:

template <int N>
struct allocatorStruct {
    char byte1;
    allocatorStruct<N-1> next;
};

template <>
struct allocatorStruct<1> {};

我不确定标准是怎么说的,或者微软以外的其他编译器如何编译它。 但是使用我的编译器,对于任何大于或等于1的N,sizeof(allocatorStruct)等于N。

因此, allocatorStruct<sizeof...(Params)>具有与Params相同的大小。

创建与Params大小相同的另一种方法是使用char [sizeof...(Params)] 这样做的缺点是,当您尝试将数组作为参数传递时,编译器仅将指针传递给该数组。 这就是为什么最好使用allocatorStruct<sizeof...(Params)>

现在的主要思想是:

保存函数时,可以将其T (*)(allocatorStruct<sizeof...(Params)>)为: T (*)(allocatorStruct<sizeof...(Params)>) 当保存函数的参数时,我们可以将它们保存到allocatorStruct<sizeof...(Params)>类型的结构中。

参数的大小是相同的。 尽管函数指针与函数的类型有关,但指向的函数将正确获取其数据。

至少我希望如此。 根据调用约定,由于从左至右保存参数与从右至左传递参数之间的差异,我期望传递的参数可以重新排序或错误。 但事实并非如此。 使用__cdecl调用约定,仅传递了第一个参数,而另一个则丢失了。 与其他调用约定,该程序停止工作。

我没有花很多时间调试它并寻找内存中的数据(堆栈)。 这至少是正确的方法吗?

只需使用lambda表达式

// Some function.
int add(int a, int b) {
    return a + b;
}

auto lazyFunc = [] { return add(1, 2); };

std::cout << lazyFunc() << std::endl; // Evaluate function and output result.

如果您真的想使用可变参数模板创建只对函数进行一次(懒惰)求值的类,则可以执行以下代码中的操作。

我也做了这样的类,您不必在每次参数更改时都创建一个新实例。 我使用std::tuple存储给定的参数,并与先前给定的参数进行比较。 如果参数不同,则将重新评估该函数。

函数是通过std::function包装器传递和存储的,因此我不必使用原始函数指针(糟糕)。

#include <iostream>
#include <functional>
#include <utility>
#include <tuple>

template <typename T>
class LazyEvaluation {};

template <typename ReturnType, typename... Params>
class LazyEvaluation<ReturnType(Params...)> {
private:
    std::function<ReturnType(Params...)> func_;
    ReturnType result;
    std::tuple<Params...> oldParams; // Contains the previous arguments.
public:
    explicit LazyEvaluation(std::function<ReturnType(Params...)> func)
        : func_(std::move(func)) {}
    template <typename... Args>
    ReturnType operator() (Args&&... args) {
        auto newParams = std::make_tuple(std::forward<Args>(args)...);

        // Check if new arguments.
        if (newParams != oldParams) {
            result = func_(std::forward<Args>(args)...);
            oldParams = newParams;
            std::cout << "Function evaluated" << std::endl;
        }

        std::cout << "Returned result" << std::endl;
        return result;
    }
};

int main() {
    auto f = [] (int a, int b) {
        return a + b;
    };

    // Specify function type as template parameter.
    // E.g. ReturnType(Param1Type, Param2Type, ..., ParamNType)
    LazyEvaluation<int(int, int)> ld(f);

    std::cout << ld(1, 2) << std::endl;
    std::cout << ld(1, 2) << std::endl;
    std::cout << ld(3, 4) << std::endl;
}

输出:

Function evaluated
Returned result
3
Returned result
3
Function evaluated
Returned result
7

给定用于形成可变参数包的标准机制:

template <std::size_t... I> struct index_sequence {};
template <std::size_t N, std::size_t... I>
struct make_index_sequence : public make_index_sequence<N-1, N-1, I...> {};
template <std::size_t... I>
struct make_index_sequence<0, I...> : public index_sequence<I...> {};

并使用未打包的元组参数调用函数:

template <typename Function, typename... Types, std::size_t... I>
auto apply_(Function&& f, const std::tuple<Types...>& t, index_sequence<I...>)
  -> decltype(std::forward<Function>(f)(std::get<I>(t)...)) {
  return std::forward<Function>(f)(std::get<I>(t)...);
}

template <typename Function, typename... Types>
auto apply(Function&& f, const std::tuple<Types...>& t)
  -> decltype(apply_(f, t, make_index_sequence<sizeof...(Types)>())) {
  return apply_(f, t, make_index_sequence<sizeof...(Types)>());
}

这很简单:

template<typename Function, typename... Params>
class LazyEvaluation {
private:
  typedef decltype(std::declval<Function>()(std::declval<Params>()...)) result_type;
  // Function to be invoked later
  Function f;
  // Params for function f
  std::tuple<Params...> storedParams;
  mutable bool evaluated;
  union {
    std::aligned_storage<sizeof(result_type)> space;
    mutable result_type result;
  };

  // Method which can be called later to evaluate stored function with stored arguments
  void evaluate() const {
    // if not evaluated then evaluate
    if (! evaluated) {
      new (&result) result_type{apply(f, storedParams)};
      evaluated = true;
    }
  }

public:
  // Constructor remembers function pointer and parameters
  LazyEvaluation(Function f, Params... params)
    : f(std::move(f)),
      storedParams(std::move(params)...),
      evaluated(false)
  {}
  ~LazyEvaluation() {
    if (evaluated)
      result.~result_type();
  }

  operator result_type&() {
    evaluate();
    return result;
  }

  operator const result_type& () const {
    evaluate();
    return result;
  }
};

template <typename Function, typename... Params>
LazyEvaluation<Function, Params...>
make_lazy(Function&& f, Params&&... params) {
  return {std::forward<Function>(f), std::forward<Params>(params)...};
}

我使用了new的并集和放置来存储求值结果,因此它不必是默认可构造的类型,并且使用了一些mutable技巧,以便可以将const LazyEvaluator以及非const实例进行转换。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM