How create a JSON-file from my data? (with С++, boost json) - c++

I wanted to create a Json-file that will be created from the data received earlier. I don't understand how to work with Json files at all. I want to use the Boost library, because I am using it in another part of this program. I need to create a Json-file with a specific structure which I have attached below.
I need to get JSON:
{
"track": {
"Wheels": {
"Wheel": [
{
"start_pos": "10",
"end_pos": "25"
},
{
"start_pos": "22",
"end_pos": "78"
}
]
},
"Brakes": {
"Brake": [
{
"start_pos": "10",
"midl_pos": "25"
}
]
}
}
}
C++:
#include "boost/property_tree/ptree.hpp"
#include "boost/property_tree/json_parser.hpp"
#include <string>
using namespace std;
using boost::property_tree::ptree;
struct wheel
{
string start_pos;
string end_pos;
};
struct brake
{
string start_pos;
string midl_pos;
};
int main()
{
string tr = "track";
string ws = "Wheels";
string bs = "Brakes";
struct wheel w1;
w1.start_pos = "10";
w1.end_pos = "25";
struct wheel w2;
w2.start_pos = "22";
w2.end_pos = "78";
struct brake b1;
b1.start_pos = "10";
b1.midl_pos = "25";
return 0;
}

Implementing it with the Boost JSON customization points.
Doing it test-driven:
Live On Coliru
#include "boost/json/src.hpp" // header-only approach
#include <iostream>
namespace json = boost::json;
using json::value_from;
using json::value_to;
static const json::value expected = json::parse(R"({
"track": {
"Wheels": {
"Wheel": [
{
"start_pos": "10",
"end_pos": "25"
},
{
"start_pos": "22",
"end_pos": "78"
}
]
},
"Brakes": {
"Brake": [
{
"start_pos": "10",
"midl_pos": "25"
}
]
}
}
})");
namespace MyLib {
struct wheel { int start_pos, end_pos; };
struct brake { int start_pos, midl_pos; };
struct track {
std::vector<wheel> wheels;
std::vector<brake> brakes;
};
void tag_invoke(json::value_from_tag, json::value& jv, wheel const& w) {
jv = {
{"start_pos", std::to_string(w.start_pos)},
{"end_pos", std::to_string(w.end_pos)},
};
}
void tag_invoke(json::value_from_tag, json::value& jv, brake const& b) {
jv = {
{"start_pos", std::to_string(b.start_pos)},
{"midl_pos", std::to_string(b.midl_pos)},
};
}
void tag_invoke(json::value_from_tag, json::value& jv, track const& t) {
jv = {{"track",
{
{"Wheels", {{"Wheel", t.wheels}}},
{"Brakes", {{"Brake", t.brakes}}},
}}};
}
}
int main()
{
MyLib::track track{{
{10, 25},
{22, 78},
},
{
{10, 25},
}};
json::value output = json::value_from(track);
std::cout << output << "\n";
std::cout << expected << "\n";
std::cout << "matching: " << std::boolalpha << (output == expected) << "\n";
}
Prints
{"track":{"Wheels":{"Wheel":[{"start_pos":"10","end_pos":"25"},{"start_pos":"22","end_pos":"78"}]},"Brakes":{"Brake":[{"start_pos":"10","midl_pos":"25"}]}}}
{"track":{"Wheels":{"Wheel":[{"start_pos":"10","end_pos":"25"},{"start_pos":"22","end_pos":"78"}]},"Brakes":{"Brake":[{"start_pos":"10","midl_pos":"25"}]}}}
matching: true
BONUS
Adding full round-trip support. I elected to remove the to_string as it looks like it may have been a requirement from the Boost Property Tree limitations only:
Live On Coliru
#include "boost/json/src.hpp" // header-only approach
#include <iostream>
namespace json = boost::json;
using json::value_from;
using json::value_to;
namespace MyLib {
struct wheel {
int start_pos, end_pos;
bool operator==(wheel const&) const = default;
};
struct brake {
int start_pos, midl_pos;
bool operator==(brake const&) const = default;
};
struct track {
std::vector<wheel> wheels;
std::vector<brake> brakes;
bool operator==(track const&) const = default;
};
void tag_invoke(json::value_from_tag, json::value& jv, wheel const& w) {
jv = {{"start_pos", w.start_pos}, {"end_pos", w.end_pos}};
}
void tag_invoke(json::value_from_tag, json::value& jv, brake const& b) {
jv = {{"start_pos", b.start_pos}, {"midl_pos", b.midl_pos}};
}
void tag_invoke(json::value_from_tag, json::value& jv, track const& t) {
jv = {{"track",
{
{"Wheels", {{"Wheel", t.wheels}}},
{"Brakes", {{"Brake", t.brakes}}},
}}};
}
wheel tag_invoke(json::value_to_tag<wheel>, json::value const& jv) {
return {
value_to<int>(jv.at("start_pos")),
value_to<int>(jv.at("end_pos")),
};
}
brake tag_invoke(json::value_to_tag<brake>, json::value const& jv) {
return {
value_to<int>(jv.at("start_pos")),
value_to<int>(jv.at("midl_pos")),
};
}
track tag_invoke(json::value_to_tag<track>, json::value const& jv) {
auto& track = jv.at("track");
return {
value_to<decltype(track::wheels)>(track.at("Wheels").at("Wheel")),
value_to<decltype(track::brakes)>(track.at("Brakes").at("Brake")),
};
}
}
int main()
{
MyLib::track const track{{
{110, 125},
{111, 126},
{142, 198},
},
{
{10, 25},
{120, 135},
}};
json::value output = json::value_from(track);
std::cout << output << "\n";
std::cout << "Roundtrip: " << std::boolalpha
<< (value_to<MyLib::track>(output) == track) << "\n";
}
Prints
{"track":{"Wheels":{"Wheel":[{"start_pos":110,"end_pos":125},{"start_pos":111,"end_pos":126},{"start_pos":142,"end_pos":198}]},"Brakes":{"Brake":[{"start_pos":10,"midl_pos":25},{"start_pos":120,"midl_pos":135}]}}}
Roundtrip: true

