使用composing()和implicit_value()的Boost program_options不是"编写"

时间:2012-08-02 13:11:35

标签: c++ boost boost-program-options

在定义为composing()和implicit()的选项的情况下,我遇到了boost program_options(v1_49)的问题。我的目的是实现一个类似于perl方式的-D选项,这样你就可以做-D或-Dname并多次使用它。我的options_description是:

(  "debug,D",
   bpo::value<vector<string> >()
         ->composing()
         ->implicit_value(vector<string>(1,"1")),
   "Set debug level."
),

在大多数情况下,这似乎工作正常,但是只要命令行中出现没有值的-D,就会删除所有早期值,例如:

$ ./a.out -D abc -D 255 -D xyz
variables_map["debug"] = {"abc", "255", "xyz"}

$ ./a.out -D -D 255 -D xyz
variables_map["debug"] = {"1", "255", "xyz"}

$ ./a.out -D abc -D -D xyz
variables_map["debug"] = {"1", "xyz"}

$ ./a.out -D abc -D 255 -D
variables_map["debug"] = {"1"}

我想我明白为什么会发生这种情况,隐含值{“1”}会替换现有的向量而不是添加它。我可以做些什么来使这个工作或者它是boost :: program_options的限制吗?

2 个答案:

答案 0 :(得分:3)

这是一种不需要修改增强源的解决方法。如果将分析和存储任务分开,则可以修改选项的中间boost::program_options::parsed_options向量。向量的每个元素都包含std::string个键和std::vector<std::string>个值。解决方法依赖于以下事实:对于隐式值,该值向量为空。如果我们扫描parsed_options隐式值并明确地为它们分配一个值,那么它们不会破坏同一个键的先前值。这是工作代码:

#include <iostream>
#include <string>
#include <vector>
#include <boost/foreach.hpp>
#include <boost/program_options.hpp>

namespace po = boost::program_options;

namespace std {
   // This overload is needed to use composed options.
   static std::ostream& operator<<(
      std::ostream& os,
      const std::vector<std::string>& v) {
      os << '{';
      BOOST_FOREACH(const std::string& s, v) {
         if (&s != &*v.begin())
            os << ", ";
         os << '"' << s << '"';
      }
      os << '}';
      return os;
   }
}

int main(int argc, char *argv[]) {
   po::options_description desc("Allowed options");
   desc.add_options()
      ("debug,D",
       po::value<std::vector<std::string> >()
       ->composing()
       ->implicit_value(std::vector<std::string>(1,"1")),
       "Set debug level.");

   // Just parse the options without storing them in the map.
   po::parsed_options parsed_options = po::command_line_parser(argc, argv)
      .options(desc)
      .run();

   // Implicit option values are empty, replace with default value.
   BOOST_FOREACH(po::option& o, parsed_options.options) {
      if (o.string_key == "debug" && o.value.empty())
         o.value.push_back("1"); // default value is "1"
   }

   // Now store and earlier values aren't clobbered.
   po::variables_map vm;
   po::store(parsed_options, vm);
   po::notify(vm);

   std::cout << "variables_map[\"debug\"] = "
             << (vm.count("debug") ?
                 vm["debug"].as<std::vector<std::string> >() :
                 std::vector<std::string>())
             << '\n';
   return 0;
}

以下是相同的测试用例:

$ ./a.out -D abc -D 255 -D xyz
variables_map["debug"] = {"abc", "255", "xyz"}

$ ./a.out -D -D 255 -D xyz
variables_map["debug"] = {"1", "255", "xyz"}

$ ./a.out -D abc -D -D xyz
variables_map["debug"] = {"abc", "1", "xyz"}

$ ./a.out -D abc -D 255 -D
variables_map["debug"] = {"abc", "255", "1"}

答案 1 :(得分:0)

好吧,我最终想出了一个似乎对我有用的解决方案。如果可能的话,从boost::program_options流利的人那里得到一些独立的验证会很好,但显然似乎没有人知道或关心它。

这是boost_1_49_0的一个补丁,它允许一个选项同时是composing()并且还有一个implicit_value()。

diff -Naur old/boost/program_options/detail/value_semantic.hpp new/boost/program_options/detail/value_semantic.hpp
--- old/boost/program_options/detail/value_semantic.hpp 2010-07-12 03:14:14.000000000 -0400
+++ new/boost/program_options/detail/value_semantic.hpp 2012-08-17 16:31:03.000000000 -0400
@@ -154,6 +154,28 @@
         }
     }

+    // Helper function to copy a non-vector implicit value into the
+    // tokens vector.
+    template<class T, class charT>
+    void get_implicit_tokens(std::vector<std::basic_string<charT> >& vs,
+                             const boost::any& a,
+                             T*, long) {
+        const T va = boost::any_cast<const T>(a);
+        vs.push_back(boost::lexical_cast<std::basic_string<charT> >(va));
+    }
+
+    // Helper function to copy a vector implicit value into the
+    // tokens vector.
+    template<class T, class charT>
+    void get_implicit_tokens(std::vector<std::basic_string<charT> >& vs,
+                             const boost::any& a,
+                             std::vector<T>*, int) {
+        const std::vector<T> va = boost::any_cast<const std::vector<T> >(a);
+        for (unsigned i = 0; i < va.size(); i++) {
+            vs.push_back(boost::lexical_cast<std::basic_string<charT> >(va[i]));
+        }
+    }
+
     template<class T, class charT>
     void 
     typed_value<T, charT>::
@@ -164,7 +186,14 @@
         // value, then assign the implicit value as the stored value;
         // otherwise, validate the user-provided token(s).
         if (new_tokens.empty() && !m_implicit_value.empty())
-            value_store = m_implicit_value;
+            if (m_composing) {
+                // Attempt to append the implicit value.
+                std::vector<std::basic_string<charT> > vs;
+                get_implicit_tokens(vs, m_implicit_value, (T*)0, 0);
+                validate(value_store, vs, (T*)0, 0);
+            } else {
+                value_store = m_implicit_value;
+            }
         else
             validate(value_store, new_tokens, (T*)0, 0);
     }