I find it convenient to have a unified text options, similar to those ini/rc files, with serializer/de-serializer for both Java and C++ and here is it.
Sample text file:
testmode=3
block_repeat=100
# floating point
scaling_factor=0.9
# arrays
c=1.0, -2.0, 1.0
b[0]=-1.5, 2.0, -0.5, 0.0
b[1]=0.0, 0.5
b[2]=0.00000000e+00, 6.78046882e-01, -8.91344100e-02
b[3]=0.00000000e+00, 7.75490344e-01, -1.71563059e-01, 2.25807708e-02
# strings
log_file=/tmp/test.log
Java source:
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Options {
private void init() {
opt_l = new HashMap<String, Long>();
opt_f = new HashMap<String, Float>();
opt_ff = new HashMap<String, Double>();
opt_s = new HashMap<String, String>();
}
public Options() {
init();
doublePrecision = false;
}
public Options(Boolean dblPrecision) {
init();
doublePrecision = dblPrecision;
}
public Options(String text) {
init();
doublePrecision = false;
from(text);
}
public Options(String text, Boolean dblPrecision) {
init();
doublePrecision = dblPrecision;
from(text);
}
private HashMap<String, Long> opt_l;
private HashMap<String, Float> opt_f;
private HashMap<String, Double> opt_ff;
private HashMap<String, String> opt_s;
private List validKeys_s, validKeys_f, validKeys_l;
private Boolean doublePrecision;
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder str = new StringBuilder(512);
str.append("_double_precision = " + (doublePrecision ? 1 : 0) + "\n");
str.append("; ======== Integer Options ========\n");
for (Map.Entry<String, Long> entry : opt_l.entrySet()) {
str.append(entry.getKey() + " = " + entry.getValue() + "\n");
}
str.append("; ======== " + (doublePrecision ? "Double" : "Float") + " Options ========\n");
for (Map.Entry<String, Float> entry : opt_f.entrySet()) {
str.append(entry.getKey() + " = " + String.format("%.8e", entry.getValue()) + "\n");
}
for (Map.Entry<String, Double> entry : opt_ff.entrySet()) {
str.append(entry.getKey() + " = " + String.format("%.16e", entry.getValue()) + "\n");
}
str.append("; ======== String Options ========\n");
for (Map.Entry<String, String> entry : opt_s.entrySet()) {
str.append(entry.getKey() + " = " + entry.getValue() + "\n");
}
return str.toString();
}
public void from(String text) {
for (String line : text.split("[\\r\\n]+")) {
parseLine(line);
}
}
/**
* Test the regex before using it
*/
@Deprecated
public static Boolean isNumber(String str) {
return str.matches("\\d") && str.matches("^[+-]?\\d*\\.?\\d*([eE]\\d)?\\d*$");
}
public static Boolean isInteger(String str) {
return str.matches("^[+-]?\\d+$");
}
public Boolean isDoublePrecision() {
return doublePrecision;
}
public void setDoublePrecision(boolean state) {
doublePrecision = state;
}
public void parseLine(String line) {
line.trim();
if (line.startsWith(";") || line.startsWith("#")) {
return;
}
String[] pair = line.split("\\s*=\\s*", 2);
if (pair.length < 2) { return; } try { long v = Long.parseLong(pair[1]); if (pair[0].equals("_double_precison") && v > 0) {
doublePrecision = true;
} else {
opt_l.put(pair[0], v);
}
} catch (NumberFormatException e1) {
try {
double v = Double.parseDouble(pair[1]);
if (doublePrecision) {
opt_ff.put(pair[0], v);
} else {
opt_f.put(pair[0], (float) v);
}
} catch (NumberFormatException e2) {
opt_s.put(pair[0], pair[1]);
}
}
}
public void clear() {
opt_l.clear();
opt_f.clear();
opt_ff.clear();
opt_s.clear();
}
public long getl(String key, long... default_v) {
long v = default_v.length > 0 ? default_v[0] : 0;
return opt_l.containsKey(key) ? opt_l.get(key) : v;
}
/*
* Does not return boolean, instead, an interger indicates how many alternative settings are there
*/
public int countl(String key) {
return opt_l.containsKey(key) ? 1 : 0;
}
public int geti(String key, int... default_v) {
int v = default_v.length > 0 ? default_v[0] : 0;
return (int) getl(key, v);
}
public int counti(String key) {
return countl(key);
}
public double getff(String key, double... default_v) {
double v = default_v.length > 0 ? default_v[0] : 0;
return opt_ff.containsKey(key) ? opt_ff.get(key) : opt_f.containsKey(key) ? opt_f.get(key)
: opt_l.containsKey(key) ? opt_l.get(key) : v;
}
public int countff(String key) {
return (opt_ff.containsKey(key) ? 1 : 0) + (opt_f.containsKey(key) ? 1 : 0) + (opt_l.containsKey(key) ? 1 : 0);
}
public float getf(String key, float... default_v) {
float v = default_v.length > 0 ? default_v[0] : 0;
return opt_f.containsKey(key) ? opt_f.get(key) : opt_ff.containsKey(key) ? opt_ff.get(key).floatValue() : opt_l
.containsKey(key) ? opt_l.get(key) : v;
}
public int countf(String key) {
return countff(key);
}
public String gets(String key, String... default_v) {
String v = default_v.length > 0 ? default_v[0] : "";
return opt_s.containsKey(key) ? opt_s.get(key) : v;
}
public int counts(String key) {
return opt_s.containsKey(key) ? 1 : 0;
}
public boolean containsLong(String key) {
return countl(key) > 0;
}
public boolean containsInt(String key) {
return counti(key) > 0;
}
public boolean containsDouble(String key) {
return countff(key) > 0;
}
public boolean containsFloat(String key) {
return countf(key) > 0;
}
public boolean containsString(String key) {
return counts(key) > 0;
}
public void set(String key, long v) {
opt_l.put(key, v);
}
public void set(String key, int v) {
opt_l.put(key, (long) v);
}
public void set(String key, double v) {
if (doublePrecision) {
opt_ff.put(key, v);
} else {
opt_f.put(key, (float) v);
}
}
public void set(String key, float v) {
if (doublePrecision) {
opt_ff.put(key, (double) v);
} else {
opt_f.put(key, v);
}
}
public void set(String key, String v) {
opt_s.put(key, v);
}
public static void main(String[] args) {
Options opt = new Options("int1=1\nfloat2= 3.0 \nfloat3= .3e-5 \nstring_test = TEST STRING");
System.out.println(opt);
String text = opt.toString();
opt = new Options(text, true);
System.out.println(opt);
}
public static String floats2str(float[] v) {
String val = String.format("%.8e", v[0]);
for (int i = 1; i < v.length; i++) {
val += String.format(", %.8e", v[i]);
}
return val;
}
public static String doubles2str(double[] v) {
String val = String.format("%.16e", v[0]);
for (int i = 1; i < v.length; i++) {
val += String.format(", %.16e", v[i]);
}
return val;
}
/**
* This method sets a string entry of the arrays with sufficient precisions
* for simplicity, no { } to enclose it (no plan to support 2D array)
*/
public void set(String key, float[] v) {
set(key, floats2str(v));
}
public void set(String key, double[] v) {
set(key, doubles2str(v));
}
/*
* This method will fail if cannot parse properly
*/
public static float[] str2floats(String str) {
String[] strs = str.split(",\\s*");
float[] floats = new float[strs.length];
for (int i = 0; i < strs.length; i++) {
floats[i] = Float.parseFloat(strs[i]);
}
return floats;
}
/*
* This method will fail if cannot parse properly
*/
public static double[] str2doubles(String str) {
String[] strs = str.split(",\\s*");
double[] doubles = new double[strs.length];
for (int i = 0; i < strs.length; i++) { doubles[i] = Double.parseDouble(strs[i]); } return doubles; } public float[] getFloats(String key, float[]... default_v) { float[] v = default_v.length > 0 ? default_v[0] : null;
return opt_s.containsKey(key) ? str2floats(opt_s.get(key)) : v;
}
public double[] getDoubles(String key, double[]... default_v) {
double[] v = default_v.length > 0 ? default_v[0] : null;
return opt_s.containsKey(key) ? str2doubles(opt_s.get(key)) : v;
}
public void print_validKeys() {
System.out.printf("Valid int keys: %s\n", validKeys_l);
System.out.printf("Valid float keys: %s\n", validKeys_f);
System.out.printf("Valid string keys: %s\n", validKeys_s);
}
private boolean validate_keys(List validKeys, Set keys, String description) {
Iterator it = keys.iterator();
boolean valid = true;
while (it.hasNext()) {
String key = it.next();
if (!key.startsWith("_") && !validKeys.contains(key)) { // keys start with _ are exempted from validation
System.out.printf("ERROR: Invalid %s key for text options: %s\n", description, key);
valid = false;
}
}
return valid;
}
public boolean validate_keys() {
boolean valid = true;
valid &= validate_keys(validKeys_l, opt_l.keySet(), "int"); // a = a && b will short circuit, which we do not want
valid &= validate_keys(validKeys_f, opt_f.keySet(), "float");
valid &= validate_keys(validKeys_f, opt_ff.keySet(), "float");
valid &= validate_keys(validKeys_s, opt_s.keySet(), "string");
if (!valid) {
print_validKeys();
}
return valid;
}
public void setValidStringKeys(List keys) {
validKeys_s = keys;
}
public void setValidFloatKeys(List keys) {
validKeys_f = keys;
}
public void setValidIntKeys(List keys) {
validKeys_l = keys;
}
}
C++ source:
Options.h:
#ifndef OPTIONS_H_
#define OPTIONS_H_
#include <map>
using std::string;
using std::map;
typedef map<string, int> map_i;
typedef map<string, long> map_l;
typedef map<string, float> map_f;
typedef map<string, double> map_ff;
typedef map<string, string> map_s;
class COptions {
public:
map_l opt_l;
map_f opt_f;
map_ff opt_ff;
map_s opt_s;
private:
int doublePrecision;
public:
COptions();
COptions(const char* text);
COptions(string str);
virtual ~COptions();
COptions& operator<<(const char* text);
COptions& operator<<(const string& str);
COptions& operator<<(std::istream& is); COptions& operator>>(std::ostream& os);
friend std::istream& operator>>(std::istream& is, COptions&);
friend std::ostream& operator<<(std::ostream& os, const COptions&);
void clear();
void reset();
virtual void init_defs(); // for overriding ...
string to_string();
// const char * c_str(); // NO NO!!!
int isDoublePrecision();
int geti(const char* key, int def = 0);
long getl(const char* key, long def = 0L);
float getf(const char* key, float def = 0.0f);
double getff(const char* key, double def = 0.0);
string gets(const char* key, string def = "");
vector getFloats(const char* key);
vector getFloats(const char* key, vector& def);
vector getDoubles(const char* key);
vector getDoubles(const char* key, vector& def);
int counti(const char* key);
int countl(const char* key);
int countf(const char* key);
int countff(const char* key);
int counts(const char* key);
void set(const char* key, int val);
void set(const char* key, long val);
void set(const char* key, float val);
void set(const char* key, double val);
void set(const char* key, vector vec);
void set(const char* key, vector vec);
void set(const char* key, string val);
void parse_line(string& line);
void chop_space(string& str);
static vector str2floats(string& str);
static vector str2doubles(string& str);
static string floats2str(vector& vec);
static string doubles2str(vector& vec);
static int test();
};
#endif /* OPTIONS_H_ */
Options.cpp:
#include "Options.h"
#include <sstream>
#include <iostream>
#include <iterator>
#include <iomanip>
#include <limits>
COptions::COptions(const char* text) {
init_defs();
*this << text;
}
COptions::COptions(string str) {
init_defs();
*this << str;
}
COptions::COptions() {
init_defs();
}
COptions::~COptions() {
}
void COptions::clear() {
opt_l.clear();
opt_f.clear();
opt_ff.clear();
opt_s.clear();
}
void COptions::reset() {
clear();
init_defs();
}
void COptions::init_defs() {
doublePrecision = 0;
}
COptions& COptions::operator <<(const char* text) {
if (!text) return *this;
std::stringstream ss(text);
return (*this << ss);
}
COptions& COptions::operator <<(const string& str) {
if (str.empty()) return *this;
std::stringstream ss(str);
return (*this << ss);
}
COptions& COptions::operator <<(std::istream& is) { string line; while (std::getline(is, line)) parse_line(line); return *this; } COptions& COptions::operator >>(std::ostream& os) {
os << *this; return *this; } std::istream& operator >>(std::istream& is, COptions& me) {
me << is;
return is;
}
std::ostream& operator <<(std::ostream& os, const COptions& me) {
os << "_double_precision = " << (me.doublePrecision ? 1 : 0) << std::endl;
os << "; =======Integer options=======" << std::endl;
for (map_l::const_iterator iter = me.opt_l.begin(); iter != me.opt_l.end(); iter++)
os << iter->first << " = " << iter->second << std::endl;
os << "; =======" << (me.doublePrecision ? "Double" : "Float") << " options=======" << std::endl; for (map_f::const_iterator iter = me.opt_f.begin(); iter != me.opt_f.end(); iter++) { float f = iter->second;
//if ((float) ((int) nearbyintf(f)) == f) os << iter->first << " = " << iter->second << ".0" << std::endl;
//else os << iter->first << " = " << iter->second << std::endl;
os << iter->first << " = " << std::scientific
<< std::setprecision(std::numeric_limits::digits10 + 1) // 7+1, same as %.8e, totally 9 digits
<< f << std::endl; } for (map_ff::const_iterator iter = me.opt_ff.begin(); iter != me.opt_ff.end(); iter++) { double f = iter->second;
//if ((double) ((int) nearbyint(f)) == f) os << iter->first << " = " << iter->second << ".0" << std::endl;
//else os << iter->first << " = " << iter->second << std::endl;
os << iter->first << " = " << std::scientific
<< std::setprecision(std::numeric_limits::digits10 + 1) // 15+1, same as %.16e, totally 17 digits
<< f << std::endl;
}
os << "; =======String options=======" << std::endl;
for (map_s::const_iterator iter = me.opt_s.begin(); iter != me.opt_s.end(); iter++)
os << iter->first << " = " << iter->second << std::endl;
return os;
}
void COptions::parse_line(string& line) {
if (line[0] == ';' || line[0] == '#') return; // comment line
size_t pos = line.find('=');
if (pos == string::npos) return;
string key = line.substr(0, pos);
string val = line.substr(pos + 1);
chop_space(key);
chop_space(val);
if (key.empty() || val.empty()) return;
// std::cout << "key = " << key << ", val = " << val << std::endl; if (val.find_first_not_of("+-0123456789") == string::npos) { if (key.compare("_double_precision") == 0) doublePrecision = atol(val.c_str()); else opt_l[key] = atol(val.c_str()); // integer } // floating point, not fool-proof but sufficient ... else if ((val.find_first_of("0123456789") != string::npos) && (val.find_first_not_of("+-.0123456789eE") == string::npos)) { if (doublePrecision) opt_ff[key] = atof(val.c_str()); else opt_f[key] = atof(val.c_str()); } else opt_s[key] = val; // string } void COptions::chop_space(string& str) { size_t pos; if ((pos = str.find_first_not_of(" \t\n\r")) > 0) str = str.substr(pos); // remove leading spaces
if ((pos = str.find_last_not_of(" \t\n\r")) < str.size() - 1) str = str.substr(0, pos + 1); // remove trailing spaces
}
string COptions::to_string() {
std::stringstream ss(std::stringstream::out);
ss << *this;
return ss.str();
}
/* should not do like this, to_string() will be free'ed and c_str() will be meaningless
const char* COptions::c_str() {
return to_string().c_str();
}
*/
int COptions::geti(const char* key, int def) {
return opt_l.count(key) ? opt_l[key] : def;
}
long COptions::getl(const char* key, long def) {
return opt_l.count(key) ? opt_l[key] : def;
}
float COptions::getf(const char* key, float def) {
return opt_f.count(key) ? opt_f[key] : opt_ff.count(key) ? opt_ff[key] : opt_l.count(key) ? opt_l[key] : def;
}
double COptions::getff(const char* key, double def) {
return opt_ff.count(key) ? opt_ff[key] : opt_f.count(key) ? opt_f[key] : opt_l.count(key) ? opt_l[key] : def;
}
string COptions::gets(const char* key, string def) {
return opt_s.count(key) ? opt_s[key] : def;
}
void COptions::set(const char* key, int val) {
opt_l[key] = val;
}
void COptions::set(const char* key, long val) {
opt_l[key] = val;
}
void COptions::set(const char* key, float val) {
opt_f[key] = val;
}
void COptions::set(const char* key, double val) {
opt_ff[key] = val;
}
void COptions::set(const char* key, string val) {
opt_s[key] = val;
}
int COptions::test() {
const char text[] = "_double_precision = 1\nint1= 2\n float2=0.0 \n string3 = TEST STRING \n float4 = 0.01";
COptions opt;
opt.clear();
opt << text;
std::cout << opt << std::endl;
string str = opt.to_string();
opt.reset();
opt << str;
std::cout << opt << std::endl;
COptions opt2(text);
std::cout << opt2 << std::endl;
return 0;
}
int COptions::isDoublePrecision() {
return doublePrecision;
}
vector COptions::getFloats(const char* key) {
if (opt_s.count(key)) return str2floats(opt_s[key]);
vector v;
if (countf(key)) v.push_back(getf(key)); // handles size=1
return v;
}
vector COptions::getFloats(const char* key, vector& def) {
if (opt_s.count(key)) return str2floats(opt_s[key]);
if (countf(key)) { // handles size=1
vector v;
v.push_back(getf(key));
return v;
}
return def;
}
vector COptions::getDoubles(const char* key) {
if (opt_s.count(key)) return str2doubles(opt_s[key]);
vector v;
if (countff(key)) v.push_back(getff(key)); // handles size=1
return v;
}
vector COptions::getDoubles(const char* key, vector& def) {
if (opt_s.count(key)) return str2doubles(opt_s[key]);
if (countff(key)) { // handles size=1
vector v;
v.push_back(getff(key));
return v;
}
return def;
}
int COptions::counti(const char* key) {
return opt_l.count(key);
}
int COptions::countl(const char* key) {
return opt_l.count(key);
}
int COptions::countf(const char* key) {
return opt_f.count(key) + opt_ff.count(key) + opt_l.count(key);
}
int COptions::countff(const char* key) {
return opt_ff.count(key) + opt_f.count(key) + opt_l.count(key);
}
int COptions::counts(const char* key) {
return opt_s.count(key);
}
vector COptions::str2floats(string& str) {
std::stringstream iss(str);
vector v;
float val;
char c;
while (iss >> val) {
v.push_back(val);
while ((c = iss.peek()) == ',' || c == ' ')
iss.get();
}
return v;
}
vector COptions::str2doubles(string& str) {
std::stringstream iss(str);
vector v;
double val;
char c;
while (iss >> val) {
v.push_back(val);
while ((c = iss.peek()) == ',' || c == ' ')
iss.get();
}
return v;
}
string COptions::floats2str(vector& vec) {
std::ostringstream ss;
ss << std::scientific << std::setprecision(std::numeric_limits::digits10 + 1) // 7+1, same as %.8e, totally 9 digits
<< vec[0];
for (size_t i = 1; i < vec.size(); i++) {
ss << ", " << std::scientific << std::setprecision(std::numeric_limits::digits10 + 1) // 7+1, same as %.8e, totally 9 digits
<< vec[i];
}
return ss.str();
}
string COptions::doubles2str(vector& vec) {
std::ostringstream ss;
ss << std::scientific << std::setprecision(std::numeric_limits::digits10 + 1) // 15+1, same as %.16e, totally 17 digits
<< vec[0];
for (size_t i = 1; i < vec.size(); i++) {
ss << ", " << std::scientific << std::setprecision(std::numeric_limits::digits10 + 1) // 15+1, same as %.16e, totally 17 digits
<< vec[i];
}
return ss.str();
}
void COptions::set(const char* key, vector vec) {
set(key, floats2str(vec));
}
void COptions::set(const char* key, vector vec) {
set(key, doubles2str(vec));
}