如何解决从字符串/ boost :: any映射构建boost :: option
|| 我有一个代表配置的地图。这是std::string
和boost::any
的地图。
此映射在开始时已初始化,我希望用户能够在命令行上覆盖这些选项。
我想做的是使用options_description::add_option()
方法从此地图构建程序选项。但是,它使用模板参数po::value<>
,而我仅有的是boost::any
。
到目前为止,我只有代码的外壳。 m_Config
代表我的配置类,getTuples()
返回一个std::map<std::string,Tuple>
。 TuplePair
是std::pair<std::string,Tuple>
的typedef,元组包含我感兴趣的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指出的那样,您可以只测试要支持的每种类型,但这是维护的噩梦。
更好的是扩展“ 1”用法的想法。不幸的是,这需要一些样板代码。如果您想尝试一下,现在在邮件列表中正在讨论一个新的Boost库(标题为““ [boost] RFC:type erasure \”),它实际上是一种通用的类型擦除实用程序:您定义您希望擦除类型支持的操作,它会生成正确的实用程序类型。 (例如,可以通过要求已擦除的类型是可复制构造且类型安全的来模拟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.
}
让我知道是否有不清楚的地方。 :)
,template<class T>
bool any_is(const boost::any& a)
{
try
{
boost::any_cast<const T&>(a);
return true;
}
catch(boost::bad_any_cast&)
{
return false;
}
}
// ...
po::options_description desc;
std::for_each(m_Config.getTuples().begin(),m_Config.getTuples().end(),[&desc](const TuplePair& _pair)
{
if(any_is<int>(_pair.first))
{
desc.add_options() { _pair.first,po::value<int>,\"\"};
}
else if(any_is<std::string>(_pair.first))
{
desc.add_options() { _pair.first,po::value<std::string>,\"\"};
}
else
{
// ...
}
});
// ...
如果类型不只一种,请考虑使用类型列表。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。