简体   繁体   English

C ++在运行时从设置中选择类型

[英]c++ choose type from set at runtime

I have a program in which I would like to choose a set of types (from predefined lists) at run time, not compile time. 我有一个程序,我想在运行时而不是编译时从预定义列表中选择一组类型。

Below is an example of the kind of code I'd like to run; 下面是我想运行的那种代码的示例; Even and Log are types to define a numerical grid, and deriv_Ox is a differentiation scheme of order x: 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);
  // ...
}

I would like to decide which of these types to use at run time, by reading a parameter file. 我想通过读取参数文件来决定在运行时使用哪种类型。 My solution was to use tags and case statements to decide which type to use from a single list, then nest each case statement in a function deciding each type in the set as follows: 我的解决方案是使用标签和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]);
}

The problem is that I have a set of ~6 types to choose from lists of size ~10, and these will grow in the future. 问题是我从〜10大小的列表中选择了〜6种类型,并且将来会越来越多。 The solution I have at the moment makes adding new types awkward. 我目前的解决方案使添加新类型变得很尴尬。

Is this solution the best I'm going to do? 这个解决方案是我要做的最好的选择吗? Am I being very fussy, or is there a better solution someone can suggest? 我是不是很挑剔,还是有人可以建议更好的解决方案? I have looked at boost::variant (as recommended in similar questions) but I don't think this is really suitable for what I want to do. 我已经看过boost :: variant(在类似问题中建议使用),但是我认为这真的不适合我想做的事情。

As written, this leads to "double dispatch", which is not an easy thing to solve in C++ (see eg here: Understanding double dispatch C++ ). 如所写,这导致“双重调度”,这在C ++中不容易解决(例如,请参见此处: 了解双重调度C ++ )。

What might be applicable in this case, instead of having: 在这种情况下可能适用的方法,而不是:

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);
  // ...
}

to retrieve the grid/deriv from the file and result in a concrete type, to have instead 从文件中检索网格/派生并产生具体类型,

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);
  // ...
}

and using virtual function call on the Grid/Deriv interface to do the stuff. 并在Grid / Deriv接口上使用虚拟函数调用来完成任务。

(if you do not want to pollute the original grid/deriv classes by the virtual methods, you could also create wrappers for them) (如果您不想通过虚拟方法污染原始的网格/派生类,则还可以为其创建包装器)

The advantage of this (of course if applicable to your actual situation) would be, that you would not need to solve all the combinations. 这样做的好处(当然,如果适用于您的实际情况)将是您不需要解决所有组合。 Compared to the "switch" solution (works in a similar way), you do not need to remember to put switches everywhere to decide the types, you can just call the appropriate virtual functions to do the work (and if the virt. functions are pure in the interface, you cannot forget to provide them, because it will not compile otherwise). 与“开关”解决方案(以类似的方式工作)相比,您无需记住到处都放置开关来确定类型,您只需调用适当的虚函数即可完成工作(如果virt。函数是纯粹在接口中,您不能忘记提供它们,因为否则它将无法编译)。

Also, instead of grid_tag, deriv_tag, you could provide a virtual method on the interfaces to read from the file appropriately. 另外,您可以在接口上提供一个虚拟方法来代替grid_tag,deriv_tag,以适当地从文件中读取数据。

And I would also recommend to pass the string by const ref (" const std::string & param_file "), not by value (copies made). 而且我还建议通过const ref(“ const std::string & param_file ”)传递字符串,而不是通过值(已复制)传递字符串。

Selecting a type from a runtime value inherently have some ugliness involved, but judging from the snippet provided, a table of functions would work just fine 从运行时值中选择类型本质上涉及一些麻烦,但是从提供的代码段来看,函数表可以正常工作

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);
}

You can solve this creating several interface (abstract virtual classes with no method implemented) , one for each type you want to decide at runtime. 您可以解决此问题,以创建多个接口(未实现任何方法的抽象虚拟类),每种接口都可以在运行时确定。

You then can use a template method pattern to write your algorithm using the interfaces you have written. 然后,您可以使用模板方法模式通过已编写的接口来编写算法。

This way, adding elements to the lists of type is just adding a new class implementing the interface. 这样,将元素添加到类型列表中只是在添加实现该接口的新类。

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

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