Despite comments suggesting to use a different library, which they could, I think #Nindzzya wanted to specifically use the boost library.
Using the boost library from How to use boost::property_tree to load and write JSON:
#include "boost/property_tree/ptree.hpp"
#include "boost/property_tree/json_parser.hpp"
#include <string>
#include <iostream>
using namespace std;
namespace pt = boost::property_tree;
struct wheel
{
string start_pos;
string end_pos;
};
struct brake
{
string start_pos;
string midl_pos;
};
int main()
{
string tr = "track";
string ws = "Wheels";
string bs = "Brakes";
struct wheel w1;
w1.start_pos = "10";
w1.end_pos = "25";
struct wheel w2;
w2.start_pos = "22";
w2.end_pos = "78";
struct brake b1;
b1.start_pos = "10";
b1.midl_pos = "25";
pt::ptree wheel1, wheel2, wheels, wheel;
pt::ptree brake1, brakes, brake;
pt::ptree track, root;
wheel1.put("start_pos", w1.start_pos);
wheel1.put("end_pos", w1.end_pos);
wheel2.put("start_pos", w2.start_pos);
wheel2.put("end_pos", w2.end_pos);
wheels.push_back(make_pair("", wheel1));
wheels.push_back(make_pair("", wheel2));
wheel.add_child("Wheel", wheels);
track.add_child(ws, wheel);
brake1.put("start_pos", b1.start_pos);
brake1.put("midl_pos", b1.midl_pos);
brakes.push_back(make_pair("", brake1));
brake.add_child("Brake", brakes);
track.add_child(bs, brake);
root.add_child(tr, track);
pt::write_json(std::cout, root);
return 0;
}
results in:
{
"track": {
"Wheels": {
"Wheel": [
{
"start_pos": "10",
"end_pos": "25"
},
{
"start_pos": "22",
"end_pos": "78"
}
]
},
"Brakes": {
"Brake": [
{
"start_pos": "10",
"midl_pos": "25"
}
]
}
}
}

Related

nlohmann::json array of objects conversion into memory structure

I am using the nlohmann JSON C++ library to read a JSON file with this structure:
{
"Main": [
{
"obj1": "bar"
},
{
"obj2": "foo"
}
]
}
The main object with key known to the user "Main" contains an array of objects with unknown key names.
I want to transfer the JSON object in the following program to a C++ structure. How could this be done?
#include <nlohmann/json.hpp>
#include <iostream>
#include <fstream>
using json = nlohmann::json;
int main()
{
std::ifstream ifs("../test/test_2.json");
json js = json::parse(ifs);
for (json::iterator it = js.begin(); it != js.end(); ++it)
{
std::cout << it.key() << " :\n";
std::cout << it.value() << "\n";
}
if (js.contains("Main"))
{
json a = js["Main"];
for (size_t idx = 0; idx < a.size(); ++idx)
{
json o = a.at(idx);
std::cout << o << "\n";
}
}
return 0;
}
Output:
Main :
[{"obj1":"bar"},{"obj2":"foo"}]
{"obj1":"bar"}
{"obj2":"foo"}
You could parse the vector of maps under Main with:
auto objects{ j.at("Main").get<objects_t>() };
Where:
using object_t = std::map<std::string, std::string>;
using objects_t = std::vector<object_t>;
[Demo]
#include <fmt/ranges.h>
#include <iostream> // cout
#include <map>
#include <nlohmann/json.hpp>
#include <string>
#include <vector>
using json = nlohmann::json;
using object_t = std::map<std::string, std::string>;
using objects_t = std::vector<object_t>;
int main() {
std::string str{R"({ "Main": [ { "obj1": "bar" }, { "obj2": "foo" } ] })"};
json j = json::parse(str);
auto objects{ j.at("Main").get<objects_t>() };
fmt::print("{}", objects);
}
// Outputs:
//
// [{"obj1": "bar"}, {"obj2": "foo"}]
Yes, but the API gives an automatic way to do this by means of NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE;
it seems it would be like just defining a vector like this, but this program gives the error
[json.exception.out_of_range.403] key 'key' not found
program
#include <nlohmann/json.hpp>
#include <iostream>
#include <fstream>
using json = nlohmann::json;
struct key_value_t
{
std::string key;
std::string value;
};
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(key_value_t, key, value);
int main()
{
try
{
std::string str{ R"({ "Main": [ { "obj1": "bar" }, { "obj2": "foo" } ] })" };
json js = json::parse(str);
std::vector<key_value_t> kv;
if (js.contains("Main"))
{
js.at("Main").get_to(kv);
}
}
catch (std::exception& e)
{
std::cout << e.what() << '\n';
}
return 0;
}
This is one way to read and write the JSON in the original format, versions of the functions from_json() and to_json() are needed:
#include <iostream>
#include <fstream>
#include <map>
#include <nlohmann/json.hpp>
#include <string>
#include <vector>
using json = nlohmann::json;
struct key_value_t
{
std::string key;
std::string value;
};
void from_json(const json& j, key_value_t& kv)
{
assert(j.is_object());
assert(j.size() == 1);
kv.key = j.begin().key();
kv.value = j.begin().value();
}
void to_json(json& j, const key_value_t& kv)
{
j = nlohmann::json{
{kv.key, kv.value}
};
}
int main()
{
try
{
std::string str{ R"({ "Main": [ { "obj1": "bar" }, { "obj2": "foo" } ] })" };
json js_in = json::parse(str);
std::vector<key_value_t> kv_in;
//get input object
js_in.at("Main").get_to(kv_in);
if (js_in.contains("Main"))
{
nlohmann::json js_out = nlohmann::json::object_t();
std::vector<key_value_t> kv_;
key_value_t kv1{ "obj1", "1" };
key_value_t kv2{ "obj2", "2" };
kv_.push_back(kv1);
kv_.push_back(kv2);
nlohmann::json js_arr = kv_;
//add to main JSON object
js_out += {"Main", js_arr};
//save output
std::ofstream ofs("output.json");
ofs << std::setw(2) << js_out << std::endl;
ofs.close();
}
}
catch (std::exception& e)
{
std::cout << e.what() << '\n';
}
return 0;
}
Output is:
{
"Main": [
{
"obj1": "1"
},
{
"obj2": "2"
}
]
}

Creating std::set copies only one element, how to fix this?

