c ++在运行时从set中选择type

时间:2017-08-02 09:49:57

标签: c++ boost

我有一个程序,我想在运行时选择一组类型(来自预定义列表),而不是编译时。

以下是我想要运行的代码类型的示例; EvenLog是用于定义数字网格的类型,deriv_Ox是x阶数的微分方案:

struct Even {
  double a, b;
};
struct Log {
  double a, x0, b;
};
// ...

struct deriv_O2 {
  vec_type operator() (const vec_type & f_points) const;
};
struct deriv_O4 {
  vec_type operator() (const vec_type & f_points) const;
};
// ...

template <class grid_type, class deriv_type>
void run_calculation (const std::string param_file) {
  auto grid  =  grid_from_file<grid_type>(param_file);
  auto deriv = deriv_from_file<deriv_type>(param_file);
  // ...
}

我想通过读取参数文件来决定在运行时使用哪些类型。我的解决方案是使用标签和case语句来决定从单个列表中使用哪种类型,然后将每个case语句嵌套在一个函数中,决定集合中的每个类型,如下所示:

enum struct grid_tag { Even, Log };
enum struct deriv_tag { O4, O2 };

grid_tag grid_tag_from_file (const char file_name[]);
deriv_tag deriv_tag_from_file (const char file_name[]);

template <class deriv_type>
void run_calculation (const grid_tag g,
                      const std::string param_file) {
  switch(g) {
  case grid_tag::Even:
    run_calculation<Even, deriv_type>(param_file);
    break;
  case grid_tag::Log:
    run_calculation<Log, deriv_type>(param_file);
    break;
  }
}

void run_calculation (const grid_tag g, const deriv_tag d,
                      const std::string param_file) {
  switch(d) {
  case deriv_tag::O4:
    run_calculation<deriv_O4>(g, param_file);
    break;
  case deriv_tag::O2:
    run_calculation<deriv_O2>(g, param_file);
    break;
  }
}

int main (int argc, char * argv[]) {
  grid_tag g = grid_tag_from_file(argv[1]);
  deriv_tag d = deriv_tag_from_file(argv[1]);
  run_calculation(g, d, argv[1]);
}

问题在于我有一组约6种类型可供选择,大小约为10,这些将在未来增长。我现在的解决方案使添加新类型变得尴尬。

这个解决方案是我要做的最好的吗?我是非常挑剔,还是有人可以提出更好的解决方案?我看过boost :: variant(在类似的问题中推荐),但我认为这不适合我想做的事。

3 个答案:

答案 0 :(得分:1)

如上所述,这会导致“双重调度”,这在C ++中解决并不容易(参见例如:Understanding double dispatch C++)。

在这种情况下可能适用的是,而不是:

template <class grid_type, class deriv_type>
void run_calculation (const std::string param_file) {
  auto grid  =  grid_from_file<grid_type>(param_file);
  auto deriv = deriv_from_file<deriv_type>(param_file);
  // ...
}

从文件中检索网格/派生并生成具体类型,而不是

void run_calculation (const std::string param_file, grid_tag gtag, deriv_tag dtag) {
  auto /*Grid interface*/ grid  = grid_from_file(param_file, gtag);
  auto /*Deriv interface*/ deriv = deriv_from_file(param_file, dtag);
  // ...
}

并使用Grid / Deriv接口上的虚函数调用来完成这些工作。

(如果你不想通过虚方法污染原始网格/派生类,你也可以为它们创建包装器)

这一点(当然,如果适用于您的实际情况)的优点是,您不需要解决所有组合。与“切换”解决方案(以类似方式工作)相比,您不需要记住将交换机放在任何地方来决定类型,您可以只调用适当的虚函数来完成工作(如果virt。函数是纯粹在界面中,你不能忘记提供它们,因为它不会编译否则)。

此外,您可以在接口上提供虚拟方法来代替grid_tag,deriv_tag,以便从文件中正确读取。

我还建议通过const ref(“const std::string & param_file”)传递字符串,而不是按值传递(复制品)。

答案 1 :(得分:1)

从运行时值中选择一个类型本身就有一些丑陋,但从提供的代码片段判断,一个函数表可以正常工作

enum struct grid_tag { Even, Log, size };
enum struct deriv_tag { O4, O2, size };

using calculation = void (*)(std::string);

calculation table[grid_tag::size][deriv_tag::size];  // populate them

void run_calculation (const grid_tag g, const deriv_tag d, const std::string& param_file)
{
    table[g][d](param_file);
}

答案 2 :(得分:0)

你可以解决这个问题,创建几个接口(没有实现方法的抽象虚拟类),每个类型都要在运行时决定。

然后,您可以使用template method pattern使用您编写的接口编写算法。

这样,将元素添加到类型列表中只是添加一个实现接口的新类。