How to access yaml with complex keys - yaml-cpp

The file test.yaml .....
---
map:
? [L, itsy] : ISO_LOW_ITSY(out, in, ctrl)
? [L, bitsy] : ISO_LOW_BITSY(out, in, ctrl)
? [L, spider] : ISO_LOW_SPIDER(out, in, ctrl)
? [H, ANY] : ISO_HIGH(out, in, ctrl)
What command can I use to access one of these with yaml-cpp. I can access the map as a whole, but not individual elements.
Here's what I am trying:
YAML::Node doc = YAML::LoadFile("test.yaml");
std::cout << "A:" << doc["mapping"] << std::endl;
std::cout << "LS1:" << doc["mapping"]["[L, spider]"] << std::endl;
std::cout << "LS2:" << doc["mapping"]["L", "spider"] << std::endl;
Here are my results:
A:? ? - L
- itsy
: ISO_LOW_ITSY(out, in, ctrl)
: ~
? ? - L
- bitsy
: ISO_LOW_BITSY(out, in, ctrl)
: ~
? ? - L
- spider
: ISO_LOW_SPIDER(out, in, ctrl)
: ~
? ? - H
- ANY
: ISO_HIGH(out, in, ctrl)
: ~
LS1:
LS2:
If this is not yet possible in yaml-cpp, I would like to know that too.

You have to define a type that matches your key. For example, if your key is a sequence of two scalars:
struct Key {
std::string a, b;
Key(std::string A="", std::string B=""): a(A), b(B) {}
bool operator==(const Key& rhs) const {
return a == rhs.a && b == rhs.b;
}
};
namespace YAML {
template<>
struct convert<Key> {
static Node encode(const Key& rhs) {
Node node;
node.push_back(rhs.a);
node.push_back(rhs.b);
return node;
}
static bool decode(const Node& node, Key& rhs) {
if(!node.IsSequence() || node.size() != 2)
return false;
rhs.a = node[0].as<std::string>();
rhs.b = node[1].as<std::string>();
return true;
}
};
}
Then, if your YAML file is
? [foo, bar]
: some value
You could write:
YAML::Node doc = YAML::LoadFile("test.yaml");
std::cout << doc[Key("foo", "bar")]; // prints "some value"
Note:
I think your YAML doesn't do what you intended. In block context, an explicit key/value pair must be on separate lines. In other words, you should do
? [L, itsy]
: ISO_LOW_ITSY(out, in, ctrl)
and not
? [L, itsy] : ISO_LOW_ITSY(out, in, ctrl)
The latter makes it a single key, with an (implicit) null value; i.e., it's the same as
? [L, itsy] : ISO_LOW_ITSY(out, in, ctrl)
: ~
(You can see this by how yaml-cpp outputs your example.) See the relevant area in the spec.

Related

Defining strict_real_policies for reals with a comma decimal character

I would like to create a custom policy derived from strict_real_policies that will parse reals, such as "3,14", i.e. with a comma decimal point as used e.g. in Germany.
That should be easy, right?
#include <iostream>
#include <string>
#include <boost/spirit/home/x3.hpp>
template <typename T>
struct decimal_comma_strict_real_policies:boost::spirit::x3::strict_real_policies<T>
{
template <typename Iterator>
static bool
parse_dot(Iterator& first, Iterator const& last)
{
if (first == last || *first != ',')
return false;
++first;
return true;
}
};
void parse(const std::string& input)
{
namespace x3=boost::spirit::x3;
std::cout << "Parsing '" << input << "'" << std::endl;
std::string::const_iterator iter=std::begin(input),end=std::end(input);
const auto parser = x3::real_parser<double, decimal_comma_strict_real_policies<double>>{};
double parsed_num;
bool result=x3::parse(iter,end,parser,parsed_num);
if(result && iter==end)
{
std::cout << "Parsed: " << parsed_num << std::endl;
}
else
{
std::cout << "Something failed." << std::endl;
}
}
int main()
{
parse("3,14");
parse("3.14");
}

How to clear the std::map<K,V*> container and delete all the pointed objects safely?