v_map has the correct amount of information stored, however when i try to use std::set it only copies one element ,I assume the first one. This is my first time using std::set , maybe I miss something here...Thanks for your help !
typedef std::map<std::string,std::pair<int,int>> points_map;
void list_average(points_map &v_map)
{
Comparator compFunctor = [](std::pair<std::string,std::pair<int,int>> elem1,std::pair<std::string,std::pair<int,int>> elem2)
{
std::pair<int,int> it = elem1.second;
std::pair<int,int> jt = elem2.second;
return it.first < jt.first;
};
std::set<std::pair<std::string,std::pair<int,int>>,Comparator> v_set(v_map.begin(),v_map.end(),compFunctor);
for (std::pair<std::string,std::pair<int,int>> it : v_set)
{
std::pair<int,int> jt = it.second;
std::cout << it.first << " " << (jt.second - jt.first) / jt.first<< std::endl;
}
}
Note the following is the full program, I apologize in advance for the ugly code , and length of the code ,also I rewrote the name in the upper part of my code, in the full code , this particular function is called list_atlag
#include <iostream>
#include <string>
#include <map>
#include <set>
#include <vector>
#include <codecvt>
#include <iterator>
#include <numeric>
#include <functional>
#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>
#include <boost/program_options.hpp>
#include <boost/tokenizer.hpp>
class Adatok
{
public:
Adatok(std::string name, std::string path, std::string date, int points) : _name(name), _path(path), _date(date), _points(points) {}
Adatok(const Adatok &other) = default;
Adatok &operator=(const Adatok &other) = default;
std::string get_name() { return _name; }
std::string get_path() { return _path; }
std::string get_date() { return _date; }
int get_points() { return _points; }
private:
std::string _name;
std::string _path;
std::string _date;
int _points;
};
class Ranglista
{
public:
Ranglista(std::string name, int points) : _name(name), _points(points) {}
Ranglista(const Ranglista &other) = default;
Ranglista &operator=(const Ranglista &other) = default;
std::string get_name() { return _name; }
int get_points() { return _points; }
bool operator<(const Ranglista &other)
{
return _points > other._points;
}
private:
std::string _name;
int _points;
};
class Vedes
{
public:
Vedes(std::string name, int point) : _name(name), _point(point) { _count++; }
Vedes(const Vedes &other) = default;
Vedes &operator=(const Vedes &other) = default;
std::string get_name() { return _name; }
int get_point() { return _point; }
int get_count() { return _count; }
void set_stuff(int &points)
{
_point += points;
_count++;
}
bool operator<(const Vedes &other)
{
return _count > other._count;
}
private:
std::string _name;
int _point;
int _count = 0;
};
typedef std::map<std::string, int> path_value; //minden path + az erteke
typedef std::vector<Adatok> name_path_date; //bejegyzesek
typedef std::vector<Ranglista> ranglista; //ranglista
typedef std::map<std::string,std::pair<int,int>> vedes_vec; //vedesek
typedef std::function<bool(std::pair<std::string,std::pair<int,int>>,std::pair<std::string,std::pair<int,int>>)> Comparator;
void create_pv(path_value &, boost::filesystem::path); //feltolti a path+ertek map-ot
void create_npd(name_path_date &, path_value &, std::string input); //feltolti a bejegyzesek vektorat + mindenki pontszama map
void create_np(name_path_date &, path_value &); // name + path map
void list_np(path_value &name_point); // nam + path kiiratas
void list_bejegyzesek(name_path_date &bejegyzesek); // bejegyzesek vektora kiiratas
bool check_bejegyzesek(name_path_date &bejegyzesek, std::string name, std::string path); //van-e mar ilyen bejegyzes
void create_rl(ranglista &rl_vec, path_value &name_point); //ranglista feltoltes
void list_rl(ranglista &rl_vec); //ranglista kiiratas
void vedes_atlag(name_path_date &bejegyzesek, vedes_vec &v_vec); //vedes atlag map
void list_atlag(vedes_vec &v_vec); //vedes atlag kiiratas
bool check_vedes(vedes_vec &v_vec, std::string name);
void vedes_elem(vedes_vec &v_vec, std::string name, int &&points); //
//void accumulate_pv(path_value&);
int main(int argc, char **argv)
{
std::vector<std::string> roots = {"City/Debrecen/Oktatás/Informatika/Programozás/DEIK/Prog1/", "City/Debrecen/Oktatás/Informatika/Programozás/DEIK/"};
std::string input_file_name = "db-2018-05-06.csv";
/* OPTIONS */
boost::program_options::options_description desc("ALLOWED OPTIONS");
desc.add_options()("help", "help msg")("root,r", boost::program_options::value<std::vector<std::string>>())("csv", boost::program_options::value<std::string>(), "comma separated values")("rank", "rang lista")("vedes", "labor vedesek");
boost::program_options::positional_options_description pdesc;
pdesc.add("root", -1);
boost::program_options::variables_map vm;
boost::program_options::store(boost::program_options::command_line_parser(argc, argv).options(desc).positional(pdesc).run(), vm);
boost::program_options::notify(vm);
int sum = 0;
path_value pv_map;
if (vm.count("help") || argc == 1)
{
std::cout << desc << std::endl;
return 1;
}
if (vm.count("root"))
{
roots = vm["root"].as<std::vector<std::string>>();
for (auto &i : roots)
{
boost::filesystem::path path(i);
create_pv(pv_map, path);
}
for (path_value::iterator it{pv_map.begin()}; it != pv_map.end(); it++)
sum += it->second;
//std::cout << sum << std::endl;create_npd
std::cout << std::accumulate(pv_map.begin(), pv_map.end(), 0, [](int value, const std::map<std::string, int>::value_type &p) { return value + p.second; });
std::cout << std::endl;
}
if (vm.count("csv"))
{
//input_file_name = vm["csv"].as<std::string>();
std::ifstream input_file{vm["csv"].as<std::string>()};
name_path_date bejegyzesek;
std::string temp;
path_value name_point;
while (getline(input_file, temp))
create_npd(bejegyzesek, pv_map, temp);
create_np(bejegyzesek, name_point);
//list_bejegyzesek(bejegyzesek);
//list_np(name_point);
if (vm.count("rank"))
{
ranglista rl_vec;
create_rl(rl_vec, name_point);
list_rl(rl_vec);
}
if (vm.count("vedes"))
{
vedes_vec v_vec;
vedes_atlag(bejegyzesek, v_vec);
list_atlag(v_vec);
}
return 0;
}
return 0;
}
void create_pv(path_value &pv_map, boost::filesystem::path path)
{
boost::filesystem::directory_iterator it{path}, eod;
BOOST_FOREACH (boost::filesystem::path const &p, std::make_pair(it, eod))
{
if (boost::filesystem::is_regular_file(p))
{
boost::filesystem::ifstream regular_file{p};
std::string temp;
int sum = 0; //aktualis .props erteke
while (getline(regular_file, temp))
{
temp.erase(0, temp.find_last_of('/'));
temp.erase(0, temp.find_first_of(' '));
sum += std::atoi((temp.substr(temp.find_first_of("0123456789"), temp.find_last_of("0123456789"))).c_str());
}
std::string result = p.string();
std::string result_path = result.substr(0, result.find_last_of('/'));
//std::cout << result_path << std::endl;
//pv_map.insert(std::make_pair(result, sum));
pv_map[result_path] = sum;
}
else
create_pv(pv_map, p);
}
}
//void accumulate_pv(path_value& pv_map)
//{
// std::cout<<std::accumulate(pv_map.begin(),pv_map.end(),0,[](int value,const path_value::int& p){return value+p.second;});
//}
void create_npd(name_path_date &bejegyzesek, path_value &pv_map, std::string input)
{
boost::tokenizer<boost::escaped_list_separator<char>> tokenizer{input};
boost::tokenizer<boost::escaped_list_separator<char>>::iterator it{tokenizer.begin()};
std::string name = *it;
std::string path = *(++it);
std::string date = *(++it);
path = path.substr(2);
if (!check_bejegyzesek(bejegyzesek, name, path))
bejegyzesek.push_back(Adatok(name, path, date, pv_map["/home/erik/Documents/Programs/"+path]));
}
bool check_bejegyzesek(name_path_date &bejegyzesek, std::string name, std::string path)
{
bool ok = false;
for (name_path_date::iterator it{bejegyzesek.begin()}; it != bejegyzesek.end(); it++)
{
if ((it->get_name() == name) && (it->get_path() == path))
ok = true;
}
return ok;
}
bool check_vedes(vedes_vec &v_vec, std::string name)
{
vedes_vec::iterator it = v_vec.find(name);
if (it != v_vec.end()) return true;
else return false;
}
void vedes_elem(vedes_vec &v_vec, std::string name, int &&points)
{
/*for (auto &it : v_vec)
if (it.get_name() == name)
it.set_stuff(points);
*/
vedes_vec::iterator i = v_vec.find(name);
std::pair<int,int> it = i->second;
//auto& jt = it->second;
it.first++;
it.second += points;
}
void create_np(name_path_date &bejegyzesek, path_value &name_point)
{
for (name_path_date::iterator it{bejegyzesek.begin()}; it != bejegyzesek.end(); it++)
if (name_point.count(it->get_name()) == 0)
name_point.insert(std::make_pair(it->get_name(), it->get_points()));
else
name_point[it->get_name()] += it->get_points();
}
void list_np(path_value &name_point)
{
for (path_value::iterator it{name_point.begin()}; it != name_point.end(); it++)
{
if (it->second)
std::cout << it->first << " " << it->second << std::endl;
}
}
void list_bejegyzesek(name_path_date &bejegyzesek)
{
for (name_path_date::iterator it{bejegyzesek.begin()}; it != bejegyzesek.end(); it++)
if (it->get_name() == "Varga Erik")
std::cout << it->get_name() << " " << it->get_path() << " " << it->get_points() << std::endl;
}
void create_rl(ranglista &rl_vec, path_value &name_point)
{
for (auto &it : name_point)
{
if (it.second > 0)
rl_vec.push_back(Ranglista(it.first, it.second));
}
std::sort(rl_vec.begin(), rl_vec.end());
}
void list_rl(ranglista &rl_vec)
{
for (auto &it : rl_vec)
std::cout << it.get_name() << " " << it.get_points() << std::endl;
}
void vedes_atlag(name_path_date &bejegyzesek, vedes_vec &v_vec)
{
std::string key = "City/Debrecen/Oktatás/Informatika/Programozás/DEIK/Prog1/Labor/Védés/";
for (auto &it : bejegyzesek)
{
if ((it.get_path().find("City/Debrecen/Oktatás/Informatika/Programozás/DEIK/Prog1/Labor/Védés/") != std::string::npos) && (it.get_points()) && (!check_vedes(v_vec, it.get_name())))
v_vec.insert(std::make_pair(it.get_name(),std::make_pair(1,it.get_points())));
else if ((check_vedes(v_vec, it.get_name())) && (it.get_path().find("City/Debrecen/Oktatás/Informatika/Programozás/DEIK/Prog1/Labor/Védés/") != std::string::npos) && (it.get_points()))
vedes_elem(v_vec, it.get_name(), it.get_points());
}
}
void list_atlag(vedes_vec &v_vec)
{
//std::sort(v_vec.begin(), v_vec.end());
Comparator compFunctor = [](std::pair<std::string,std::pair<int,int>> elem1,std::pair<std::string,std::pair<int,int>> elem2)
{
std::pair<int,int> it = elem1.second;
std::pair<int,int> jt = elem2.second;
return it.first < jt.first;
};
std::set<std::pair<std::string,std::pair<int,int>>,Comparator> v_set(v_vec.begin(),v_vec.end(),compFunctor);
//int sum = 0;
//int csum = 0;
for (std::pair<std::string,std::pair<int,int>> it : v_set)
{
std::pair<int,int> jt = it.second;
std::cout << it.first << " " << (jt.second - jt.first) / jt.first<< std::endl;
//sum += it.get_point();
//csum += it.get_count();
//sum = std::accumulate(v_vec.begin(), v_vec.end(), 0, [](int i, Vedes &o) { return i + o.get_point(); });
//csum = std::accumulate(v_vec.begin(), v_vec.end(), 0, [](int i, Vedes &o) { return i + o.get_count(); });
}
//std::cout << (sum - csum) / csum << std::endl;
}
so, as described here
template<
class Key,
class Compare = std::less<Key>,
class Allocator = std::allocator<Key>
> class set;
std::set is an associative container that contains a sorted set of unique objects of type Key.
I cleaned up your code, and made a Minimal, Complete, and Verifiable example,
#include <iostream>
#include <map>
#include <set>
using point_pair = std::pair<int,int>;
using points_map = std::map<std::string, point_pair>;
using points_set_pair = std::pair<std::string, point_pair>;
auto compFunctor = [](const points_set_pair &elem1, const points_set_pair &elem2)
{
return elem1.second.first < elem2.second.first;
};
using points_set = std::set<points_set_pair, decltype(compFunctor)>;
void list_average(const points_map &v_map)
{
points_set v_set(v_map.begin(),v_map.end(),compFunctor);
for (auto &elem : v_set)
{
const point_pair &jt = elem.second;
std::cout << elem.first << " " << (jt.second - jt.first) / jt.first<< "\n";
}
}
Now consider the first version of main
int main()
{
points_map v_map = { {"foo", { 1, 2}}, {"bar", { 3, 4}}};
list_average(v_map);
}
output:
foo 1
bar 0
Now consider the second version of main:
int main()
{
points_map v_map = { {"foo", { 1, 2}}, {"bar", { 1, 4}}};
list_average(v_map);
}
output:
bar 3
See the problem? As .second.first of the elements are both 1, the latter replaces the first. It is not unique. That's the downside of std::set.
So, what then?
Don't use std::set, but use std::vector and std::sort. Example:
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
using point_pair = std::pair<int,int>;
using points_map = std::map<std::string, point_pair>;
using string_point_pair = std::pair<std::string, point_pair>;
auto compFunctor = [](string_point_pair const &elem1, string_point_pair const &elem2)
{
return
elem1.second.first != elem2.second.first?
elem1.second.first < elem2.second.first:
elem1.second.second < elem2.second.second;
};
void list_average(points_map const &v_map)
{
std::vector<string_point_pair> v_vec(v_map.begin(),v_map.end());
std::sort(v_vec.begin(), v_vec.end(), compFunctor);
for (auto &elem : v_vec)
{
const point_pair &jt = elem.second;
std::cout << elem.first << " " << (jt.second - jt.first) / jt.first<< "\n";
}
}
int main()
{
points_map v_map = { {"foo", { 1, 2}}, {"bar", { 1, 4}}, {"baz", { 2, 4}}};
list_average(v_map);
}
Output:
foo 1
bar 3
baz 1
live demo

