我有一个表示一个配置的地图.它是一个std :: string和boost :: any的地图.
该地图在开始时被初始化,我希望用户能够在命令行上覆盖这些选项.
我想做的是使用options_description :: add_option()方法从该地图构建程序选项.但是,它需要一个模板参数po :: value<>而我所有的都是boost :: any.
到目前为止,我只是有代码的shell. m_Config表示我的配置类,getTuples()返回一个std :: map< std :: string,Tuple>. TuplePair是一个typedef的std :: pair< std :: string,Tuple>而元组包含boost :: any我感兴趣的.
po::options_description desc; std::for_each(m_Config.getTuples().begin(),m_Config.getTuples().end(),[&desc](const TuplePair& _pair) { // what goes here? :) // desc.add_options() ( _pair.first,po::value<???>,""); });
有没有办法以这种方式建立,还是需要自己去做?
提前致谢!
解决方法
boost :: any不适用于你的问题.它执行最基本的类型擦除形式:存储和(类型安全)检索,就是这样.如您所见,不能执行其他操作.正如jhasse指出的,你可以测试你想要支持的每种类型,但这是一个维护噩梦.
更好的是扩大想法boost ::任何用途.不幸的是,这需要一些锅炉代码.如果你想尝试一下,现在正在邮件列表(标题为“[boost] RFC:type erasure”)上讨论一个新的Boost库,这个基本上是一个广义的类型擦除实用程序:你定义你想要的操作像您擦除的类型一样支持,它会生成正确的实用程序类型. (它可以模拟boost :: any,例如,要求擦除类型是可复制的和类型安全的,并且可以通过额外要求该类型可调用来模拟boost :: function&.
除此之外,您最好的选择可能是自己写这样一个类型.我会为你做的:
#include <boost/program_options.hpp> #include <typeinfo> #include <stdexcept> namespace po = boost::program_options; class any_option { public: any_option() : mContent(0) // no content {} template <typename T> any_option(const T& value) : mContent(new holder<T>(value)) { // above is where the erasure happens,// holder<T> inherits from our non-template // base class,which will make virtual calls // to the actual implementation; see below } any_option(const any_option& other) : mContent(other.empty() ? 0 : other.mContent->clone()) { // note we need an explicit clone method to copy,// since with an erased type it's impossible } any_option& operator=(any_option other) { // copy-and-swap idiom is short and sweet swap(*this,other); return *this; } ~any_option() { // delete our content when we're done delete mContent; } bool empty() const { return !mContent; } friend void swap(any_option& first,any_option& second) { std::swap(first.mContent,second.mContent); } // now we define the interface we'd like to support through erasure: // getting the data out if we know the type will be useful,// just like boost::any. (defined as friend free-function) template <typename T> friend T* any_option_cast(any_option*); // and the ability to query the type const std::type_info& type() const { return mContent->type(); // call actual function } // we also want to be able to call options_description::add_option(),// so we add a function that will do so (through a virtual call) void add_option(po::options_description desc,const char* name) { mContent->add_option(desc,name); // call actual function } private: // done with the interface,now we define the non-template base class,// which has virtual functions where we need type-erased functionality class placeholder { public: virtual ~placeholder() { // allow deletion through base with virtual destructor } // the interface needed to support any_option operations: // need to be able to clone the stored value virtual placeholder* clone() const = 0; // need to be able to test the stored type,for safe casts virtual const std::type_info& type() const = 0; // and need to be able to perform add_option with type info virtual void add_option(po::options_description desc,const char* name) = 0; }; // and the template derived class,which will support the interface template <typename T> class holder : public placeholder { public: holder(const T& value) : mValue(value) {} // implement the required interface: placeholder* clone() const { return new holder<T>(mValue); } const std::type_info& type() const { return typeid(mValue); } void add_option(po::options_description desc,const char* name) { desc.add_options()(name,po::value<T>(),""); } // finally,we have a direct value accessor T& value() { return mValue; } private: T mValue; // noncopyable,use cloning interface holder(const holder&); holder& operator=(const holder&); }; // finally,we store a pointer to the base class placeholder* mContent; }; class bad_any_option_cast : public std::bad_cast { public: const char* what() const throw() { return "bad_any_option_cast: Failed conversion"; } }; template <typename T> T* any_option_cast(any_option* anyOption) { typedef any_option::holder<T> holder; return anyOption.type() == typeid(T) ? &static_cast<holder*>(anyOption.mContent)->value() : 0; } template <typename T> const T* any_option_cast(const any_option* anyOption) { // none of the operations in non-const any_option_cast // are mutating,so this is safe and simple (constness // is restored to the return value automatically) return any_option_cast<T>(const_cast<any_option*>(anyOption)); } template <typename T> T& any_option_cast(any_option& anyOption) { T* result = any_option_cast(&anyOption); if (!result) throw bad_any_option_cast(); return *result; } template <typename T> const T& any_option_cast(const any_option& anyOption) { return any_option_cast<T>(const_cast<any_option&>(anyOption)); } // NOTE: My casting operator has slightly different use than // that of boost::any. Namely,it automatically returns a reference // to the stored value,so you don't need to (and cannot) specify it. // If you liked the old way,feel free to peek into their source. #include <boost/foreach.hpp> #include <map> int main() { // (it's a good exercise to step through this with // a debugger to see how it all comes together) typedef std::map<std::string,any_option> map_type; typedef map_type::value_type pair_type; map_type m; m.insert(std::make_pair("int",any_option(5))); m.insert(std::make_pair("double",any_option(3.14))); po::options_description desc; BOOST_FOREACH(pair_type& pair,m) { pair.second.add_option(desc,pair.first.c_str()); } // etc. }
让我知道如果有什么不清楚.