You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

636 lines
23 KiB

  1. // util/parse-options.cc
  2. // Copyright 2009-2011 Karel Vesely; Microsoft Corporation;
  3. // Saarland University (Author: Arnab Ghoshal);
  4. // Copyright 2012-2013 Johns Hopkins University (Author: Daniel Povey);
  5. // Frantisek Skala; Arnab Ghoshal
  6. // Copyright 2013 Tanel Alumae
  7. //
  8. // See ../../COPYING for clarification regarding multiple authors
  9. //
  10. // Licensed under the Apache License, Version 2.0 (the "License");
  11. // you may not use this file except in compliance with the License.
  12. // You may obtain a copy of the License at
  13. //
  14. // http://www.apache.org/licenses/LICENSE-2.0
  15. //
  16. // THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  17. // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
  18. // WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
  19. // MERCHANTABLITY OR NON-INFRINGEMENT.
  20. // See the Apache 2 License for the specific language governing permissions and
  21. // limitations under the License.
  22. #include <algorithm>
  23. #include <cassert>
  24. #include <cstdlib>
  25. #include <cstring>
  26. #include <fstream>
  27. #include <iomanip>
  28. #include <iostream>
  29. #include "base/kaldi-common.h"
  30. #include "util/parse-options.h"
  31. #include "util/text-utils.h"
  32. namespace kaldi {
  33. ParseOptions::ParseOptions(const std::string& prefix, OptionsItf* other)
  34. : print_args_(false), help_(false), usage_(""), argc_(0), argv_(NULL) {
  35. ParseOptions* po = dynamic_cast<ParseOptions*>(other);
  36. if (po != NULL && po->other_parser_ != NULL) {
  37. // we get here if this constructor is used twice, recursively.
  38. other_parser_ = po->other_parser_;
  39. } else {
  40. other_parser_ = other;
  41. }
  42. if (po != NULL && po->prefix_ != "") {
  43. prefix_ = po->prefix_ + std::string(".") + prefix;
  44. } else {
  45. prefix_ = prefix;
  46. }
  47. }
  48. void ParseOptions::Register(const std::string& name, bool* ptr,
  49. const std::string& doc) {
  50. RegisterTmpl(name, ptr, doc);
  51. }
  52. void ParseOptions::Register(const std::string& name, int32* ptr,
  53. const std::string& doc) {
  54. RegisterTmpl(name, ptr, doc);
  55. }
  56. void ParseOptions::Register(const std::string& name, uint32* ptr,
  57. const std::string& doc) {
  58. RegisterTmpl(name, ptr, doc);
  59. }
  60. void ParseOptions::Register(const std::string& name, float* ptr,
  61. const std::string& doc) {
  62. RegisterTmpl(name, ptr, doc);
  63. }
  64. void ParseOptions::Register(const std::string& name, double* ptr,
  65. const std::string& doc) {
  66. RegisterTmpl(name, ptr, doc);
  67. }
  68. void ParseOptions::Register(const std::string& name, std::string* ptr,
  69. const std::string& doc) {
  70. RegisterTmpl(name, ptr, doc);
  71. }
  72. // old-style, used for registering application-specific parameters
  73. template <typename T>
  74. void ParseOptions::RegisterTmpl(const std::string& name, T* ptr,
  75. const std::string& doc) {
  76. if (other_parser_ == NULL) {
  77. this->RegisterCommon(name, ptr, doc, false);
  78. } else {
  79. KALDI_ASSERT(prefix_ != "" &&
  80. "Cannot use empty prefix when registering with prefix.");
  81. std::string new_name = prefix_ + '.' + name; // name becomes prefix.name
  82. other_parser_->Register(new_name, ptr, doc);
  83. }
  84. }
  85. // does the common part of the job of registering a parameter
  86. template <typename T>
  87. void ParseOptions::RegisterCommon(const std::string& name, T* ptr,
  88. const std::string& doc, bool is_standard) {
  89. KALDI_ASSERT(ptr != NULL);
  90. std::string idx = name;
  91. NormalizeArgName(&idx);
  92. if (doc_map_.find(idx) != doc_map_.end())
  93. KALDI_WARN << "Registering option twice, ignoring second time: " << name;
  94. this->RegisterSpecific(name, idx, ptr, doc, is_standard);
  95. }
  96. // used to register standard parameters (those that are present in all of the
  97. // applications)
  98. template <typename T>
  99. void ParseOptions::RegisterStandard(const std::string& name, T* ptr,
  100. const std::string& doc) {
  101. this->RegisterCommon(name, ptr, doc, true);
  102. }
  103. void ParseOptions::RegisterSpecific(const std::string& name,
  104. const std::string& idx, bool* b,
  105. const std::string& doc, bool is_standard) {
  106. bool_map_[idx] = b;
  107. doc_map_[idx] =
  108. DocInfo(name, doc + " (bool, default = " + ((*b) ? "true)" : "false)"),
  109. is_standard);
  110. }
  111. void ParseOptions::RegisterSpecific(const std::string& name,
  112. const std::string& idx, int32* i,
  113. const std::string& doc, bool is_standard) {
  114. int_map_[idx] = i;
  115. std::ostringstream ss;
  116. ss << doc << " (int, default = " << *i << ")";
  117. doc_map_[idx] = DocInfo(name, ss.str(), is_standard);
  118. }
  119. void ParseOptions::RegisterSpecific(const std::string& name,
  120. const std::string& idx, uint32* u,
  121. const std::string& doc, bool is_standard) {
  122. uint_map_[idx] = u;
  123. std::ostringstream ss;
  124. ss << doc << " (uint, default = " << *u << ")";
  125. doc_map_[idx] = DocInfo(name, ss.str(), is_standard);
  126. }
  127. void ParseOptions::RegisterSpecific(const std::string& name,
  128. const std::string& idx, float* f,
  129. const std::string& doc, bool is_standard) {
  130. float_map_[idx] = f;
  131. std::ostringstream ss;
  132. ss << doc << " (float, default = " << *f << ")";
  133. doc_map_[idx] = DocInfo(name, ss.str(), is_standard);
  134. }
  135. void ParseOptions::RegisterSpecific(const std::string& name,
  136. const std::string& idx, double* f,
  137. const std::string& doc, bool is_standard) {
  138. double_map_[idx] = f;
  139. std::ostringstream ss;
  140. ss << doc << " (double, default = " << *f << ")";
  141. doc_map_[idx] = DocInfo(name, ss.str(), is_standard);
  142. }
  143. void ParseOptions::RegisterSpecific(const std::string& name,
  144. const std::string& idx, std::string* s,
  145. const std::string& doc, bool is_standard) {
  146. string_map_[idx] = s;
  147. doc_map_[idx] =
  148. DocInfo(name, doc + " (string, default = \"" + *s + "\")", is_standard);
  149. }
  150. void ParseOptions::DisableOption(const std::string& name) {
  151. if (argv_ != NULL)
  152. KALDI_ERR << "DisableOption must not be called after calling Read().";
  153. if (doc_map_.erase(name) == 0)
  154. KALDI_ERR << "Option " << name
  155. << " was not registered so cannot be disabled: ";
  156. bool_map_.erase(name);
  157. int_map_.erase(name);
  158. uint_map_.erase(name);
  159. float_map_.erase(name);
  160. double_map_.erase(name);
  161. string_map_.erase(name);
  162. }
  163. int ParseOptions::NumArgs() const { return positional_args_.size(); }
  164. std::string ParseOptions::GetArg(int i) const {
  165. // use KALDI_ERR if code error
  166. if (i < 1 || i > static_cast<int>(positional_args_.size()))
  167. KALDI_ERR << "ParseOptions::GetArg, invalid index " << i;
  168. return positional_args_[i - 1];
  169. }
  170. // We currently do not support any other options.
  171. enum ShellType { kBash = 0 };
  172. // This can be changed in the code if it ever does need to be changed (as it's
  173. // unlikely that one compilation of this tool-set would use both shells).
  174. static ShellType kShellType = kBash;
  175. // Returns true if we need to escape a string before putting it into
  176. // a shell (mainly thinking of bash shell, but should work for others)
  177. // This is for the convenience of the user so command-lines that are
  178. // printed out by ParseOptions::Read (with --print-args=true) are
  179. // paste-able into the shell and will run. If you use a different type of
  180. // shell, it might be necessary to change this function.
  181. // But it's mostly a cosmetic issue as it basically affects how
  182. // the program echoes its command-line arguments to the screen.
  183. static bool MustBeQuoted(const std::string& str, ShellType st) {
  184. // Only Bash is supported (for the moment).
  185. KALDI_ASSERT(st == kBash && "Invalid shell type.");
  186. const char* c = str.c_str();
  187. if (*c == '\0') {
  188. return true; // Must quote empty string
  189. } else {
  190. const char* ok_chars[2];
  191. // These seem not to be interpreted as long as there are no other "bad"
  192. // characters involved (e.g. "," would be interpreted as part of something
  193. // like a{b,c}, but not on its own.
  194. ok_chars[kBash] = "[]~#^_-+=:.,/";
  195. // Just want to make sure that a space character doesn't get automatically
  196. // inserted here via an automated style-checking script, like it did before.
  197. KALDI_ASSERT(!strchr(ok_chars[kBash], ' '));
  198. for (; *c != '\0'; c++) {
  199. // For non-alphanumeric characters we have a list of characters which
  200. // are OK. All others are forbidden (this is easier since the shell
  201. // interprets most non-alphanumeric characters).
  202. if (!isalnum(*c)) {
  203. const char* d;
  204. for (d = ok_chars[st]; *d != '\0'; d++)
  205. if (*c == *d) break;
  206. // If not alphanumeric or one of the "ok_chars", it must be escaped.
  207. if (*d == '\0') return true;
  208. }
  209. }
  210. return false; // The string was OK. No quoting or escaping.
  211. }
  212. }
  213. // Returns a quoted and escaped version of "str"
  214. // which has previously been determined to need escaping.
  215. // Our aim is to print out the command line in such a way that if it's
  216. // pasted into a shell of ShellType "st" (only bash for now), it
  217. // will get passed to the program in the same way.
  218. static std::string QuoteAndEscape(const std::string& str, ShellType st) {
  219. // Only Bash is supported (for the moment).
  220. KALDI_ASSERT(st == kBash && "Invalid shell type.");
  221. // For now we use the following rules:
  222. // In the normal case, we quote with single-quote "'", and to escape
  223. // a single-quote we use the string: '\'' (interpreted as closing the
  224. // single-quote, putting an escaped single-quote from the shell, and
  225. // then reopening the single quote).
  226. char quote_char = '\'';
  227. const char* escape_str = "'\\''"; // e.g. echo 'a'\''b' returns a'b
  228. // If the string contains single-quotes that would need escaping this
  229. // way, and we determine that the string could be safely double-quoted
  230. // without requiring any escaping, then we double-quote the string.
  231. // This is the case if the characters "`$\ do not appear in the string.
  232. // e.g. see http://www.redhat.com/mirrors/LDP/LDP/abs/html/quotingvar.html
  233. const char* c_str = str.c_str();
  234. if (strchr(c_str, '\'') && !strpbrk(c_str, "\"`$\\")) {
  235. quote_char = '"';
  236. escape_str = "\\\""; // should never be accessed.
  237. }
  238. char buf[2];
  239. buf[1] = '\0';
  240. buf[0] = quote_char;
  241. std::string ans = buf;
  242. const char* c = str.c_str();
  243. for (; *c != '\0'; c++) {
  244. if (*c == quote_char) {
  245. ans += escape_str;
  246. } else {
  247. buf[0] = *c;
  248. ans += buf;
  249. }
  250. }
  251. buf[0] = quote_char;
  252. ans += buf;
  253. return ans;
  254. }
  255. // static function
  256. std::string ParseOptions::Escape(const std::string& str) {
  257. return MustBeQuoted(str, kShellType) ? QuoteAndEscape(str, kShellType) : str;
  258. }
  259. int ParseOptions::Read(int argc, const char* const argv[]) {
  260. argc_ = argc;
  261. argv_ = argv;
  262. std::string key, value;
  263. int i;
  264. if (argc > 0) {
  265. // set global "const char*" g_program_name (name of the program)
  266. // so it can be printed out in error messages;
  267. // it's useful because often the stderr of different programs will
  268. // be mixed together in the same log file.
  269. #ifdef _MSC_VER
  270. const char* c = strrchr(argv[0], '\\');
  271. #else
  272. const char* c = strrchr(argv[0], '/');
  273. #endif
  274. SetProgramName(c == NULL ? argv[0] : c + 1);
  275. }
  276. // first pass: look for config parameter, look for priority
  277. for (i = 1; i < argc; i++) {
  278. if (std::strncmp(argv[i], "--", 2) == 0) {
  279. if (std::strcmp(argv[i], "--") == 0) {
  280. // a lone "--" marks the end of named options
  281. break;
  282. }
  283. bool has_equal_sign;
  284. SplitLongArg(argv[i], &key, &value, &has_equal_sign);
  285. NormalizeArgName(&key);
  286. Trim(&value);
  287. if (key.compare("config") == 0) {
  288. ReadConfigFile(value);
  289. }
  290. if (key.compare("help") == 0) {
  291. PrintUsage();
  292. exit(0);
  293. }
  294. }
  295. }
  296. bool double_dash_seen = false;
  297. // second pass: add the command line options
  298. for (i = 1; i < argc; i++) {
  299. if (std::strncmp(argv[i], "--", 2) == 0) {
  300. if (std::strcmp(argv[i], "--") == 0) {
  301. // A lone "--" marks the end of named options.
  302. // Skip that option and break the processing of named options
  303. i += 1;
  304. double_dash_seen = true;
  305. break;
  306. }
  307. bool has_equal_sign;
  308. SplitLongArg(argv[i], &key, &value, &has_equal_sign);
  309. NormalizeArgName(&key);
  310. Trim(&value);
  311. if (!SetOption(key, value, has_equal_sign)) {
  312. PrintUsage(true);
  313. KALDI_ERR << "Invalid option " << argv[i];
  314. }
  315. } else {
  316. break;
  317. }
  318. }
  319. // process remaining arguments as positional
  320. for (; i < argc; i++) {
  321. if ((std::strcmp(argv[i], "--") == 0) && !double_dash_seen) {
  322. double_dash_seen = true;
  323. } else {
  324. positional_args_.push_back(std::string(argv[i]));
  325. }
  326. }
  327. // if the user did not suppress this with --print-args = false....
  328. if (print_args_) {
  329. std::ostringstream strm;
  330. for (int j = 0; j < argc; j++) strm << Escape(argv[j]) << " ";
  331. strm << '\n';
  332. std::cerr << strm.str() << std::flush;
  333. }
  334. return i;
  335. }
  336. void ParseOptions::PrintUsage(bool print_command_line) {
  337. std::cerr << '\n' << usage_ << '\n';
  338. DocMapType::iterator it;
  339. // first we print application-specific options
  340. bool app_specific_header_printed = false;
  341. for (it = doc_map_.begin(); it != doc_map_.end(); ++it) {
  342. if (it->second.is_standard_ == false) { // application-specific option
  343. if (app_specific_header_printed == false) { // header was not yet printed
  344. std::cerr << "Options:" << '\n';
  345. app_specific_header_printed = true;
  346. }
  347. std::cerr << " --" << std::setw(25) << std::left << it->second.name_
  348. << " : " << it->second.use_msg_ << '\n';
  349. }
  350. }
  351. if (app_specific_header_printed == true) {
  352. std::cerr << '\n';
  353. }
  354. // then the standard options
  355. std::cerr << "Standard options:" << '\n';
  356. for (it = doc_map_.begin(); it != doc_map_.end(); ++it) {
  357. if (it->second.is_standard_ == true) { // we have standard option
  358. std::cerr << " --" << std::setw(25) << std::left << it->second.name_
  359. << " : " << it->second.use_msg_ << '\n';
  360. }
  361. }
  362. std::cerr << '\n';
  363. if (print_command_line) {
  364. std::ostringstream strm;
  365. strm << "Command line was: ";
  366. for (int j = 0; j < argc_; j++) strm << Escape(argv_[j]) << " ";
  367. strm << '\n';
  368. std::cerr << strm.str() << std::flush;
  369. }
  370. }
  371. void ParseOptions::PrintConfig(std::ostream& os) {
  372. os << '\n' << "[[ Configuration of UI-Registered options ]]" << '\n';
  373. std::string key;
  374. DocMapType::iterator it;
  375. for (it = doc_map_.begin(); it != doc_map_.end(); ++it) {
  376. key = it->first;
  377. os << it->second.name_ << " = ";
  378. if (bool_map_.end() != bool_map_.find(key)) {
  379. os << (*bool_map_[key] ? "true" : "false");
  380. } else if (int_map_.end() != int_map_.find(key)) {
  381. os << (*int_map_[key]);
  382. } else if (uint_map_.end() != uint_map_.find(key)) {
  383. os << (*uint_map_[key]);
  384. } else if (float_map_.end() != float_map_.find(key)) {
  385. os << (*float_map_[key]);
  386. } else if (double_map_.end() != double_map_.find(key)) {
  387. os << (*double_map_[key]);
  388. } else if (string_map_.end() != string_map_.find(key)) {
  389. os << "'" << *string_map_[key] << "'";
  390. } else {
  391. KALDI_ERR << "PrintConfig: unrecognized option " << key << "[code error]";
  392. }
  393. os << '\n';
  394. }
  395. os << '\n';
  396. }
  397. void ParseOptions::ReadConfigFile(const std::string& filename) {
  398. std::ifstream is(filename.c_str(), std::ifstream::in);
  399. if (!is.good()) {
  400. KALDI_ERR << "Cannot open config file: " << filename;
  401. }
  402. std::string line, key, value;
  403. int32 line_number = 0;
  404. while (std::getline(is, line)) {
  405. line_number++;
  406. // trim out the comments
  407. size_t pos;
  408. if ((pos = line.find_first_of('#')) != std::string::npos) {
  409. line.erase(pos);
  410. }
  411. // skip empty lines
  412. Trim(&line);
  413. if (line.length() == 0) continue;
  414. if (line.substr(0, 2) != "--") {
  415. KALDI_ERR << "Reading config file " << filename << ": line "
  416. << line_number << " does not look like a line "
  417. << "from a Kaldi command-line program's config file: should "
  418. << "be of the form --x=y. Note: config files intended to "
  419. << "be sourced by shell scripts lack the '--'.";
  420. }
  421. // parse option
  422. bool has_equal_sign;
  423. SplitLongArg(line, &key, &value, &has_equal_sign);
  424. NormalizeArgName(&key);
  425. Trim(&value);
  426. if (!SetOption(key, value, has_equal_sign)) {
  427. PrintUsage(true);
  428. KALDI_ERR << "Invalid option " << line << " in config file " << filename;
  429. }
  430. }
  431. }
  432. void ParseOptions::SplitLongArg(const std::string& in, std::string* key,
  433. std::string* value, bool* has_equal_sign) {
  434. KALDI_ASSERT(in.substr(0, 2) == "--"); // precondition.
  435. size_t pos = in.find_first_of('=', 0);
  436. if (pos == std::string::npos) { // we allow --option for bools
  437. // defaults to empty. We handle this differently in different cases.
  438. *key = in.substr(2, in.size() - 2); // 2 because starts with --.
  439. *value = "";
  440. *has_equal_sign = false;
  441. } else if (pos == 2) { // we also don't allow empty keys: --=value
  442. PrintUsage(true);
  443. KALDI_ERR << "Invalid option (no key): " << in;
  444. } else { // normal case: --option=value
  445. *key = in.substr(2, pos - 2); // 2 because starts with --.
  446. *value = in.substr(pos + 1);
  447. *has_equal_sign = true;
  448. }
  449. }
  450. void ParseOptions::NormalizeArgName(std::string* str) {
  451. std::string out;
  452. std::string::iterator it;
  453. for (it = str->begin(); it != str->end(); ++it) {
  454. if (*it == '_')
  455. out += '-'; // convert _ to -
  456. else
  457. out += std::tolower(*it);
  458. }
  459. *str = out;
  460. KALDI_ASSERT(str->length() > 0);
  461. }
  462. bool ParseOptions::SetOption(const std::string& key, const std::string& value,
  463. bool has_equal_sign) {
  464. if (bool_map_.end() != bool_map_.find(key)) {
  465. if (has_equal_sign && value == "")
  466. KALDI_ERR << "Invalid option --" << key << "=";
  467. *(bool_map_[key]) = ToBool(value);
  468. } else if (int_map_.end() != int_map_.find(key)) {
  469. *(int_map_[key]) = ToInt(value);
  470. } else if (uint_map_.end() != uint_map_.find(key)) {
  471. *(uint_map_[key]) = ToUint(value);
  472. } else if (float_map_.end() != float_map_.find(key)) {
  473. *(float_map_[key]) = ToFloat(value);
  474. } else if (double_map_.end() != double_map_.find(key)) {
  475. *(double_map_[key]) = ToDouble(value);
  476. } else if (string_map_.end() != string_map_.find(key)) {
  477. if (!has_equal_sign)
  478. KALDI_ERR << "Invalid option --" << key << " (option format is --x=y).";
  479. *(string_map_[key]) = value;
  480. } else {
  481. return false;
  482. }
  483. return true;
  484. }
  485. bool ParseOptions::ToBool(std::string str) {
  486. std::transform(str.begin(), str.end(), str.begin(), ::tolower);
  487. // allow "" as a valid option for "true", so that --x is the same as --x=true
  488. if ((str.compare("true") == 0) || (str.compare("t") == 0) ||
  489. (str.compare("1") == 0) || (str.compare("") == 0)) {
  490. return true;
  491. }
  492. if ((str.compare("false") == 0) || (str.compare("f") == 0) ||
  493. (str.compare("0") == 0)) {
  494. return false;
  495. }
  496. // if it is neither true nor false:
  497. PrintUsage(true);
  498. KALDI_ERR << "Invalid format for boolean argument [expected true or false]: "
  499. << str;
  500. return false; // never reached
  501. }
  502. int32 ParseOptions::ToInt(const std::string& str) {
  503. int32 ret;
  504. if (!ConvertStringToInteger(str, &ret))
  505. KALDI_ERR << "Invalid integer option \"" << str << "\"";
  506. return ret;
  507. }
  508. uint32 ParseOptions::ToUint(const std::string& str) {
  509. uint32 ret;
  510. if (!ConvertStringToInteger(str, &ret))
  511. KALDI_ERR << "Invalid integer option \"" << str << "\"";
  512. return ret;
  513. }
  514. float ParseOptions::ToFloat(const std::string& str) {
  515. float ret;
  516. if (!ConvertStringToReal(str, &ret))
  517. KALDI_ERR << "Invalid floating-point option \"" << str << "\"";
  518. return ret;
  519. }
  520. double ParseOptions::ToDouble(const std::string& str) {
  521. double ret;
  522. if (!ConvertStringToReal(str, &ret))
  523. KALDI_ERR << "Invalid floating-point option \"" << str << "\"";
  524. return ret;
  525. }
  526. // instantiate templates
  527. template void ParseOptions::RegisterTmpl(const std::string& name, bool* ptr,
  528. const std::string& doc);
  529. template void ParseOptions::RegisterTmpl(const std::string& name, int32* ptr,
  530. const std::string& doc);
  531. template void ParseOptions::RegisterTmpl(const std::string& name, uint32* ptr,
  532. const std::string& doc);
  533. template void ParseOptions::RegisterTmpl(const std::string& name, float* ptr,
  534. const std::string& doc);
  535. template void ParseOptions::RegisterTmpl(const std::string& name, double* ptr,
  536. const std::string& doc);
  537. template void ParseOptions::RegisterTmpl(const std::string& name,
  538. std::string* ptr,
  539. const std::string& doc);
  540. template void ParseOptions::RegisterStandard(const std::string& name, bool* ptr,
  541. const std::string& doc);
  542. template void ParseOptions::RegisterStandard(const std::string& name,
  543. int32* ptr,
  544. const std::string& doc);
  545. template void ParseOptions::RegisterStandard(const std::string& name,
  546. uint32* ptr,
  547. const std::string& doc);
  548. template void ParseOptions::RegisterStandard(const std::string& name,
  549. float* ptr,
  550. const std::string& doc);
  551. template void ParseOptions::RegisterStandard(const std::string& name,
  552. double* ptr,
  553. const std::string& doc);
  554. template void ParseOptions::RegisterStandard(const std::string& name,
  555. std::string* ptr,
  556. const std::string& doc);
  557. template void ParseOptions::RegisterCommon(const std::string& name, bool* ptr,
  558. const std::string& doc,
  559. bool is_standard);
  560. template void ParseOptions::RegisterCommon(const std::string& name, int32* ptr,
  561. const std::string& doc,
  562. bool is_standard);
  563. template void ParseOptions::RegisterCommon(const std::string& name, uint32* ptr,
  564. const std::string& doc,
  565. bool is_standard);
  566. template void ParseOptions::RegisterCommon(const std::string& name, float* ptr,
  567. const std::string& doc,
  568. bool is_standard);
  569. template void ParseOptions::RegisterCommon(const std::string& name, double* ptr,
  570. const std::string& doc,
  571. bool is_standard);
  572. template void ParseOptions::RegisterCommon(const std::string& name,
  573. std::string* ptr,
  574. const std::string& doc,
  575. bool is_standard);
  576. } // namespace kaldi