None of the standard library containers will call delete on contained raw pointers. I have checked for a solution on SO for C++98 but have not found the answer.
I have created template <typename K, typename V> void clearAndDestroy( std::map<K, V*> *&myMap) as a replacement function for std::clear() (remove all elements and call the destructors).
It works for maps with pointers to objects std::map(key,V*). The function works also for cases when map contains same V* pointers for the different keys.
#include <iostream>
#include <map>
#include <string>
#include <set>
using namespace std;
// clearAndDestroy deletes all objects and remove them from the std::map(K,V*) container.
template <typename K, typename V>
void clearAndDestroy( std::map<K, V*> *&myMap)
{
if(myMap == NULL)
return;
std::set<V*> mySet;
typename std::map<K,V*>::iterator itr;
typename std::set<V*>::iterator sitr;
itr = myMap->begin();
while (itr != myMap->end()) {
mySet.insert(itr->second);
++itr;
}
sitr = mySet.begin();
while (sitr != mySet.end()) {
delete(*sitr);
++sitr;
}
myMap->clear();
}
template <typename K, typename V> void clear1( std::map<K, V*> *myMap)
{
if(myMap == NULL) return;
typename std::map<K, V*>::iterator itr = myMap->begin();
while (itr != myMap->end()) {
typename std::map<K, V*>::iterator toErase = itr;
++itr;
myMap->erase(toErase);
delete(toErase->second);
}
}
template <typename M> void clear2( M *myMap )
{
if(myMap == NULL) return;
for ( typename M::iterator it = myMap->begin(); it != myMap->end(); ++it ) {
delete it->second;
}
myMap->clear();
}
class MY_CLASS
{
public:
int counter;
string *message;
MY_CLASS(int c, string *m):counter(c), message(m) {
std::cout << "Constructor MY_CLASS " << this << std::endl;
};
~MY_CLASS()
{
if(message) {
cout << "Being destroyed MY_CLASS: " << *message << " this = " << this <<endl;
}
else {
cout << "Being destoyed MY_CLASS: " << " this = " << this <<endl;
}
if(message) {
delete message;
message = NULL;
}
}
MY_CLASS(const MY_CLASS & other)
{
std::cout << "Copy Constructor MY_CLASS " << this << std::endl;
//1.
counter = other.counter;
//2.
if(other.message) {
message = new string;
*message = *other.message; // copy the value
}
else {
message = NULL;
}
}
};
void print(const string *str,MY_CLASS *& value, void *)
{
if (value && value->message)
cout << value->counter << " ! " << *(value->message) << endl;
}
int main() {
std::map<std::string, MY_CLASS *> *mpa = new std::map<std::string, MY_CLASS *>;
MY_CLASS *p = new MY_CLASS(2, new string("abc"));
mpa->insert(std::pair<std::string, MY_CLASS *>("1", p));
mpa->insert(std::pair<std::string, MY_CLASS *>("2", p));
clearAndDestroy(mpa);
delete mpa;
return 0;
}
Output:
Constructor MY_CLASS 0x111ccb0
Being destroyed MY_CLASS: abc this = 0x111ccb0
Being restricted to C++98 is clearAndDestroy my best option? Thank you!
Another approach you can take is using an object wrapper. Place the pointer you want within an object and have the destructor call the delete on the pointer. Basically building a simple "smart-pointer".
class AutoDeletePtr {
MY_CLASS* pointer;
AutoDeletePtr(MY_CLASS* myObjectPtr) {pointer = myObjectPtr};
~AutoDeletePtr() {delete(pointer)};
}
You can insert these objects into the std::map.

Segmentation Fault 11 in while loop dependent on std::map.count()

