选项子系统

简介#

所有主要的 SUMO 应用程序都使用相同的类来解析命令行选项和配置文件。这些类封装了从命令行和 XML 配置文件解析选项的功能,并验证它们是否为正确的类型。选项子系统还支持写入配置模板、将当前(解析/加载的)设置保存到文件以及打印帮助屏幕。

初始化选项#

第一步#

每个应用程序都恰好有一个 OptionsCont(选项容器)类的实例。可以使用以下代码获取对该类的引用:

static OptionsCont &OptionsCont::getOptions();

在应用程序开始注册其选项之前,必须让 OptionsCont 知道其自身。这是必要的,因为 OptionsCont 负责打印有关应用程序的基本信息,并且它还生成包含应用程序名称、可能的示例调用等的帮助屏幕。因此,每个应用程序对选项子系统的首次接触类似于以下代码,这段代码摘自 netconvert_main.cpp:

   OptionsCont &oc = OptionsCont::getOptions();
   // 给出一些应用程序描述
   oc.setApplicationDescription("Road network importer / builder for the road traffic simulation SUMO.");
#ifdef WIN32
   oc.setApplicationName("netconvert.exe", "SUMO netconvert Version " + (string)VERSION_STRING);
#else
   oc.setApplicationName("sumo-netconvert", "SUMO netconvert Version " + (string)VERSION_STRING);
#endif

ifdef 块用于区分支持的操作系统(Windows/Linux),在这些系统上 SUMO 应用程序的名称略有不同。

请注意,由 setApplicationDescription 设置的描述是简短描述,应适合一行(不超过 80 个字符)。

注册选项#

在解析命令行选项之前,必须将其名称、允许的类型和默认值(如果存在)插入到此容器中。这通常在专用方法或函数中完成。

要将应用程序选项插入容器,请使用以下多态形式之一:

void doRegister(const std::string &name1, Option *v);

或者

void doRegister(const std::string &name1, char abbr, Option *v);

参数 "name1" 是要插入选项的名称(或者更准确地说,是可能的名称之一)。第二个调用中的字符 "abbr" 是该名称可能的单字符缩写。

Caution: The behavior after adding the same abbreviation for two different options is not defined.

第三个参数是应分配给名称的选项。请注意,OptionsCont 会负责此选项 - 当 OptionsCont 被删除时,它将被删除。该选项本身必须是以下类之一的实例,这些类都派生自 Option 类:

  • Option_Integer
  • Option_Bool
  • Option_Float
  • Option_String
  • Option_FileName
  • Option_IntVector
  • Option_StringVector

类的名称反映了选项预期的参数类型。同时请注意,以后的读取访问应依赖于类型进行 - 这意味着您不应尝试从 Option_Float 获取字符串。这会导致异常。

Caution

Option 的所有子类都存储在文件 ''<SUMO_HOME>/src/utils/options/Option.h 和 ''<SUMO_HOME>/src/utils/options/Option.cpp 中。这肯定不是期望的,因为遵循编码风格指南时,每个类都应该存储在单独的文件对中。此外,可以想象在这里使用模板。

每个选项的子类都有两个构造函数:一个使用适当类型参数化,另一个无参数。参数化构造函数用于为选项提供默认值,而在另一种情况下,系统最初不知道任何值。例如:将 "verbose" 开关设置如下,让应用程序默认安静运行:

oc.doRegister("verbose", 'v', new Option_Bool(false));

添加同义词#

还可以为选项添加同义词。如果您想实现选项 "name-of-vehicle-that-shall-halt",但又希望有一个更短的名称,请使用以下代码使同一选项可以通过 "name-of-vehicle-that-shall-halt" 和 "v2h" 访问:

oc.doRegister("name-of-vehicle-that-shall-halt", new Option_String());
oc.addSynonyme("name-of-vehicle-that-shall-halt", "v2h");

检索选项#

如果未为选项提供默认值,您应该询问 OptionsCont 是否已设置它。访问未设置选项的值会导致异常。这听起来可能很严厉,但实际上只有开发人员访问选项容器,这是确保代码安全的最佳方式。可以使用以下代码确定选项是否已设置:

bool OptionsCont::isSet(const std::string &name) const;

如果需要知道选项是仍存储默认值还是用户提供了值,可以调用:

bool OptionsCont::isDefault(const std::string &name) const;

根据选项的类型,可以使用 OptionsCont 中的以下方法检索其值:

int OptionsCont::getInt(const std::string &name) const;
long OptionsCont::getLong(const std::string &name) const;
std::string OptionsCont::getString(const std::string &name) const;
float OptionsCont::getFloat(const std::string &name) const;
const IntVector &OptionsCont::getIntVector(const std::string &name) const;
const StringVector &OptionsCont::getStringVector(const std::string &name) const;

使用不适当的方法会产生异常。

您可能已经注意到,检索特定值的方法比可用类型少一个。类型 Option_FileName 也返回一个字符串向量。唯一的区别是它们的类型,这允许在解析时对设置的值进行不同的处理。

请注意,使用长(实际上是最长)名称来检索选项的值是一种好的风格,并且在所有地方都应使用相同的名称(同义词)。这使得定位此选项的使用更加容易。

实现#

存储和处理选项所需的所有类都可以在 <SUMO_HOME>/src/utils/options/ 中找到。