ExprTk 自定义 lambda 函数和默认参数

问题描述 投票:0回答:1

之前ExprTk表中添加了一个

random
函数:

exprtk::symbol_table<double> symbolTable;
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_real_distribution<double> dis(0.0, 1.0);

auto randomFunc = []() {
    return dis(gen);
};
symbolTable.add_function("random", randomFunc);

exprtk::expression<double> expression;
expression.register_symbol_table(symbolTable);

现在我修改它以获取最小和最大参数:

exprtk::symbol_table<double> symbolTable;
std::random_device rd;
std::mt19937 gen(rd());

auto randomFunc = [](double min = 0, double max = 1) {
    std::uniform_real_distribution<double> dis(min, max);
    return dis(gen);
};
symbolTable.add_function("random", randomFunc);

exprtk::expression<double> expression;
expression.register_symbol_table(symbolTable);

它可以工作,除了保持与旧的无参数

random
用法的兼容性,这是我们所希望的。尽管有默认参数,ExprTk 无法编译
random
random()
。我假设库只处理参数的数量,并且不检查是否有任何默认值。

我想我需要切换到

ivararg_function
来支持这一点。还是我缺少更简单的方法?

lambda c++17 exprtk
1个回答
0
投票

为函数参数指定默认值并不会改变您正在注册的函数是一个两个输入参数函数的事实,需要在表达式/程序中调用它时传递两个参数。

简而言之,您正在寻找的称为:函数重载,这可以通过使用

exprtk::igeneric_function
来实现。

您感兴趣的两个重载是:

  1. 零参数意味着最小值/最大值为 0 和 1
  2. 两个标量参数,其中 min 是参数 0,max 是参数 1

以下演示了如何实现这样的

exprtk::igeneric_function

template <typename T>
struct unirandom_dist final : public exprtk::igeneric_function<T>
{
   typedef exprtk::igeneric_function<T>           igenfunct_t;
   typedef typename igenfunct_t::generic_type     generic_t;
   typedef typename igenfunct_t::parameter_list_t parameter_list_t;
   typedef typename generic_t::scalar_view        scalar_t;

   unirandom_dist()
   : exprtk::igeneric_function<T>("Z|TT")
      /*
         Overloads:
         0. Z  - Zero parameters, range is [0,1)
         1. TT - min and max, range is [min,max)
      */
   {
      std::random_device device;
      std::array<unsigned int,std::mt19937::state_size> seed;
      std::generate_n(seed.data(),seed.size(),std::ref(device));
      std::seed_seq seq(std::begin(seed),std::end(seed));
      generator.seed(seq);
   }

   using igenfunct_t::operator();

   T operator() (const std::size_t& ps_index, parameter_list_t parameters) override
   {
      const T min = (ps_index == 0) ? T(0) : scalar_t(parameters[0])();
      const T max = (ps_index == 0) ? T(1) : scalar_t(parameters[1])();
      std::uniform_real_distribution<T> unidist(min,max);
      return unidist(generator);
   }

   std::mt19937 generator;
};

以下是如何将所有这些组合在一起并在表达式中使用:

template <typename T>
void foo()
{
   typedef exprtk::symbol_table<T> symbol_table_t;
   typedef exprtk::expression<T>   expression_t;
   typedef exprtk::parser<T>       parser_t;

   unirandom_dist<T> unirnd;
   exprtk::rtl::io::package<T> io_package;

   symbol_table_t symbol_table;
   symbol_table.add_function("random" , unirnd);
   symbol_table.add_package (io_package);

   expression_t expression;
   expression.register_symbol_table(symbol_table);

   parser_t parser;

   const std::string random_program =
      " println('random:        ', random       ); "
      " println('random():      ', random()     ); "
      " println('random(-1,+1): ', random(-1,+1)); ";

   parser.compile(random_program,expression);

   expression.value();
}

int main()
{
   foo<double>();
   return 0;
}

值得注意的是,现在该函数有一些重载,可以通过以下方式调用:

  1. 随机
  2. 随机()
  3. 随机(最小,最大)

顺便说明一下,您不会使用

ivararg_function
的原因是因为它没有类型检查,并且需要实现
unirandom_dist
来确定 critpath 中传递的参数数量及其性质等。

© www.soinside.com 2019 - 2024. All rights reserved.