Within the definition of a class template method, the following lambda definition has been included:
//lambda
auto fnd_nxt_free = [&] ()
{
while(m_map.count(it->first))
{
DBG_PRINT("it->first:" << it->first)
DBG_PRINT("m_map.count(it->first):" << m_map.count(it->first))
DBG_PRINT("distance(m_map.begin(), it):" << distance(m_map.begin(),it))
if(it->first == keyEnd)
{
break;
}
++it;
}
return it;
};
The lambda is used to increment the iterator to the map, for the purpose of assignment.
For consecutive assignments to the map following construction, the intended logic of the code is to increment the iterator to the next free key position within the specified range, before insertion of the specific value.
The different values used in this particular test are (0,1,'A'), which corresponds to construction, (0,2,'B') and (0,3,'C'). Note that the range is implied as [keyBegin, keyEnd).
The segmentation fault is encountered for the third insertion inside the lambda fnd_nxt_free. Notice the debug prints highlighted inside the lambda as part of the source code. The corresponding output (for the third insertion) is as shown below:
it->first:0
m_map.count(it->first):1
distance(m_map.begin(), it):0
it->first:1
m_map.count(it->first):1
distance(m_map.begin(), it):1
it->first:1
m_map.count(it->first):1
distance(m_map.begin(), it):2
The output of m_map.count(<key>) is expected to be 0 when the distance of the iterator from m_map.begin() is 2, for the insertion (0,3,'C'). As the debug print output shows, the value of key is printed correctly in the first two cases, but does not increment after that, even though the distance of the iterator from m_map.begin() is shown to be 2.
In the case of the second insertion (0,2,'B') the same logic in the lambda works as expected, and the while() loop exits when the iterator is incremented past the first key position (0), and the only debug print that is output from the lambda is:
it->first:0
m_map.count(it->first):1
distance(m_map.begin(), it):0
Thus in the second case, the as the iterator moves past the first key position, mm_count() returns 0. However, the same logic fails in the case of the second insertion. I can assure that the rest of the processing before the lambda is hit is exactly the same in both the cases.
Can someone point to the flaw?
MWE
#define DBG_PRINT(...) (cout << __VA_ARGS__<< endl);
template<class K, class V>
class interval_map{
friend void IntervalMapTest();
private:
map<K,V> m_map;
private:
bool did_assign;
public:
interval_map(V const&);
public:
void assign(K const& keyBegin, K const& keyEnd, V const& val);
public:
V const& operator [](K const& key) const;
public:
struct comp;
public:
void display(void);
};
template<class K, class V> struct interval_map<K,V>::comp{
bool operator()(pair<K const&,V const&> p, V const& v) const{
return(p.second < v);
}
bool operator()(V const& v, pair<K const&, V const&> p) const{
return(v < p.second);
}
};
template<class K, class V> interval_map<K,V>::interval_map(V const& val){
m_map.insert(m_map.begin(), make_pair(numeric_limits<K>::lowest(), val));
}
template<class K, class V> V const& interval_map<K,V>::operator [] (K const& key) const{
return (--m_map.upper_bound(key))->second;
}
template<class K, class V> void interval_map<K,V>::assign(K const& keyBegin, K const& keyEnd, V const& val){
auto begin = m_map.find(keyBegin);
auto end = m_map.find(keyEnd);
auto p = equal_range(begin,end,val,comp());
auto it = p.first;
//lambda
auto fnd_nxt_free = [&] (){
LINE
while(
m_map.count(it->first)
){
DBG_PRINT("it->first:" << it->first)
DBG_PRINT("m_map.count(it->first):" << m_map.count(it->first))
DBG_PRINT("distance(m_map.begin(), it):" << distance(m_map.begin(), it))
if(it->first == keyEnd){
break;
}
++it;
}
return it;
};
if(it == end)
{
it = m_map.begin();
next(it,keyBegin);
fnd_nxt_free();
if(it->first < keyEnd){
m_map.insert(make_pair(it->first,val));
}
}
else{
if(p.second == end){
auto tmpKey = it->first;
do
{
if(fnd_nxt_free()->first < keyEnd)
{
if( fnd_nxt_free()->first - tmpKey < 2)
{
if(it == end){
break;
}
++it;
continue;
}
else
{
if(it->first < keyEnd){
m_map.insert(make_pair(it->first, val));
}
}
}
}while(it->first < keyEnd);
}
else{
it = p.second;
fnd_nxt_free();
if(it->first < keyEnd){
m_map.insert(make_pair(it->first,val));
}
}
}
}
template<class K, class V> void interval_map<K,V>::display(){
for(auto i : m_map){
cout << i.first;
cout << " ";
cout << i.second;
cout << endl;
}
}
void IntervalMapTest(){
interval_map<unsigned int, char> iMap('A');
iMap.assign(0,2,'B');
iMap.assign(0,3,'C');
iMap.display();
return;
}

