Boost property tree seems like an excellent library to use for parsing config files. However, I can't figure out how to handle situations where there are multiple values per key. For example, let's say I was specifying a box like this:
box
{
x -1 1
y -1 1
z -1 1
}
where x, y, and z are the bounds of the box on the x, y, and z axes respectively, specified using property_tree's INFO format. I see mention in the manual of using quotes for values that use spaces, but then I don't see that I could import those values as numbers. I'd have to parse the string into numbers, which seems to defeat the purpose of using property_tree in the first place. I could certainly give each number a key:
box
{
xlo -1
xhi 1
ylo -1
yhi 1
zlo -1
zhi 1
}
but that seems cumbersome, and will inflate my config file. I also noted that I could handle this situation in program_options, but I lose the nested config file capabilities (yeah, I know I can use dot notation to "nest", but it's not the same).
Is there a way to import e.g. x as a list of numbers like this?
The standard property_tree handles only one string value per key since it is defined as:
typedef basic_ptree<std::string, std::string> ptree;
So, the only option is to use strings and parse them. I think the best method is to define a new class that stores the low and high values and then create a translator class for the get and set methods. For example:
struct low_high_value
{
low_high_value() : m_low(0), m_high(0) { }
low_high_value(double low, double high) : m_low(low), m_high(high) { }
double m_low;
double m_high;
};
The translator would be:
struct low_high_value_translator
{
typedef std::string internal_type;
typedef low_high_value external_type;
// Get a low_high_value from a string
boost::optional<external_type> get_value(const internal_type& str)
{
if (!str.empty())
{
low_high_value val;
std::stringstream s(str);
s >> val.m_high >> val.m_low;
return boost::optional<external_type>(val);
}
else
return boost::optional<external_type>(boost::none);
}
// Create a string from a low_high_value
boost::optional<internal_type> put_value(const external_type& b)
{
std::stringstream ss;
ss << b.m_low << " " << b.m_high;
return boost::optional<internal_type>(ss.str());
}
};
The previous get_value method is very simple. It should be improved if the file could be written by the user.
This class should be registered using:
namespace boost { namespace property_tree
{
template<typename Ch, typename Traits, typename Alloc>
struct translator_between<std::basic_string< Ch, Traits, Alloc >, low_high_value>
{
typedef low_high_value_translator type;
};
} }
After you include the previous code, you can use property_tree as:
pt.get<low_high_value>("box.x")
pt.put("box.u", low_high_value(-110, 200));
Related
I've read the Boost.Locale docs several times through, but still can't find the answer to this seemingly easy problem: I need to output using a specific locale (e.g. ru_RU), but with a customized decimal separator (e.g. dot instead of comma). Is this possible?
For dates, there's the "ftime" manipulator that allows to specify a custom datetime format string. But is there anything like that for numbers?
Thanks!
You can use the C++ <locale> library.
std::string russian_number_string(int n) {
std::stringstream ss;
ss.imbue(std::locale("ru_RU"));
ss << n;
return ss.str();
}
For the sake of completeness, I'll post how I've solved the problem.
Say we have a locale object and need to use a custom decimal point character sep:
template <class charT>
class DecimalPointFacet : public std::numpunct<charT> {
charT _sep;
public:
explicit DecimalPointFacet(charT sep): _sep(sep) {}
protected:
[[nodiscard]] charT do_decimal_point() const override
{
return _sep;
}
[[nodiscard]] typename std::numpunct<charT>::string_type do_grouping() const override
{
return "\0";
}
};
// ...
std::locale locale = obtainLocale();
const char sep = obtainDecimalSeparator();
locale = std::locale(locale, new DecimalPointFacet<char>(sep);
std::cout.imbue(locale);
std::cout << someNumber;
Note also that the DecimalPointFacet turns off grouping of digits which was also handy for me(if you don't need that, remove the do_grouping override).
I am new to redis. I want to write a simple library (for testing) that is on top level of the hiredis.
for example to implement SET command I wrote the code bellow:
#include<iostream>
#include<type_traits>
#include<hiredis.h>
#include<string>
using namespace std;
template<typename T>
string set(string key, T value)
{
/* set a key */
if(is_same<T, int>::value)
{
reply = (redisReply*) redisCommand(c, "SET %s %d", key, value) // c is redisContext*
}
else if(is_same<T, string>::value)
{
reply = (redisReply*) redisCommand(c, "SET %s %s", key, value)
}
// and so on for other data types ...
string replyStr = reply->str;
freeReplyObject(reply);
return replyStr;
}
is there any better solution to handle different data types as Value for SET command? (I mean avoiding using If statements for each datatype ).
regards.
If I understand correctly, you only need to know the type of value in order to know what type you are inserting in your redisCommand string as your write your reply.
If you are restricting these types to basic types try calling to_string on value in order to build a result std::string
for more information https://en.cppreference.com/w/cpp/string/basic_string/to_string
don't forget the include of course!
something like this :
template<typename T>
string set(string key, T value)
{
std::string result(std::string("SET ") + to_string(key) + to_string(value));
reply = (redisReply*) redisCommand(c, result);
string replyStr = reply->str;
freeReplyObject(reply);
return replyStr;
}
EDIT : Another viable solution would be to simply cast your variable each time you call 'set' and simply rewrite your function as string set(string key, string value).
I'm dealing with a cryptocurrency RPC and receiving json data as follows:
{
...
"amount": 1.34000000,
"confirmations": 230016,
"spendable": true,
"solvable": true
...
}
Using Jsoncpp library or json11 gets the number parsed to a double. When this happens, the result is: 1.3400000000000001, due to double accuracy problems. In general this is catastrophic to financial transations and unacceptable.
I already have a fixed-point library that can take a valid string and treat it as an integer internally. Is there a way I could make Jsoncpp (or any other json library) take selected numeric json values as strings, so that I could treat them the right way with fixed-precision?
There doesn't seem to be a solution in json libraries, so I had to modify the number myself and wrap it with quotes. I applied this function to the responses to do that.
[](std::string& jsonStr) {
// matches "amount" field in json
static std::regex reg(R"((\s*\"amount\"\s*:)\s*(\d*\.{0,1}\d{0,8})\s*)");
jsonStr = std::regex_replace(jsonStr, reg, "$1\"$2\"");
};
And now it works properly.
I like ThorsSerializer. Disclaimer I wrote it.
It supports what you are looking for.
You can tell the parser to use the standard input/output operators for a class (which you can then define for yourself).
Example:
#include "ThorSerialize/JsonThor.h"
#include "ThorSerialize/SerUtil.h"
#include <sstream>
#include <iostream>
#include <string>
#include <map>
struct FixedPoint
{
int integerPart;
int floatPart;
friend std::istream& operator>>(std::istream& stream, FixedPoint& data)
{
// This code assumes <number>.<number>
// Change to suite your needs.
char c;
stream >> data.integerPart >> c >> data.floatPart;
if (c != '.')
{
stream.setstate(std::ios::failbit);
}
return stream;
}
};
// This declaration tells the serializer to use operator>> for reading
// and operator<< for writing this value.
// Note: The value must still conform to standard Json type
// true/false/null/integer/real/quoted string
ThorsAnvil_MakeTraitCustom(FixedPoint);
struct BitCoin
{
FixedPoint amount;
int confirmations;
bool spendable;
bool solvable;
};
// This declaration tells the serializer to use the standard
// built in operators for a struct and serialize the listed members.
// There are built in operations for all built in types and std::Types
ThorsAnvil_MakeTrait(BitCoin, amount, confirmations, spendable, solvable);
Example usage:
int main()
{
using ThorsAnvil::Serialize::jsonImport;
using ThorsAnvil::Serialize::jsonExport;
std::stringstream file(R"(
{
"amount": 1.34000000,
"confirmations": 230016,
"spendable": true,
"solvable": true
}
)");
BitCoin coin;
file >> jsonImport(coin);
std::cout << coin.amount.integerPart << " . " << coin.amount.floatPart << "\n";
}
Build:
> g++ -std=c++1z 51087868.cpp -lThorSerialize17
The native jsoncpp solution is to RTFM!!! (e.g., here: https://open-source-parsers.github.io/jsoncpp-docs/doxygen/class_json_1_1_stream_writer_builder.html)
Json::StreamWriterBuilder builder;
builder["commentStyle"] = "None";
builder["indentation"] = " ";
builder["precision"] = 15;
That'll set your writer float precision to avoid printing the small truncation errors in the double representations. e.g., instead of a json field,
"amount": 1.3400000000000001,
you will now get
"amount": 1.340000000000000,
as desired.
I played a little with code at link and I have another question. I added semantic action to:
action = actions_ >> '(' >> parameters >> ')'[ /* semantic action placed here */];
so I can reuse the rule together with verification at multiple places. The problem is that then the spirit stops propagate my attribute type to the upper rules (which uses the action as parser). I read at link that operator %= should be used to enable it again (to have semantic actions and attribute propagation). But then I am getting compiler error that it is not possible to convert boost::fuction::vector2<ast::actionid, ast::parameters> to ast::action. Is there any macro in fusion to enable assignment in another direction? Or what should I do that the rule still exposes the same attribute as it is passed to the semantic action instead of having fusion vector there?
Sample code:
#include "stdafx.h"
// boost
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/repository/include/qi_iter_pos.hpp>
#include <boost/bind.hpp>
#include <boost/phoenix.hpp>
// std
#include <string>
#include <vector>
namespace bsqi = boost::spirit::qi;
namespace bsqi_coding = boost::spirit::standard_wide;
namespace bsqi_repos = boost::spirit::repository::qi;
//////////////////////////////////////////////////////////////////////////
enum class Action : uint8_t
{
eAction0 = 0,
eAction1,
eAction2
};
//////////////////////////////////////////////////////////////////////////
struct ActionSymbols : public boost::spirit::qi::symbols<wchar_t, Action>
{
ActionSymbols()
{
add
(L"action0", Action::eAction0)
(L"action1", Action::eAction1)
(L"action2", Action::eAction2)
;
}
} actionSymbols;
//////////////////////////////////////////////////////////////////////////
using ParameterValue = boost::variant<int, std::wstring>;
struct Parameter
{
std::wstring::const_iterator source; ///< position within the input where parameter begins
ParameterValue value; ///< type and value of the parameter
};
//////////////////////////////////////////////////////////////////////////
using Parameters = std::vector<Parameter>;
//////////////////////////////////////////////////////////////////////////
struct ActionParameters
{
Action action;
Parameters parameters;
};
//////////////////////////////////////////////////////////////////////////
BOOST_FUSION_ADAPT_STRUCT(Parameter, (std::wstring::const_iterator, source), (ParameterValue, value));
BOOST_FUSION_ADAPT_STRUCT(ActionParameters, (Action, action), (Parameters, parameters));
//////////////////////////////////////////////////////////////////////////
class SyntaxError : public std::runtime_error
{
public:
SyntaxError()
: std::runtime_error("Syntax error!")
{ }
};
//////////////////////////////////////////////////////////////////////////
template<typename IteratorT>
struct ScriptGrammar : bsqi::grammar<IteratorT, std::vector<ActionParameters>, bsqi_coding::space_type>
{
/// helper type to define all rules
template<typename T>
using RuleT = bsqi::rule<iterator_type, T, bsqi_coding::space_type>;
using result_type = std::vector<ActionParameters>;
explicit ScriptGrammar()
: base_type(start, "script")
{
// supported parameter types (int or quoted strings)
// note: iter_pos is used for saving the iterator for the parameter to enable generating more detailed error reports
parameter = bsqi_repos::iter_pos >> (bsqi::int_ | bsqi::lexeme[L'"' > *(bsqi_coding::char_ - L'"') > L'"']);
parameter.name("parameter");
// comma separator list of parameters (or no parameters)
parameters = -(parameter % L',');
parameters.name("parameters");
// action with parameters
action = (actionSymbols > L'(' > parameters > L')')[bsqi::_pass = boost::phoenix::bind(&ScriptGrammar::ValidateAction, this, bsqi::_1, bsqi::_2)];
action.name("action");
// action(..) [-> event(..) -> event(..) -> ..]
// eps = force to use this rule for parsing
// eoi = the rule must consume whole input
start = bsqi::eps > (action % L';') > L';' > bsqi::eoi;
}
private:
bool ValidateAction(Action action, const Parameters& parameters)
{
return true;
}
RuleT<Parameter> parameter;
RuleT<Parameters> parameters;
RuleT<ActionParameters> action;
RuleT<std::vector<ActionParameters>> start;
};
//////////////////////////////////////////////////////////////////////////
int _tmain(int argc, _TCHAR* argv[])
{
using ScriptParser = ScriptGrammar<std::wstring::const_iterator>;
ScriptParser parser;
auto input = std::wstring(L"\taction0(1, 2, 3); action1(\"str1\", \"str2\"); action2(\"strmix\", 0);\t\t");
auto it = input.begin();
ScriptParser::result_type output;
try
{
if(!phrase_parse(it, input.end(), parser, bsqi_coding::space, output))
throw SyntaxError();
}
catch(bsqi::expectation_failure<ScriptParser::iterator_type>& e)
{
std::cout << "Error! Expecting " << e.what_ << " here: \"" << std::string(e.first, e.last) << "\"";
}
catch(SyntaxError& e)
{
std::cout << e.what() << "\n";
}
return 0;
}
I try to get an attribute from start rule. I get correctly parsed values in my semantic action (ValidateAction) but the attribute from start rule receive only uninitialized values (size of the output std::vector is 3 but values are uninitialized). I tried to replace the initialization of the rules with %= instead of simple = but then the mentioned compilation error pops.
There is BOOST_SPIRIT_ACTIONS_ALLOW_ATTR_COMPAT which is supposed to allow attribute compatibility rules to work inside semantic actions like they work during automation attribute propagation.
However, the superior solution is to specify the conversions you wish, when you wish them.
The most obvious approaches are
wrap the intermediate into a qi::rule<..., T()>
Incidentally, I already solved your particular issue that way here boost spirit reporting semantic error in your previous question.
Actually, I suppose you would like to have a stateful validator working on the fly, and you can use Attribute Traits to transform your intermediates to the desired AST (e.g. if you don't want to actually store the iterators in your AST)
wrap the sub-expression in a qi::transform_attribute<T>()[p] directive.
Beware of a bug in some versions of Boost Spirit that requires you to explicitly deep-copy the subexpression in transform_attribute (use qi::copy(p))
I have a map, where string representing the name of attribute and the second value representing the type, which this attribute should have.
map.insert(make_pair("X", iDatatype::iUint));
map.insert(make_pair("Y", iDatatype::iUint));
map.insert(make_pair("RADIANT", iDatatype::iFloat));
where iDatatype is just enumeration of all possible types.
typedef enum
{
iUnknown = 0,
iByte = 1,
iChar = 2,
iUChar = 3,
iShort = 4.....
} iDatatype;
If the program gets the command to create, for example, "RADIANT" than it look at the map, find the iDatatype value (iter->second) and go to switch case.
switch (iter->second) {
case iDatatype::iUint:
uint value = ......
// You gotta do what you gonna do
break;
} .......
In Switch case, the function, which depends on type of attribute, will be called.
This code works. But I am not sure, if it the best solution to map string with the types.
And the problem that I don't know what should I look for? Could you recommend what methods or techniques are commonly used for such purpose? Thank you a lot.
Unless you need the map for some other reference, another approach will be:
if(command == "X" || command == "Y") // make sure command is std::string
// or use strcmp
{
// create uint
}
else if(command == "RADIANT")
{
// create float
}
However I'm not sure how much faster this will be than using a map, because a map uses binary search while this uses iterative search.
If you want to gain the boost of binary search while no need for another enum you can use a map of functions:
std::map<std::string, std::function<void()> map;
map.insert(make_pair("X", &create_uint));
map.insert(make_pair("Y", &create_uint));
map.insert(make_pair("RADIANT", &create_float));
and later call it like:
std::string key = "X";
map[key]();
you can also pass parameters to it like:
void create_uint(std::string param) { /* ... */ }
std::map<std::string, std::function<void(std::string)> map;
map.insert(make_pair("X", &create_uint));
std::string key = "X";
std::string param = "XYZ";
map[key](param);