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

C++ c++;从运行时设置中选择类型,c++,boost,C++,Boost,我有一个程序,我想在运行时而不是编译时选择一组类型(从预定义列表中) 下面是我想运行的代码类型的示例偶数和对数是定义数值网格的类型,而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_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);
  // ...
}
struct偶数{
双a,b;
};
结构日志{
双a,x0,b;
};
// ...
结构deriv_O2{
向量类型运算符()(常量向量类型和f点)常量;
};
结构deriv_O4{
向量类型运算符()(常量向量类型和f点)常量;
};
// ...
模板
无效运行计算(常量std::字符串参数文件){
自动网格=网格文件(参数文件)中的网格;
auto deriv=从文件(参数文件)中删除;
// ...
}
我想通过读取一个参数文件来决定在运行时使用哪种类型。我的解决方案是使用标记和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]);
}
enum struct grid_tag{偶,Log};
枚举结构deriv_标记{O4,O2};
grid_tag grid_tag_from_file(const char file_name[]);
deriv_tag deriv_tag_from_file(const char file_name[]);
模板
无效运行计算(常数网格标记g,
常量std::字符串参数(文件){
开关(g){
case grid_标记::偶数:
运行计算(参数文件);
打破
案例网格_标记::日志:
运行计算(参数文件);
打破
}
}
无效运行计算(常数网格标记g、常数导数标记d、,
常量std::字符串参数(文件){
开关(d){
案例deriv_标签::O4:
运行计算(g,参数文件);
打破
案例deriv_标签::O2:
运行计算(g,参数文件);
打破
}
}
int main(int argc,char*argv[]){
grid_tag g=来自_文件的grid_tag_(argv[1]);
deriv_tag d=来自_文件的deriv_tag_(argv[1]);
运行u计算(g,d,argv[1]);
}
问题是,我有一套~6种类型可以从~10大小的列表中选择,这些类型在未来还会增加。我目前的解决方案使得添加新类型变得很尴尬


这是我要做的最好的解决方案吗?是我太挑剔了,还是有人能提出更好的解决方案?我已经看过boost::variant(类似问题中推荐的),但我认为这并不适合我想要做的事情。

您可以通过创建几个接口(抽象虚拟类,没有实现任何方法)来解决这个问题,每个接口对应于您希望在运行时确定的类型

然后,您可以使用编写的接口来编写算法

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

作为编写,这导致了“双分派”,这在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);
  // ...
}
并在Grid/Deriv接口上使用虚拟函数调用来完成这些工作

(如果您不希望虚拟方法污染原始grid/deriv类,还可以为它们创建包装器)

这样做的好处(当然,如果适用于您的实际情况)是,您不需要解决所有的组合。与“switch”解决方案(工作方式类似)相比,您不需要记住在任何地方放置开关来决定类型,您只需调用适当的虚拟函数即可完成工作(如果virt.函数在接口中是纯函数,您不能忘记提供它们,因为否则它将不会编译)

此外,您可以在接口上提供一个虚拟方法来适当地读取文件,而不是grid_标记、deriv_标记


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

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

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

您需要所有~10^6种类型的组合吗?听起来好像有很多代码
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);
}