从字符串/提升::任何映射中构建提升::选项
Building boost::options from a string/boost::any map
我有一个代表配置的映射。 这是一张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>
的类型定义,元组包含我感兴趣的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::any
用途的想法。不幸的是,这需要一些样板代码。如果你想尝试一下,现在在邮件列表中正在讨论一个新的 Boost 库(标题为"[boost] RFC:类型擦除"(,它本质上是一个通用的类型擦除实用程序:你定义你希望你的擦除类型支持的操作,它会生成正确的实用程序类型。(它可以模拟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
{
// ...
}
});
// ...
如果您有多个类型,请考虑使用类型列表。
- Visual Studio C++ 它只构建选项卡中显示的文件吗?
- 以现代 CMake 方式控制包含的库中的构建选项(测试等)
- 使用Bazel构建具有不同编译器/链接器选项的C/C++依赖关系
- OpenSSL with CMake - 未知选项: --sysroot 在构建 openSSL 时
- C++:构建选项"-j"是什么意思?
- 当 /Whorearchive选项处于活动状态时,无法使用Visual Studio链接到Windows上的自构建张量
- 我如何在Qt中选择构建选项
- 如何从"开始组..结束组"链接器选项构建文件列表
- 使用-PG选项构建时出现不同的意外错误
- C++构建系统 - 远程编译和链接选项
- 从源代码构建Qt 5.8 - 配置:未知命令行选项'-c++11'
- gcc:错误:在 MacOSX 上构建 openjdk9 时'-mstack-alignment=16'无法识别的命令行选项
- Qt 5测试版2,Qt Creator 2.6,MSVC错误:Qt Creaator需要一个编译器来构建.在工具包选项中
- 代码::封锁前构建选项与命令行(QT)不一致
- 在MinGW中使用-j(jobs)选项构建wxWidgets时出错
- C++:使用 g++ 和 -m32 选项构建的 c++ 库在 Msys 下构建 wxWidgets 项目时出现 ld 不
- 为什么我在构建 avin2/SensorKinect for android、NDK、gcc 时出现无法识别的选项"--dynamic-linker"错误
- QtCreator 在使用 cmake 构建时会忽略 cmake 选项
- 将cmake构建选项导出到外部项目
- Sublime Text:编译器构建选项