Tagging (or coloring) CGAL objects

I am trying to use CGAL to perform some simple 2D CSG operations. Here is an example of an intersection of two polygons.
The actual problem is tracking down the origin (marked with color) of each segment in resulting polygon.
I would like to know if that is possible, maybe with some hacking on the CGAL itself. Any suggestion will be highly appreciated.
Unfortunately, there is no out-of-the-box way doing it. However, it doesn't require too much (famous last words...). You need to do two things described below. The first is supported by the API. The second is not, so you will need to patch a source file. A simple example is provided further bellow. Notice that the data you need, that is, the specification of the origin of each edge, ends up in a 2D arrangement data structure. If you want to obtain the polygons with this data, you need to extract them from the arrangement. You can obtain the header pgn_print.h, used in the example, from the 2D-Arrangement book.
Use an instance of CGAL::Polygon_set_2<Kernel, Container, Dcel>, where the Dcel is substituted with an extended Dcel, the halfedge of which is extended with a label that indicates the origin of the halfedge (i.e., first polygon, second polygon, or both in case of an overlap).
Patch the header file Boolean_set_operations_2/Gps_base_functor.h. In particular, add to the body of the three functions called create_edge() statements that set the label of the resulting halfedges according to their origin:
void create_edge(Halfedge_const_handle h1, Halfedge_const_handle h2,
Halfedge_handle h)
{
h->set_label(3);
h->twin()->set_label(3);
}
void create_edge(Halfedge_const_handle h1, Face_const_handle f2,
Halfedge_handle h)
{
h->set_label(1);
h->twin()->set_label(1);
}
void create_edge(Face_const_handle f1, Halfedge_const_handle h2,
Halfedge_handle h)
{
h->set_label(2);
h->twin()->set_label(2);
}
#include <list>
#include <vector>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Boolean_set_operations_2.h>
#include <CGAL/Polygon_set_2.h>
#include "pgn_print.h"
/*! Extend the arrangement halfedge */
template <typename X_monotone_curve_2>
class Arr_labeled_halfedge :
public CGAL::Arr_halfedge_base<X_monotone_curve_2>
{
private:
unsigned m_label;
public:
Arr_labeled_halfedge() : m_label(0) {}
unsigned label() const { return m_label; }
void set_label(unsigned label) { m_label = label; }
virtual void assign(const Arr_labeled_halfedge& he)
{
CGAL::Arr_halfedge_base<X_monotone_curve_2>::assign(he);
m_label = he.m_label;
}
};
template <typename Traits>
class Arr_labeled_dcel :
public CGAL::Arr_dcel_base<CGAL::Arr_vertex_base<typename Traits::Point_2>,
Arr_labeled_halfedge<typename Traits::
X_monotone_curve_2>,
CGAL::Gps_face_base>
{
public:
Arr_labeled_dcel() {}
};
typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel;
typedef Kernel::Point_2 Point_2;
typedef CGAL::Polygon_2<Kernel> Polygon_2;
typedef CGAL::Polygon_with_holes_2<Kernel> Polygon_with_holes_2;
typedef std::vector<Point_2> Container;
typedef CGAL::Gps_segment_traits_2<Kernel, Container> Traits_2;
typedef Arr_labeled_dcel<Traits_2> Dcel;
typedef CGAL::Polygon_set_2<Kernel, Container, Dcel> Polygon_set_2;
typedef std::list<Polygon_with_holes_2> Pwh_list_2;
typedef Polygon_set_2::Arrangement_2 Arrangement_2;
typedef Arrangement_2::Edge_const_iterator Edge_const_iterator;
void print_result(const Polygon_set_2& S)
{
std::cout << "The result contains " << S.number_of_polygons_with_holes()
<< " components:" << std::endl;
Pwh_list_2 res;
S.polygons_with_holes(std::back_inserter(res));
for (Pwh_list_2::const_iterator hit = res.begin(); hit != res.end(); ++hit) {
std::cout << "--> ";
print_polygon_with_holes(*hit);
}
const Arrangement_2& arr = S.arrangement();
for (Edge_const_iterator it = arr.edges_begin(); it != arr.edges_end(); ++it) {
std::cout << it->curve()
<< ", " << it->label()
<< std::endl;
}
}
int main()
{
// Construct the two input polygons.
Polygon_2 P;
P.push_back(Point_2(0, 0));
P.push_back(Point_2(5, 0));
P.push_back(Point_2(3.5, 1.5));
P.push_back(Point_2(2.5, 0.5));
P.push_back(Point_2(1.5, 1.5));
std::cout << "P = "; print_polygon(P);
Polygon_2 Q;
Q.push_back(Point_2(0, 2));
Q.push_back(Point_2(1.5, 0.5));
Q.push_back(Point_2(2.5, 1.5));
Q.push_back(Point_2(3.5, 0.5));
Q.push_back(Point_2(5, 2));
std::cout << "Q = "; print_polygon(Q);
// Compute the union of P and Q.
Polygon_set_2 intersection_set;
intersection_set.insert(P);
intersection_set.intersection(Q);
print_result(intersection_set);
// Compute the intersection of P and Q.
Polygon_set_2 union_set;
union_set.insert(P);
union_set.join(Q);
print_result(union_set);
return 0;
}

