Utilizing boost, I would like to
The first part can be done with 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;
}
To the best of my knowledge writing an inifile is not possible with boost::program_options, but boost::property_tree works:
pt::ptree iniPropTree;
pt::ini_parser::write_ini("./used0.ini",iniPropTree);
Now the question is how can I translate the data stored in the po::variables_map to pt::ptree?
Reading the boost documentation leaves me with the impression that this is not possible. Is the following the only viable way?
iniPropTree.put<int>("ops1.i0",vm["ops1.i0"].as<int>();
It introduces quite a bit of redundancy for my taste. However, reading data into a property tree from the beginning does not seem to support checking for undefined/misspelled options.
Alternatively,is it possible to iterate over the contents of variables_map and somehow infer the corresponding datatype of each element?
The full code is here:
/*
* 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;
}
The contents of the inifile "testini.ini" are:
[ops1]
i0=2
After giving this problem some more time, I found a suitable compact solution:
The key is to write a function that translates entries from the variables_map to the propTree depending on their datatype (thx to sehe for putting me on the right track):
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); }
}
}
The full working example writes the correct inifile containing all read info:
/*
* 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;
}
Taking a variables_map vm from reading the commandline, it is also possible to update the values in the property tree (from reading the inifile) with:
string opsName = "ops1.i0"; if(vm.count(opsName)) p.i0 = vm[opsName].as<int>();