Boosts JSON parser escapes quotation marks on MacOS but not on Linux

I have a C++ program running on a server returning JSON formatted data, serialized with boost. In some cases one value part of a JSON packet is another JSON packet serialized to string. Running in XCode on my developing machine the program returns me the value data with escaped quotation marks, compiled on the Linux system without. Is there any way to force boost on linux to do that or a good workaround? (There was an error with missing value quotation marks in general that we already fixed).
Beneath is as an example the first line:
local:
"stations": "{\n \"area CHALLENGE_FUTURE\": \"{\\n \\\"areaname CHALLENGE_FUTURE\\\": [...]"
online:
"stations": "{\n "area CHALLENGE_FUTURE": "{\\n "areaname CHALLENGE_FUTURE": [...]"
Boost version is 1.57, compiler (Local Xcode and Online Linux) is clang-linux-3.5. (see edit)
Here's the simplified code:
// ----- STL
#include <iostream>
#include <string>
#include <map>
// ------ boost
#include <boost/asio.hpp>
#include <boost/foreach.hpp>
//formatter_base.hpp
//------------------------------------------------------------------------------
class formatter_base
{
protected:
std::map<std::string, std::string> datas;
public:
virtual ~formatter_base() {}
void setvalue(std::string key, std::string value)
{
datas[key] = value;
}
std::string getvalue(std::string key)
{
return datas[key];
}
bool containsKey(std::string key)
{
return (datas.find(key) != datas.end());
}
virtual void deserialize(char *data, const std::size_t size) = 0;
virtual std::string serialize(std::vector<std::string> keys) = 0;
};
//json_formatter.hpp
class json_formatter : public formatter_base
{
public:
virtual void deserialize(char *data, const std::size_t size);
virtual std::string serialize(std::vector<std::string> keys);
};
//datapacket.hpp
//------------------------------------------------------------------------------
class server;
extern server *tcp_server;
class datapacket
{
static const char id[4];
public:
enum DataFormat { BINARY = 0, JSON, XML };
std::string ip;
bool useHeader;
datapacket() : useHeader(false), packet_data(NULL) {}
datapacket(DataFormat format);
std::vector<char> process(char *data, std::size_t size, std::string ip);
std::string getvalue(std::string key)
{
return packet_data->getvalue(key);
}
void setvalue(std::string key, std::string value)
{
packet_data->setvalue(key, value);
}
bool containsKey(std::string key)
{
return packet_data->containsKey(key);
}
std::vector<char> serialize();
std::string toString();
private:
bool deserialize(char *data, std::size_t size);
std::string serialize_data(std::vector<std::string> keys);
formatter_base *packet_data;
};
//datapacket.cpp
//------------------------------------------------------------------------------
#include <boost/iostreams/stream.hpp>
#include <boost/foreach.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <string.h>
datapacket::datapacket(DataFormat format)
: useHeader(false)
{
if(format == JSON)
{
packet_data = new json_formatter();
}
else
{
packet_data = NULL;
}
}
std::vector<char> datapacket::process(
char *data, std::size_t size, std::string ip)
{
//std::cout << "datapacket::process" << std::endl;
this->ip = ip;
std::vector<char> ret;
if (!deserialize(data, size))
return ret;
std::vector<std::string> keys;
std::string result;
/*extern void process(datapacket& gamedata);
process(*this);*/
ret.push_back('a');
ret.push_back('c');
ret.push_back('k');
return ret;
}
bool datapacket::deserialize(char *data, std::size_t size)
{
packet_data = new json_formatter();
packet_data->deserialize(data, size);
return true;
}
std::string datapacket::serialize_data(std::vector<std::string> keys)
{
return packet_data->serialize(keys);
}
std::vector<char> datapacket::serialize()
{
std::vector<std::string> keys;
std::string str = serialize_data(keys);
std::vector<char> ret;
ret.assign(str.begin(), str.end());
return ret;
}
std::string datapacket::toString()
{
std::vector<std::string> keys;
std::string str = serialize_data(keys);
return str;
}
//json_formatter.cpp
//------------------------------------------------------------------------------
using namespace boost::property_tree;
void json_formatter::deserialize(char *data, const std::size_t size)
{
std::stringstream ss;
ss.write(data, size);
// std::cout << "ss: " << ss.str() << std::endl;
ptree pt;
read_json(ss, pt);
BOOST_FOREACH(ptree::value_type &v, pt)
{
//log all received data
//std::cout << v.first.data() << ": " << v.second.data() << std::endl;
datas[v.first.data()] = v.second.data();
}
}
///-workaround 1.57 json
template <typename T>
struct my_id_translator
{
typedef T internal_type;
typedef T external_type;
boost::optional<T> get_value(const T &v) { return v.substr(1, v.size() - 2) ; }
boost::optional<T> put_value(const T &v) { return '"' + v + '"'; }
};
///
std::string json_formatter::serialize(std::vector<std::string> keys)
{
ptree pt;
if(keys.empty())
{
typedef std::map<std::string, std::string> mapType;
BOOST_FOREACH(const mapType::value_type& myPair, datas)
{
//workaround for wrong formatted string
if((BOOST_VERSION == 105700) && (BOOST_OS_LINUX))
{
//1.57
pt.put(myPair.first, myPair.second, my_id_translator<std::string>());
}
else
{
//1.54
pt.put(myPair.first, myPair.second);
}
//std::cout << myPair.first << ": " << myPair.second << std::endl;
}
}
else
{
BOOST_FOREACH(std::string key, keys)
{
//workaround for wrong formatted string
if(BOOST_VERSION == 105700)
{
#if BOOST_OS_LINUX
pt.put(key, "\"" + datas[key] + "\"", my_id_translator<std::string>());
#else
pt.put(key, datas[key], my_id_translator<std::string>());
#endif
}
else
{
pt.put(key, datas[key]);
}
// std::cout << key << ": " << datas[key] << std::endl;
}
}
std::stringstream ss;
write_json(ss, pt);
std::string str = ss.str();
// WORKAROUND
// Replace all escaped backslashes
// This was because some clients couldn't interpret "command\\/read"
std::string oldStr = "\\/";
std::string newStr = "/";
std::size_t pos = 0;
while((pos = str.find(oldStr)) != std::string::npos){
str = str.replace(pos, oldStr.length(), newStr);
pos += newStr.length();
}
// /WORKAROUND
//std::cout << "Serialize message:" << std::endl;
//std::cout << str << std::endl;
return str;
}
//main.cpp
//------------------------------------------------------------------------------
class dataClass
{
public:
dataClass() {}
std::string name;
};
class innerDataClass
{
public:
innerDataClass() {}
std::string name;
int score;
std::string baseClassName;
};
using boost::asio::ip::tcp;
namespace stdpatch
{
template <typename T> std::string to_string(const T& n)
{
std::ostringstream stm;
stm << n;
return stm.str();
}
}
std::map<std::string, dataClass> listDC;
std::map<std::string, innerDataClass> listIDC;
void Init()
{
//Some initial values
dataClass d1; d1.name = "dataClass1"; listDC["d1"] = d1;
dataClass d2; d2.name = "dataClass2"; listDC["d2"] = d2;
innerDataClass i1; i1.name = "innerClass1"; i1.baseClassName = "dataClass1";
i1.score = 5; listIDC["i1"] = i1;
innerDataClass i2; i2.name = "innerClass2"; i2.baseClassName = "dataClass1";
i1.score = 21; listIDC["i2"] = i2;
innerDataClass i3; i3.name = "innerClass3"; i1.baseClassName = "dataClass2";
i1.score = 1; listIDC["i3"] = i3;
}
//returns JSON
datapacket GetJSON()
{
std::pair<std::string, dataClass> baseClassPair;
std::pair<std::string, innerDataClass> innerClassPair;
datapacket baseClasses (datapacket::JSON);
baseClasses.setvalue("comment", "this holds all the base classes");
BOOST_FOREACH(baseClassPair, listDC)
{
datapacket baseClassData (datapacket::JSON);
baseClassData.setvalue("dataName", baseClassPair.first);
BOOST_FOREACH(innerClassPair, listIDC)
{
if (innerClassPair.second.baseClassName == baseClassPair.second.name)
{
datapacket innerClassData (datapacket::JSON);
innerClassData.setvalue(
"name", innerClassPair.second.name);
innerClassData.setvalue(
"score", stdpatch::to_string(innerClassPair.second.score));
baseClassData.setvalue(
"inner " + innerClassPair.first, innerClassData.toString());
}
}
baseClasses.setvalue("base " + baseClassPair.first, baseClassData.toString());
}
datapacket packet (datapacket::JSON);
packet.setvalue("comment", "this is the base-packet");
packet.setvalue("baseClasses", baseClasses.toString());
return packet;
}
int main(int argc, char* argv[])
{
Init();
datapacket packet (datapacket::JSON);
packet = GetJSON();
std::cout << std::endl << std::endl
<< "------------- RESULT --------------"
<< std::endl << std::endl;
std::cout << packet.toString() << std::endl;
return 0;
}
The expected output should be:
------------- RESULT --------------
baseClasses: {
"base d1": "{\n \"dataName\": \"d1\",\n \"inner i1\": \"{\\n \\\"gameID\\\": \\\"5\\\",\\n \\\"name\\\": \\\"innerClass1\\\"\\n}\\n\",\n \"inner i2\": \"{\\n \\\"gameID\\\": \\\"1989860191\\\",\\n \\\"name\\\": \\\"innerClass2\\\"\\n}\\n\"\n}\n",
"base d2": "{\n \"dataName\": \"d2\"\n}\n",
"comment": "this holds all the base classes"
}
comment: this is the base-packet
{
"baseClasses": "{\n \"base d1\": \"{\\n \\\"dataName\\\": \\\"d1\\\",\\n \\\"inner i1\\\": \\\"{\\\\n \\\\\\\"gameID\\\\\\\": \\\\\\\"5\\\\\\\",\\\\n \\\\\\\"name\\\\\\\": \\\\\\\"innerClass1\\\\\\\"\\\\n}\\\\n\\\",\\n \\\"inner i2\\\": \\\"{\\\\n \\\\\\\"gameID\\\\\\\": \\\\\\\"1989860191\\\\\\\",\\\\n \\\\\\\"name\\\\\\\": \\\\\\\"innerClass2\\\\\\\"\\\\n}\\\\n\\\"\\n}\\n\",\n \"base d2\": \"{\\n \\\"dataName\\\": \\\"d2\\\"\\n}\\n\",\n \"comment\": \"this holds all the base classes\"\n}\n",
"comment": "this is the base-packet"
}
but the quotation marks are unescaped in my servers case:
comment: this is the base-packet
{
"baseClasses": "{\n "base d1": "{\\n "dataName": "d1",\\n "inner i1": "{\\\\n "gameID": "5",\\\\n "name": "innerClass1"\\\\n}\\\\n",\\n "inner i2": "{\\\\n "gameID": "1989860191",\\\\n "name": "innerClass2"\\\\n}\\\\n"\\n}\\n",\n "base d2": "{\\n \\\"dataName": "d2"\\n}\\n",\n "comment": "this holds all the base classes"\n}\n",
"comment": "this is the base-packet"
}
To test wether the problem is within write_json or the transit I made a simple JSON package:
datapacket testData(datapacket::JSON);
testData.setvalue("text", "\"world\"");
testData.setvalue("inner1", testData.toString());
testData.setvalue("inner2", testData.toString());
The results are the following:
XCode:
{
"inner1": "{\n \"text\": \"\\\"world\\\"\"\n}\n",
"inner2": "{\n \"inner1\": \"{\\n \\\"text\\\": \\\"\\\\\\\"world\\\\\\\"\\\"\\n}\\n\",\n \"text\": \"\\\"world\\\"\"\n}\n",
"text": "\"world\""
}
Server:
{
"inner1": "{\n "text": ""world""\n}\n"
"inner2": "{\n "text": ""world"",\n "inner1": "{\\n "text": ""world""\\n}\\n"\n}\n",
"text": ""world""
}
This should show that the problem is within the write_json
On my linux box I've tested this to be not an issue:
Live On Coliru
#include<boost/property_tree/json_parser.hpp>
#include<iostream>
int main(){
using namespace boost::property_tree;
ptree inner;
inner.put("area CHALLENGE_FUTURE.areaname CHALLENGE_FUTURE", "something");
std::ostringstream inner_json;
write_json(inner_json, inner);
ptree outer;
outer.put("stations", inner_json.str());
write_json(std::cout, outer);
}
Which prints
{
"stations": "{\n \"area CHALLENGE_FUTURE\": {\n \"areaname CHALLENGE_FUTURE\": \"something\"\n }\n}\n"
}
Exactly what it should print if you ask me.
If you want the 'inner' JSON to be unescaped, why not make it part of the same tree?
Live On Coliru
#include<boost/property_tree/json_parser.hpp>
#include<iostream>
int main(){
using namespace boost::property_tree;
ptree outer;
auto& inner = outer.add_child("stations", {});
inner.put("area CHALLENGE_FUTURE.areaname CHALLENGE_FUTURE", "something");
write_json(std::cout, outer);
}
Which prints
{
"stations": {
"area CHALLENGE_FUTURE": {
"areaname CHALLENGE_FUTURE": "something"
}
}
}
This might be the silliest error ever: After weeks of tracing the error down, we've found out that the linux compiling shell script we've used to build the project on our machine manually changed the JSON parsing behaviour.
If for all we've learned something from this, it is: Never trust stuff you've copied from the internet.

boost qi::grammar not updating valuetype using spirit/phoenix

I have the following problem:
upon parsing a text using qi::grammar the struct grammer is not returning the values of the parsed object.
The Grammer
template<typename Iterator>
struct PositionalDegreeImpl_grammer : qi::grammar<Iterator,PositionalDegreeImpl>
{
struct Type_ : qi::symbols<char,horus::parsergenerator::nmea::CardinalDirection::Direction>
{
Type_()
{
this->add("E", horus::parsergenerator::nmea::CardinalDirection::Direction::EAST)
("N", horus::parsergenerator::nmea::CardinalDirection::Direction::NORTH)
("W", horus::parsergenerator::nmea::CardinalDirection::Direction::WEST)
("S", horus::parsergenerator::nmea::CardinalDirection::Direction::SOUTH);
}
} type_;
qi::rule<Iterator,PositionalDegreeImpl> r;
PositionalDegreeImpl_grammer() : PositionalDegreeImpl_grammer::base_type(r)
{
r = (double_ >> ',' >> type_)
[
qi::_val = boost::phoenix::construct<PositionalDegreeImpl>(qi::_2,qi::_1)
];
}
};
During the debug session is can see that the correct Constructor is being called, with the correct values.
std::string str = "123.2,W";
PositionalDegreeImpl val(horus::parsergenerator::nmea::CardinalDirection::Direction::EAST,1.0);
PositionalDegreeImpl_grammer<std::string::iterator> pos_deg_parser;
bool r = qi::parse(str.begin(), str.end(), pos_deg_parser, &val);
if (!r || val.degrees() != 123.2)
{
std::cout<< "Err" <<std::endl;
}
val contains the initial values after parsing. I have the feeling i am over looking something.
Pure virutal base
class PositionalDegree
{
public:
/*! number of degrees of the position */
virtual double degrees() const = 0;
/*! the direction {N,S,E,W} of the position */
virtual const CardinalDirection& direction() const = 0;
/*! Provide the assignment operator */
virtual PositionalDegree& operator=(const PositionalDegree&) = 0;
};
Implementation
class PositionalDegreeImpl : public PositionalDegree
{
private:
double _degrees;
CardinalDirectionImpl _direction;
public:
PositionalDegreeImpl();
explicit PositionalDegreeImpl(PositionalDegree const &other);
PositionalDegreeImpl(PositionalDegreeImpl const &other);
PositionalDegreeImpl(CardinalDirectionImpl const & cardinal, double degrees);
virtual ~PositionalDegreeImpl();
virtual double degrees() const;
virtual const CardinalDirection& direction() const;
virtual PositionalDegree& operator=(const PositionalDegree& other);
PositionalDegreeImpl& operator=(const PositionalDegreeImpl& other);
};
Any ideas, tips would be appreciated.
Regards Auke
Looks like you're missing parentheses here:
qi::rule<Iterator,PositionalDegreeImpl()> r;
If I remember correctly, the absolute requirement to include the parens will be lifted in an upcoming version (might already be there in 1_57_0
Here's a self contained example
Live On Coliru
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
namespace horus { namespace parsergenerator { namespace nmea {
struct CardinalDirection {
enum class Direction { EAST, NORTH, WEST, SOUTH };
};
struct PositionalDegreeImpl {
PositionalDegreeImpl(CardinalDirection::Direction direction, double d)
: _dir(direction), _degrees(d)
{
}
double degrees() const { return _degrees; }
private:
CardinalDirection::Direction _dir;
double _degrees;
};
template<typename Iterator, typename T = PositionalDegreeImpl>
struct PositionalDegreeGrammar : qi::grammar<Iterator,T()>
{
PositionalDegreeGrammar() : PositionalDegreeGrammar::base_type(r)
{
r = (qi::double_ >> ',' >> type_)
[
qi::_val = boost::phoenix::construct<T>(qi::_2,qi::_1)
];
}
private:
struct Type_ : qi::symbols<char, CardinalDirection::Direction> {
Type_() {
this->add
("E", CardinalDirection::Direction::EAST)
("N", CardinalDirection::Direction::NORTH)
("W", CardinalDirection::Direction::WEST)
("S", CardinalDirection::Direction::SOUTH);
}
} type_;
qi::rule<Iterator,T()> r;
};
} } }
int main()
{
using namespace horus::parsergenerator::nmea;
typedef std::string::const_iterator It;
PositionalDegreeImpl val(CardinalDirection::Direction::EAST, 1.0);
PositionalDegreeGrammar<It> pos_deg_parser;
std::string const str = "123.2,W";
It f = str.begin(), l = str.end();
bool r = qi::parse(f, l, pos_deg_parser, val);
if (r)
std::cout << val.degrees() << ": " << std::boolalpha << (123.2==val.degrees()) << "\n";
else
std::cout << "parsing failed\n";
}
Prints:
123.2: true

Code for finding string between "" in a data structure

I need help to solve the following:
Lets say I have the number 70368 in int var and that I want to find the corresponding string "EVT_ACP_CAPT_MIC_FLT" in the structure below and load it ( including the "") to a
char* event variable
The code solution must work for any number lenght between 1 to 5.
struct NameOffset TestEvents[] = {
{ "EVT_ACP_CAPT_LAST1", 70387 },
{ "EVT_ACP_CAPT_LAST1", 70387 },
{ "EVT_ACP_CAPT_LAST2", 70512 },
{ "EVT_ACP_CAPT_LAST2", 70512 },
{ "EVT_ACP_CAPT_MASK_BOOM_SWITCH", 70385 },
{ "EVT_ACP_CAPT_MIC_FLT", 70368 },
{ "EVT_ACP_CAPT_MIC_HF1", 70510 },
{ "EVT_ACP_CAPT_MIC_HF2", 70511 },
};
The table is in real very long, this is just a few lines to show the structure.
Assuming that the fields of struct NameOffset are Name and Offset, you can find a match using a loop, like this:
for (int i = 0 ; i != sizeof(TestEvents)/sizeof(struct NameOffset) ; i++) {
if (PmdgEvents[i].Offset == intVar) {
printf("%s\n", PmdgEvents[i].Name);
break;
}
}
The brute-force approach is to use a loop and compare the second struct member to identigfy the right struct.
If the code will be executed regularly, you should use a unordered_map: load the data to a unordered_map<int, string> (or char * instead of string if you like) and then use
event_map[code];
to access the string.
#include <unordered_map>
#include <iostream>
using namespace std;
struct NameOffset {
const char *event;
int code;
};
struct NameOffset TestEvents[] = {
{ "EVT_ACP_CAPT_LAST1", 70387 },
{ "EVT_ACP_CAPT_LAST2", 70512 },
{ "EVT_ACP_CAPT_MASK_BOOM_SWITCH", 70385 },
{ "EVT_ACP_CAPT_MIC_FLT", 70368 },
{ "EVT_ACP_CAPT_MIC_HF1", 70510 },
{ "EVT_ACP_CAPT_MIC_HF2", 70511 },
};
#define N_ELEMENTS (sizeof(TestEvents) / sizeof(NameOffset))
int main() {
unordered_map<int, const char *> event_map;
for (NameOffset *it = TestEvents, *it_end = TestEvents + N_ELEMENTS;
it != it_end; ++it) {
event_map[it->code] = it->event;
}
cout << event_map[70387] << endl;
cout << event_map[70512] << endl;
cout << event_map[70385] << endl;
cout << event_map[70368] << endl;
cout << event_map[70510] << endl;
cout << event_map[70511] << endl;
return 0;
}
Create a Map of <int,string> and then index the map with the corresponding value you want to search
#include<iostream>
#include<map>
#include<string>
using namespace std;
int main (){
std::pair<int,string> TestEvents[] = {
make_pair( 70387,"EVT_ACP_CAPT_LAST1" ),
make_pair( 70???,"EVT_ACP_CAPT_LAST1" ),
make_pair( 70512,"EVT_ACP_CAPT_LAST2" ),
make_pair( 70512,"EVT_ACP_CAPT_LAST2" ),
make_pair( 70385, "EVT_ACP_CAPT_MASK_BOOM_SWITCH" ),
make_pair( 70368,"EVT_ACP_CAPT_MIC_FLT" ),
make_pair( 70510,"EVT_ACP_CAPT_MIC_HF1" ),
make_pair( 70511,"EVT_ACP_CAPT_MIC_HF2" )
};
map<int,string> mapTestEvents(TestEvents,TestEvents + sizeof TestEvents / sizeof TestEvents[0]);
int var = 70368;
const char* event = mapTestEvents[var].c_str();
return 0;
}
Please note, I assume the integer value is unique else the whole problem statement would be void.
And yet another way to do it using std::sort and std::lower_bound
#include<iostream>
#include<map>
#include<string>
#include<algorithm>
using namespace std;
struct NameOffset {
string name;
int offset;
};
NameOffset TestEvents[] = {
{ "EVT_ACP_CAPT_LAST1", 70387 },
{ "EVT_ACP_CAPT_LAST1", 70387 },
{ "EVT_ACP_CAPT_LAST2", 70512 },
{ "EVT_ACP_CAPT_LAST2", 70512 },
{ "EVT_ACP_CAPT_MASK_BOOM_SWITCH", 70385 },
{ "EVT_ACP_CAPT_MIC_FLT", 70368 },
{ "EVT_ACP_CAPT_MIC_HF1", 70510 },
{ "EVT_ACP_CAPT_MIC_HF2", 70511 },
};
bool CompareByEvent(NameOffset const& lhs, NameOffset const& rhs) {
return lhs.offset < rhs.offset;
}
int main (){
sort(TestEvents,TestEvents + sizeof TestEvents / sizeof TestEvents[0], &CompareByEvent);
NameOffset var = {"",70510};
const char* event =lower_bound(TestEvents,TestEvents + sizeof TestEvents / sizeof TestEvents[0], var,&CompareByEvent)->name.c_str();
return 0;
}