Issue Parsing File with YAML-CPP

In the following code, I'm having some sort of issue getting my .yaml file parsed using parser.GetNextDocument(doc);. After much gross debugging, I've found that the (main) issue here is that my for loop is not running, due to doc.size() == 0; What am I doing wrong?
void
BookView::load()
{
aBook.clear();
QString fileName =
QFileDialog::getOpenFileName(this, tr("Load Address Book"),
"", tr("Address Book (*.yaml);;All Files (*)"));
if(fileName.isEmpty())
{
return;
}
else
{
try
{
std::ifstream fin(fileName.toStdString().c_str());
YAML::Parser parser(fin);
YAML::Node doc;
std::map< std::string, std::string > entry;
parser.GetNextDocument(doc);
std::cout << doc.size();
for( YAML::Iterator it = doc.begin(); it != doc.end(); it++ )
{
*it >> entry;
aBook.push_back(entry);
}
}
catch(YAML::ParserException &e)
{
std::cout << "YAML Exception caught: "
<< e.what()
<< std::endl;
}
}
updateLayout( Navigating );
}
The .yaml file being read was generated using yaml-cpp, so I assume it is correctly formed YAML, but just in case, here's the file anyways.
^#^#^#\230---
-
address: ******************
comment: None.
email: andrew(dot)levenson(at)gmail(dot)com
name: Andrew Levenson
phone: **********^#
Edit: By request, the emitting code:
void
BookView::save()
{
QString fileName =
QFileDialog::getSaveFileName(this, tr("Save Address Book"), "",
tr("Address Book (*.yaml);;All Files (*)"));
if (fileName.isEmpty())
{
return;
}
else
{
QFile file(fileName);
if(!file.open(QIODevice::WriteOnly))
{
QMessageBox::information(this, tr("Unable to open file"),
file.errorString());
return;
}
std::vector< std::map< std::string, std::string > >::iterator itr;
std::map< std::string, std::string >::iterator mItr;
YAML::Emitter yaml;
yaml << YAML::BeginSeq;
for( itr = aBook.begin(); itr < aBook.end(); itr++ )
{
yaml << YAML::BeginMap;
for( mItr = (*itr).begin(); mItr != (*itr).end(); mItr++ )
{
yaml << YAML::Key << (*mItr).first << YAML::Value << (*mItr).second;
}
yaml << YAML::EndMap;
}
yaml << YAML::EndSeq;
QDataStream out(&file);
out.setVersion(QDataStream::Qt_4_5);
out << yaml.c_str();
}
}
Along the lines of what you thought, the problem is that you're writing using QDataStream but reading using plain std::ifstream. You need to do either one or the other.
If you want to use the QDataStream, you'll need to read it in as well. Check out the doc for more detail, but it looks like you can just grab the YAML string:
QDataStream in(&file);
QString str;
in >> str;
and then pass it to yaml-cpp:
std::stringstream stream; // remember to include <sstream>
stream << str; // or str.toStdString() - I'm not sure about how QString works
YAML::Parser parser(stream);
// etc.
The point of a std::stringstream is to transform your string containing YAML into a stream that the YAML parser can read.