Read/Write 带有 boost::{program_options,property_tree} 的 ini 文件
Read/Write inifiles with boost::{program_options,property_tree}
利用boost,我想
- 从 ini 文件中读取选项,如果在 ini 文件中遇到未知选项则中止并且
- 稍后将它们保存在另一个 ini 文件中。
第一部分可以用boost::program_options完成:
try{
inifile_options.add_options()
("ops1.i0", po::value<int>(&p.nx)->default_value(1), "test integer")
;
po::variables_map vm;
po::store(po::parse_config_file(pthfnini, inifile_options), vm);
po::notify(vm);
}
catch(exception& e){
cerr << "error: " << e.what() << "\n";
errorflag=1;
}
据我所知,boost::program_options 无法编写 ini 文件,但 boost::property_tree 可以:
pt::ptree iniPropTree;
pt::ini_parser::write_ini("./used0.ini",iniPropTree);
现在的问题是如何将 po::variables_map 中存储的数据转换为 pt::ptree?
阅读 boost 文档给我的印象是这是不可能的。以下是唯一可行的方法吗?
iniPropTree.put<int>("ops1.i0",vm["ops1.i0"].as<int>();
根据我的口味,它引入了相当多的冗余。但是,从头开始将数据读入 属性 树似乎不支持检查 undefined/misspelled 选项。
或者,是否可以迭代 variables_map 的内容并以某种方式推断出每个元素的相应数据类型?
完整代码在这里:
/*
* g++ iniOps_test.cpp -Wall -std=c++11 -O3 -lboost_system -lboost_program_options -o iniOps_test.exe
*
*/
// C++11 & Boost libraries
#include <boost/program_options.hpp> // po::options_description, po::variables_map, ...
#include <boost/property_tree/ptree.hpp> // pt::ptree
#include <boost/property_tree/ini_parser.hpp> // write_ini()
#include <iostream> // cout
#include <fstream> // ofstream, ifstream
// namespaces
namespace po = boost::program_options;
namespace pt = boost::property_tree;
using namespace std;
struct params{
std::string inipthfn;
int i0;
};
void read_inifile(params &p, po::variables_map &vm){
// initialize variables
int errorflag=0;
std::ifstream pthfnini("./testini.ini");
po::options_description inifile_options("Allowed inifile options");
try{
inifile_options.add_options()
("ops1.i0", po::value<int>(&p.i0)->default_value(1), "test integer")
;
;
po::store(po::parse_config_file(pthfnini, inifile_options), vm);
po::notify(vm);
}
catch(exception& e){
cerr << "error: " << e.what() << "\n";
errorflag=1;
}
pthfnini.close();
if(errorflag){ std::cout<<"--- program shutdown due to error in read_inifile ---"<<std::endl; exit(1); }
}
int main(){
params p;
po::variables_map vm;
pt::ptree iniPropTree;
read_inifile(p,vm); // get options from inifile
// ??? conversion from vm -> pt ???
pt::ini_parser::write_ini("./used0.ini",iniPropTree); // save options to used.ini
cout << p.i0 << endl;
return 0;
}
ini 文件 "testini.ini" 的内容是:
[ops1]
i0=2
这里有一个概念问题。
命令行参数本质上是文本的。
变量映射中的值不是。使用的类型在值语义中配置(选项描述的一部分)。
如果您的所有选项都具有相同的类型,您可以"cheat"并对转换进行硬编码:
pt::ptree to_ptree(po::variables_map const& vm) {
pt::ptree tree;
for (auto& v : vm) {
if (!v.second.empty() && !v.second.defaulted())
tree.put(v.first, v.second.as<int>());
}
return tree;
}
节省:
[ops1]
i0=1
如果您需要更大的灵活性,您至少需要访问选项说明。这不是该库的预期用途,您可能很快就会 运行 进入实施的未记录部分。
However, reading data into a property tree from the beginning does not seem to support checking for undefined/misspelled options
嗯。这不完全正确。您可以创建自己的解析函数来添加逻辑。如果需要,请使用 属性 树翻译器。
这是一个扩展示例,显示了要验证的三个不同类型的参数:
enum class restricted { value1, value2 };
struct params {
int i0 = 1;
restricted r1 = restricted::value2;
std::string s2 = "some default";
};
我们想要一个像这样的解析函数:
params read_inifile(std::string filename) {
params p;
pt::ptree tree;
std::ifstream file(filename);
read_ini(file, tree);
p.i0 = tree.get("ops1.i0", 1);
p.r1 = tree.get("ops1.r1", restricted::value2);
p.s2 = tree.get("ops1.s2", "some default");
return p;
}
流媒体类型
要翻译和验证枚举,您只需要实现流操作符:
static inline std::istream& operator>>(std::istream& is, restricted& r) {
std::string v;
if (is >> std::ws >> v) {
if (boost::iequals("value1", v))
r = restricted::value1;
else if (boost::iequals("value2", v))
r = restricted::value2;
else
throw std::runtime_error("invalid restricted value");
}
return is;
}
static inline std::ostream& operator<<(std::ostream& os, restricted r) {
switch(r) {
case restricted::value1: return os << "value1";
case restricted::value2: return os << "value2";
default: return os << "invalid";
}
}
自定义翻译器
假设 i0
需要自定义验证。在这个例子中,让我们要求它是一个奇数:
namespace translators {
template <typename T>
struct must_be_odd {
typedef T internal_type;
typedef T external_type;
boost::optional<T> get_value(const std::string& str) const {
if (str.empty()) return boost::none;
T v = boost::lexical_cast<T>(str);
if (v % 2 == 0)
throw std::runtime_error("value must be odd");
return boost::make_optional(v);
}
boost::optional<std::string> put_value(const T& i0) {
assert(i0 % 2); // assert that the value was odd
return boost::lexical_cast<std::string>(i0);
}
};
static const must_be_odd<int> i0;
}
现在我们可以简单地提供翻译器(在这里,更像是自定义验证器,如 Boost Program Options 也有它们):
p.i0 = tree.get("ops1.i0", 1, translators::i0);
See it Live On Coliru
不支持的选项
这有点多。您必须迭代树,根据已知集检查结果路径。这是一个相当通用的实现(它应该适用于任何(宽)字符串类型的区分大小写的树):
template <typename Tree,
typename Path = typename Tree::path_type,
typename Key = typename Path::key_type,
typename Cmp = typename Tree::key_compare>
std::size_t unsupported(Tree const& tree, std::set<Key, Cmp> const& supported, Path prefix = "") {
if (tree.size()) {
std::size_t n = 0;
for (auto& node : tree) {
Path sub = prefix;
sub /= node.first;
n += unsupported(node.second, supported, sub);
}
return n;
} else {
if (!supported.count(prefix.dump()) && tree.template get_value_optional<std::string>())
return 1;
}
return 0;
}
你可以这样使用它:
if (auto n = unsupported(tree, {"ops1.i0", "ops1.r1", "ops2.s2"})) {
throw std::runtime_error(std::to_string(n) + " unsupported options");
}
完整演示
#include <boost/algorithm/string.hpp>
#include <iostream>
#include <set>
enum class restricted { value1, value2 };
static inline std::istream& operator>>(std::istream& is, restricted& r) {
std::string v;
if (is >> std::ws >> v) {
if (boost::iequals("value1", v))
r = restricted::value1;
else if (boost::iequals("value2", v))
r = restricted::value2;
else
throw std::runtime_error("invalid restricted value");
}
return is;
}
static inline std::ostream& operator<<(std::ostream& os, restricted r) {
switch(r) {
case restricted::value1: return os << "value1";
case restricted::value2: return os << "value2";
default: return os << "invalid";
}
}
struct params {
int i0 = 1;
restricted r1 = restricted::value2;
std::string s2 = "some default";
};
#include <boost/property_tree/ini_parser.hpp>
#include <boost/lexical_cast.hpp>
#include <fstream>
namespace pt = boost::property_tree;
namespace translators {
template <typename T>
struct must_be_odd {
typedef T internal_type;
typedef T external_type;
boost::optional<T> get_value(const std::string& str) const {
if (str.empty()) return boost::none;
T v = boost::lexical_cast<T>(str);
if (v % 2 == 0)
throw std::runtime_error("value must be odd");
return boost::make_optional(v);
}
boost::optional<std::string> put_value(const T& i0) {
assert(i0 % 2); // assert that the value was odd
return boost::lexical_cast<std::string>(i0);
}
};
static const must_be_odd<int> i0;
}
template <typename Tree,
typename Path = typename Tree::path_type,
typename Key = typename Path::key_type,
typename Cmp = typename Tree::key_compare>
std::size_t unsupported(Tree const& tree, std::set<Key, Cmp> const& supported, Path prefix = "") {
if (tree.size()) {
std::size_t n = 0;
for (auto& node : tree) {
Path sub = prefix;
sub /= node.first;
n += unsupported(node.second, supported, sub);
}
return n;
} else {
if (!supported.count(prefix.dump()) && tree.template get_value_optional<std::string>())
return 1;
}
return 0;
}
params read_inifile(std::string filename) {
params p;
try {
pt::ptree tree;
std::ifstream file(filename);
read_ini(file, tree);
p.i0 = tree.get("ops1.i0", 1, translators::i0);
p.r1 = tree.get("ops1.r1", restricted::value2);
p.s2 = tree.get("ops1.s2", "some default");
if (auto n = unsupported(tree, {"ops1.i0", "ops1.r1", "ops2.s2"})) {
throw std::runtime_error(std::to_string(n) + " unsupported options");
}
} catch (std::exception const& e) {
std::cerr << "error: " << e.what() << "\n";
throw std::runtime_error("read_inifile");
}
return p;
}
pt::ptree to_ptree(params const& p) {
pt::ptree tree;
tree.put("ops1.i0", p.i0, translators::i0);
tree.put("ops1.r1", p.r1);
tree.put("ops1.s2", p.s2);
return tree;
}
int main() {
params const p = read_inifile("./testini.ini"); // get options from filename
write_ini("./used0.ini", to_ptree(p)); // save options to used.ini
std::cout << p.i0 << std::endl;
}
输入类似
[ops1]
i0=17
i99=oops
[oops1]
also=oops
版画
error: 2 unsupported options
terminate called after throwing an instance of 'std::runtime_error'
what(): read_inifile
error: value must be odd
terminate called after throwing an instance of 'std::runtime_error'
what(): read_inifile
在有效输入上,used0.ini
将按预期写入:
[ops1]
i0=1
r1=value2
s2=some default
经过更多时间解决这个问题,我找到了一个合适的紧凑型解决方案:
关键是编写一个函数,根据它们的数据类型将 variables_map 中的条目转换为 propTree(感谢 sehe 让我走上正轨):
void translate_variables_map_to_ptree(po::variables_map &vm, pt::ptree &propTree){
for(po::variables_map::iterator it=vm.begin(); it!=vm.end(); it++){
if( it->second.value().type() == typeid(int) ){ propTree.put<int>(it->first,vm[it->first].as<int>()); }
else if( it->second.value().type() == typeid(float) ){ propTree.put<float>(it->first,vm[it->first].as<float>()); }
else if( it->second.value().type() == typeid(double) ){ propTree.put<double>(it->first,vm[it->first].as<double>()); }
else if( it->second.value().type() == typeid(std::string) ){ propTree.put<std::string>(it->first,vm[it->first].as<std::string>()); }
else if( it->second.value().type() == typeid(size_t) ){ propTree.put<size_t>(it->first,vm[it->first].as<size_t>()); }
else{ printf("Error: unknown datatype. Abort!\n"); exit(EXIT_FAILURE); }
}
}
完整的工作示例写入包含所有读取信息的正确 ini 文件:
/*
* g++ iniOps_test.cpp -Wall -std=c++11 -O3 -lboost_system -lboost_program_options -o iniOps_test.exe
*
*/
// C++11 & Boost libraries
#include <boost/program_options.hpp> // po::options_description, po::variables_map, ...
#include <boost/property_tree/ptree.hpp> // pt::ptree
#include <boost/property_tree/ini_parser.hpp> // write_ini()
#include <iostream> // cout
#include <fstream> // ofstream, ifstream
// namespaces
namespace po = boost::program_options;
namespace pt = boost::property_tree;
using namespace std;
struct params{
std::string s0;
int i0;
};
void read_inifile(params &p, po::variables_map &vm){
// initialize variables
int errorflag=0;
std::ifstream pthfnini("./testini.ini");
po::options_description inifile_options("Allowed inifile options");
try{
inifile_options.add_options()
("ops1.i0", po::value<int>(&p.i0)->default_value(1), "test integer")
("ops1.s0", po::value<std::string>(&p.s0)->default_value("default"), "test string")
;
;
po::store(po::parse_config_file(pthfnini, inifile_options), vm);
po::notify(vm);
}
catch(exception& e){
cerr << "error: " << e.what() << "\n";
errorflag=1;
}
pthfnini.close();
if(errorflag){ std::cout<<"--- program shutdown due to error in read_inifile ---"<<std::endl; exit(1); }
}
void translate_variables_map_to_ptree(po::variables_map &vm, pt::ptree &propTree){
for(po::variables_map::iterator it=vm.begin(); it!=vm.end(); it++){
if( it->second.value().type() == typeid(int) ){ propTree.put<int>(it->first,vm[it->first].as<int>()); }
else if( it->second.value().type() == typeid(float) ){ propTree.put<float>(it->first,vm[it->first].as<float>()); }
else if( it->second.value().type() == typeid(double) ){ propTree.put<double>(it->first,vm[it->first].as<double>()); }
else if( it->second.value().type() == typeid(std::string) ){ propTree.put<std::string>(it->first,vm[it->first].as<std::string>()); }
else if( it->second.value().type() == typeid(size_t) ){ propTree.put<size_t>(it->first,vm[it->first].as<size_t>()); }
else{ printf("Error: unknown datatype. Abort!\n"); exit(EXIT_FAILURE); }
}
}
int main(){
params p;
po::variables_map vm;
pt::ptree iniPropTree;
read_inifile(p,vm); // get options from inifile
translate_variables_map_to_ptree(vm,iniPropTree); // conversion from vm -> pt
pt::ini_parser::write_ini("./used0.ini",iniPropTree); // save options to used.ini
cout << p.i0 << endl;
cout << p.s0 << endl;
return 0;
}
通过读取命令行获取 variables_map vm,也可以更新 属性 树中的值(通过读取 inifile):
string opsName = "ops1.i0"; if(vm.count(opsName)) p.i0 = vm[opsName].as<int>();
利用boost,我想
- 从 ini 文件中读取选项,如果在 ini 文件中遇到未知选项则中止并且
- 稍后将它们保存在另一个 ini 文件中。
第一部分可以用boost::program_options完成:
try{
inifile_options.add_options()
("ops1.i0", po::value<int>(&p.nx)->default_value(1), "test integer")
;
po::variables_map vm;
po::store(po::parse_config_file(pthfnini, inifile_options), vm);
po::notify(vm);
}
catch(exception& e){
cerr << "error: " << e.what() << "\n";
errorflag=1;
}
据我所知,boost::program_options 无法编写 ini 文件,但 boost::property_tree 可以:
pt::ptree iniPropTree;
pt::ini_parser::write_ini("./used0.ini",iniPropTree);
现在的问题是如何将 po::variables_map 中存储的数据转换为 pt::ptree?
阅读 boost 文档给我的印象是这是不可能的。以下是唯一可行的方法吗?
iniPropTree.put<int>("ops1.i0",vm["ops1.i0"].as<int>();
根据我的口味,它引入了相当多的冗余。但是,从头开始将数据读入 属性 树似乎不支持检查 undefined/misspelled 选项。
或者,是否可以迭代 variables_map 的内容并以某种方式推断出每个元素的相应数据类型?
完整代码在这里:
/*
* g++ iniOps_test.cpp -Wall -std=c++11 -O3 -lboost_system -lboost_program_options -o iniOps_test.exe
*
*/
// C++11 & Boost libraries
#include <boost/program_options.hpp> // po::options_description, po::variables_map, ...
#include <boost/property_tree/ptree.hpp> // pt::ptree
#include <boost/property_tree/ini_parser.hpp> // write_ini()
#include <iostream> // cout
#include <fstream> // ofstream, ifstream
// namespaces
namespace po = boost::program_options;
namespace pt = boost::property_tree;
using namespace std;
struct params{
std::string inipthfn;
int i0;
};
void read_inifile(params &p, po::variables_map &vm){
// initialize variables
int errorflag=0;
std::ifstream pthfnini("./testini.ini");
po::options_description inifile_options("Allowed inifile options");
try{
inifile_options.add_options()
("ops1.i0", po::value<int>(&p.i0)->default_value(1), "test integer")
;
;
po::store(po::parse_config_file(pthfnini, inifile_options), vm);
po::notify(vm);
}
catch(exception& e){
cerr << "error: " << e.what() << "\n";
errorflag=1;
}
pthfnini.close();
if(errorflag){ std::cout<<"--- program shutdown due to error in read_inifile ---"<<std::endl; exit(1); }
}
int main(){
params p;
po::variables_map vm;
pt::ptree iniPropTree;
read_inifile(p,vm); // get options from inifile
// ??? conversion from vm -> pt ???
pt::ini_parser::write_ini("./used0.ini",iniPropTree); // save options to used.ini
cout << p.i0 << endl;
return 0;
}
ini 文件 "testini.ini" 的内容是:
[ops1]
i0=2
这里有一个概念问题。
命令行参数本质上是文本的。
变量映射中的值不是。使用的类型在值语义中配置(选项描述的一部分)。
如果您的所有选项都具有相同的类型,您可以"cheat"并对转换进行硬编码:
pt::ptree to_ptree(po::variables_map const& vm) {
pt::ptree tree;
for (auto& v : vm) {
if (!v.second.empty() && !v.second.defaulted())
tree.put(v.first, v.second.as<int>());
}
return tree;
}
节省:
[ops1]
i0=1
如果您需要更大的灵活性,您至少需要访问选项说明。这不是该库的预期用途,您可能很快就会 运行 进入实施的未记录部分。
However, reading data into a property tree from the beginning does not seem to support checking for undefined/misspelled options
嗯。这不完全正确。您可以创建自己的解析函数来添加逻辑。如果需要,请使用 属性 树翻译器。
这是一个扩展示例,显示了要验证的三个不同类型的参数:
enum class restricted { value1, value2 };
struct params {
int i0 = 1;
restricted r1 = restricted::value2;
std::string s2 = "some default";
};
我们想要一个像这样的解析函数:
params read_inifile(std::string filename) {
params p;
pt::ptree tree;
std::ifstream file(filename);
read_ini(file, tree);
p.i0 = tree.get("ops1.i0", 1);
p.r1 = tree.get("ops1.r1", restricted::value2);
p.s2 = tree.get("ops1.s2", "some default");
return p;
}
流媒体类型
要翻译和验证枚举,您只需要实现流操作符:
static inline std::istream& operator>>(std::istream& is, restricted& r) {
std::string v;
if (is >> std::ws >> v) {
if (boost::iequals("value1", v))
r = restricted::value1;
else if (boost::iequals("value2", v))
r = restricted::value2;
else
throw std::runtime_error("invalid restricted value");
}
return is;
}
static inline std::ostream& operator<<(std::ostream& os, restricted r) {
switch(r) {
case restricted::value1: return os << "value1";
case restricted::value2: return os << "value2";
default: return os << "invalid";
}
}
自定义翻译器
假设 i0
需要自定义验证。在这个例子中,让我们要求它是一个奇数:
namespace translators {
template <typename T>
struct must_be_odd {
typedef T internal_type;
typedef T external_type;
boost::optional<T> get_value(const std::string& str) const {
if (str.empty()) return boost::none;
T v = boost::lexical_cast<T>(str);
if (v % 2 == 0)
throw std::runtime_error("value must be odd");
return boost::make_optional(v);
}
boost::optional<std::string> put_value(const T& i0) {
assert(i0 % 2); // assert that the value was odd
return boost::lexical_cast<std::string>(i0);
}
};
static const must_be_odd<int> i0;
}
现在我们可以简单地提供翻译器(在这里,更像是自定义验证器,如 Boost Program Options 也有它们):
p.i0 = tree.get("ops1.i0", 1, translators::i0);
See it Live On Coliru
不支持的选项
这有点多。您必须迭代树,根据已知集检查结果路径。这是一个相当通用的实现(它应该适用于任何(宽)字符串类型的区分大小写的树):
template <typename Tree,
typename Path = typename Tree::path_type,
typename Key = typename Path::key_type,
typename Cmp = typename Tree::key_compare>
std::size_t unsupported(Tree const& tree, std::set<Key, Cmp> const& supported, Path prefix = "") {
if (tree.size()) {
std::size_t n = 0;
for (auto& node : tree) {
Path sub = prefix;
sub /= node.first;
n += unsupported(node.second, supported, sub);
}
return n;
} else {
if (!supported.count(prefix.dump()) && tree.template get_value_optional<std::string>())
return 1;
}
return 0;
}
你可以这样使用它:
if (auto n = unsupported(tree, {"ops1.i0", "ops1.r1", "ops2.s2"})) {
throw std::runtime_error(std::to_string(n) + " unsupported options");
}
完整演示
#include <boost/algorithm/string.hpp>
#include <iostream>
#include <set>
enum class restricted { value1, value2 };
static inline std::istream& operator>>(std::istream& is, restricted& r) {
std::string v;
if (is >> std::ws >> v) {
if (boost::iequals("value1", v))
r = restricted::value1;
else if (boost::iequals("value2", v))
r = restricted::value2;
else
throw std::runtime_error("invalid restricted value");
}
return is;
}
static inline std::ostream& operator<<(std::ostream& os, restricted r) {
switch(r) {
case restricted::value1: return os << "value1";
case restricted::value2: return os << "value2";
default: return os << "invalid";
}
}
struct params {
int i0 = 1;
restricted r1 = restricted::value2;
std::string s2 = "some default";
};
#include <boost/property_tree/ini_parser.hpp>
#include <boost/lexical_cast.hpp>
#include <fstream>
namespace pt = boost::property_tree;
namespace translators {
template <typename T>
struct must_be_odd {
typedef T internal_type;
typedef T external_type;
boost::optional<T> get_value(const std::string& str) const {
if (str.empty()) return boost::none;
T v = boost::lexical_cast<T>(str);
if (v % 2 == 0)
throw std::runtime_error("value must be odd");
return boost::make_optional(v);
}
boost::optional<std::string> put_value(const T& i0) {
assert(i0 % 2); // assert that the value was odd
return boost::lexical_cast<std::string>(i0);
}
};
static const must_be_odd<int> i0;
}
template <typename Tree,
typename Path = typename Tree::path_type,
typename Key = typename Path::key_type,
typename Cmp = typename Tree::key_compare>
std::size_t unsupported(Tree const& tree, std::set<Key, Cmp> const& supported, Path prefix = "") {
if (tree.size()) {
std::size_t n = 0;
for (auto& node : tree) {
Path sub = prefix;
sub /= node.first;
n += unsupported(node.second, supported, sub);
}
return n;
} else {
if (!supported.count(prefix.dump()) && tree.template get_value_optional<std::string>())
return 1;
}
return 0;
}
params read_inifile(std::string filename) {
params p;
try {
pt::ptree tree;
std::ifstream file(filename);
read_ini(file, tree);
p.i0 = tree.get("ops1.i0", 1, translators::i0);
p.r1 = tree.get("ops1.r1", restricted::value2);
p.s2 = tree.get("ops1.s2", "some default");
if (auto n = unsupported(tree, {"ops1.i0", "ops1.r1", "ops2.s2"})) {
throw std::runtime_error(std::to_string(n) + " unsupported options");
}
} catch (std::exception const& e) {
std::cerr << "error: " << e.what() << "\n";
throw std::runtime_error("read_inifile");
}
return p;
}
pt::ptree to_ptree(params const& p) {
pt::ptree tree;
tree.put("ops1.i0", p.i0, translators::i0);
tree.put("ops1.r1", p.r1);
tree.put("ops1.s2", p.s2);
return tree;
}
int main() {
params const p = read_inifile("./testini.ini"); // get options from filename
write_ini("./used0.ini", to_ptree(p)); // save options to used.ini
std::cout << p.i0 << std::endl;
}
输入类似
[ops1]
i0=17
i99=oops
[oops1]
also=oops
版画
error: 2 unsupported options
terminate called after throwing an instance of 'std::runtime_error'
what(): read_inifile
error: value must be odd
terminate called after throwing an instance of 'std::runtime_error'
what(): read_inifile
在有效输入上,used0.ini
将按预期写入:
[ops1]
i0=1
r1=value2
s2=some default
经过更多时间解决这个问题,我找到了一个合适的紧凑型解决方案:
关键是编写一个函数,根据它们的数据类型将 variables_map 中的条目转换为 propTree(感谢 sehe 让我走上正轨):
void translate_variables_map_to_ptree(po::variables_map &vm, pt::ptree &propTree){
for(po::variables_map::iterator it=vm.begin(); it!=vm.end(); it++){
if( it->second.value().type() == typeid(int) ){ propTree.put<int>(it->first,vm[it->first].as<int>()); }
else if( it->second.value().type() == typeid(float) ){ propTree.put<float>(it->first,vm[it->first].as<float>()); }
else if( it->second.value().type() == typeid(double) ){ propTree.put<double>(it->first,vm[it->first].as<double>()); }
else if( it->second.value().type() == typeid(std::string) ){ propTree.put<std::string>(it->first,vm[it->first].as<std::string>()); }
else if( it->second.value().type() == typeid(size_t) ){ propTree.put<size_t>(it->first,vm[it->first].as<size_t>()); }
else{ printf("Error: unknown datatype. Abort!\n"); exit(EXIT_FAILURE); }
}
}
完整的工作示例写入包含所有读取信息的正确 ini 文件:
/*
* g++ iniOps_test.cpp -Wall -std=c++11 -O3 -lboost_system -lboost_program_options -o iniOps_test.exe
*
*/
// C++11 & Boost libraries
#include <boost/program_options.hpp> // po::options_description, po::variables_map, ...
#include <boost/property_tree/ptree.hpp> // pt::ptree
#include <boost/property_tree/ini_parser.hpp> // write_ini()
#include <iostream> // cout
#include <fstream> // ofstream, ifstream
// namespaces
namespace po = boost::program_options;
namespace pt = boost::property_tree;
using namespace std;
struct params{
std::string s0;
int i0;
};
void read_inifile(params &p, po::variables_map &vm){
// initialize variables
int errorflag=0;
std::ifstream pthfnini("./testini.ini");
po::options_description inifile_options("Allowed inifile options");
try{
inifile_options.add_options()
("ops1.i0", po::value<int>(&p.i0)->default_value(1), "test integer")
("ops1.s0", po::value<std::string>(&p.s0)->default_value("default"), "test string")
;
;
po::store(po::parse_config_file(pthfnini, inifile_options), vm);
po::notify(vm);
}
catch(exception& e){
cerr << "error: " << e.what() << "\n";
errorflag=1;
}
pthfnini.close();
if(errorflag){ std::cout<<"--- program shutdown due to error in read_inifile ---"<<std::endl; exit(1); }
}
void translate_variables_map_to_ptree(po::variables_map &vm, pt::ptree &propTree){
for(po::variables_map::iterator it=vm.begin(); it!=vm.end(); it++){
if( it->second.value().type() == typeid(int) ){ propTree.put<int>(it->first,vm[it->first].as<int>()); }
else if( it->second.value().type() == typeid(float) ){ propTree.put<float>(it->first,vm[it->first].as<float>()); }
else if( it->second.value().type() == typeid(double) ){ propTree.put<double>(it->first,vm[it->first].as<double>()); }
else if( it->second.value().type() == typeid(std::string) ){ propTree.put<std::string>(it->first,vm[it->first].as<std::string>()); }
else if( it->second.value().type() == typeid(size_t) ){ propTree.put<size_t>(it->first,vm[it->first].as<size_t>()); }
else{ printf("Error: unknown datatype. Abort!\n"); exit(EXIT_FAILURE); }
}
}
int main(){
params p;
po::variables_map vm;
pt::ptree iniPropTree;
read_inifile(p,vm); // get options from inifile
translate_variables_map_to_ptree(vm,iniPropTree); // conversion from vm -> pt
pt::ini_parser::write_ini("./used0.ini",iniPropTree); // save options to used.ini
cout << p.i0 << endl;
cout << p.s0 << endl;
return 0;
}
通过读取命令行获取 variables_map vm,也可以更新 属性 树中的值(通过读取 inifile):
string opsName = "ops1.i0"; if(vm.count(opsName)) p.i0 = vm[opsName].as<int>();