c++ 在运行时从集合中选择类型

Posted

技术标签:

【中文标题】c++ 在运行时从集合中选择类型【英文标题】:c++ choose type from set at runtime 【发布时间】:2017-08-02 09:49:56 【问题描述】:

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

下面是我想要运行的代码示例; 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 (如在类似问题中推荐的那样),但我认为这并不适合我想做的事情。

【问题讨论】:

您需要所有 ~10^6 的类型组合吗?听起来很多代码 【参考方案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. 函数是pure 在界面中,你不能忘记提供它们,否则它不会编译)。

此外,您可以在接口上提供一个虚拟方法来适当地从文件中读取,而不是 grid_tag、deriv_tag。

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

【讨论】:

【参考方案2】:

从运行时值中选择一个类型本身就涉及到一些丑陋,但从提供的 sn-p 来看,一个函数表就可以了

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

【讨论】:

【参考方案3】:

您可以通过创建多个接口(未实现方法的抽象虚拟类)来解决此问题,每个接口用于您想要在运行时决定的每种类型。

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

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

【讨论】:

以上是关于c++ 在运行时从集合中选择类型的主要内容,如果未能解决你的问题,请参考以下文章

C++:当我的应用程序在随机位置崩溃时从哪里开始?

填充时从集合视图中选择一个单元格

如何在运行时从 DynamoDB 获取排序结果

C# - 在需要时从多个运行的计时器中处理一个特定的计时器[关闭]

尝试在 Linux Mint 17.1 64 位(未声明/非类型)上“制作”C++ 项目时从 cstdlib 编译错误和类似错误

在运行时从静态 UITableView 中删除行