Defining custom input types for the new yaml-cpp API - yaml-cpp

I am trying to define some custom input types using the new-style YamlCpp API. I have two questions. Let me first say that we are currently using the 0.3 style-api and everything works great. The code-appearance of the new API is about 10x better though so we wanted to move to it.
Why does my current approach bork the compiler?
Is yaml-cpp smart enough to handle nested templates. For example, if my parser knows how to enc/dec std::vectors, and then I define a custom handler for MyAwesomeType separately -- can I ask it to enc/dec a std::vector<MyAwesomeType> and it will figure it out?
My code began by copying and pasting the example on the website (here) and then modifying that. My current implementation attempts to handle conversion of std::vector<std::pair<qint16,qint16>> -- this does not compile. (fyi - qint16 is simply a cross-platform typedef for __int16)
#include "yaml-cpp\yaml.h"
#include <vector>
#include "qglobal.h"
namespace YAML {
// a std::pair of qint16's
template<>
struct convert<std::pair<qint16,qint16>> {
static Node encode( std::pair<qint16,qint16> const& rhs) {
Node node;
std::vector<qint16> newVec;
newVec.push_back(rhs.first);
newVec.push_back(rhs.second);
node = newVec;
return node;
}
static bool decode(Node const& node, std::pair<qint16,qint16> & rhs) {
if(!node.IsSequence() || node.size() != 2) {
return false;
}
rhs.first = node[0].as<qint16>();
rhs.second = node[1].as<qint16>();
return true;
}
};
// a vector of std::pair of qint16's
template<>
struct convert<std::vector<std::pair<qint16,qint16>>> {
static Node encode( std::vector<std::pair<qint16,qint16>> const& rhs) {
Node node;
for(auto pairIt=rhs.begin();pairIt!=rhs.end();++pairIt)
{
node.push_back( *pairIt );
}
return node;
}
static bool decode(Node const& node, std::vector<std::pair<qint16,qint16>> & rhs) {
if( !node.IsSequence() ) {
return false;
}
for(int k=0;k<node.size();++k)
{
rhs.push_back( node[k].as<std::pair<qint16,qint16>>() );
}
return true;
}
};
}
Finally, I would like to then use this to call
std::map<std::string, std::vector<std::pair<qint16, qint16>>> m_vectorOfPairs;
if( doc["myPairs"] )
{
// key exists!
for( YAML::const_iterator it=doc["myPairs"].begin();it!=doc["myPairs"].end();++it)
{
m_vectorOfPairs[it->first.as<std::string>()] = it->second.as<std::vector<std::pair<qint16,qint16>>>();
}
}
...based on input yaml which looks like this...
myPairs:
pairOne: [[4, 25], [48, 336]]
pairTwo: [[4, 25], [57, 336]]
pairThree: [[4, 25], [48, 336]]
The compiler error output from this is about 300 lines, so I will not post that here.
Platform: Visual Studio 2010 SP1 x64
Thanks for your help.
EDIT:
Here is the first of many errors I get, namely that it doesn't even seem happy parsing a Node key with the new style... I'm adding this because what is causing this may be related to the other errors.
Code
YAML::Node doc = YAML::LoadFile(buildFilenamePath(m_spectCameraShortName, relativePath).toStdString());
if( doc["numPixels"] )
//...
Causes
1>ANONPATH\thirdparty\yaml-cpp\include\yaml-cpp/node/detail/impl.h(146): error C2734: 'lhs' : const object must be initialized if not extern
1> ANONPATH\thirdparty\yaml-cpp\include\yaml-cpp/node/detail/impl.h(96) : see reference to function template instantiation 'bool YAML::detail::node_data::equals<const char[10]>(YAML::detail::node &,T (&),YAML::detail::shared_memory_holder)' being compiled
1> with
1> [
1> T=const char [10]
1> ]
1> ANONPATH\thirdparty\yaml-cpp\include\yaml-cpp/node/detail/node_ref.h(52) : see reference to function template instantiation 'YAML::detail::node &YAML::detail::node_data::get<const char[10]>(Key (&),YAML::detail::shared_memory_holder)' being compiled
1> with
1> [
1> Key=const char [10]
1> ]
1> ANONPATH\thirdparty\yaml-cpp\include\yaml-cpp/node/detail/node.h(103) : see reference to function template instantiation 'YAML::detail::node &YAML::detail::node_ref::get<const char[10]>(Key (&),YAML::detail::shared_memory_holder)' being compiled
1> with
1> [
1> Key=const char [10]
1> ]
1> ANONPATH\thirdparty\yaml-cpp\include\yaml-cpp/node/impl.h(333) : see reference to function template instantiation 'YAML::detail::node &YAML::detail::node::get<const char[10]>(Key (&),YAML::detail::shared_memory_holder)' being compiled
1> with
1> [
1> Key=const char [10]
1> ]
1> MYCODEFILE.cpp(75) : see reference to function template instantiation 'YAML::Node YAML::Node::operator []<const char[10]>(Key (&))' being compiled
1> with
1> [
1> Key=const char [10]
1> ]
Note that the Line 75 mentioned in the error above is the
if( doc["numPixels"] )
line
I've recompiled from source latest using Rev 573 of default branch and all these examples are from that code.

Yes, yaml-cpp should be able to handle nested templates (and so you shouldn't need to define a conversion for std::vector<std::pair<qint16, qint16>> - and, this suggests that yaml-cpp should define a conversion for std::pair<T, U> in general (http://code.google.com/p/yaml-cpp/issues/detail?id=188)).
And I'm not sure why the compiler doesn't accept your code (it looks correct, and compiles fine on Linux for me).
Do the tests compile at all? Apparently, 0.5.0 doesn't work out of the box on Visual Studio (see http://code.google.com/p/yaml-cpp/issues/detail?id=182), and I haven't had a chance to test or merge the fix yet.

Related

corefine_and_compute_difference CGAL error: precondition violation

Problem description
I read the mesh from the file "blank.off" and load it into the a surface_mesh variable blank. One file named "hepoints49.txt" stores point clouds. I use function CGAL::advancing_front_surface_reconstruction() to convert this point cloud to surface_mesh sv, and then use function corefine_and_compute_difference(blank,sv,res) to perform the Boolean subtraction between blank and sv.But the program throws an exception and terminates. The following is displayed on the terminal:
Using context 4 . 3 GL
load sv...
Using context 4 . 3 GL
start difference...
CGAL error: precondition violation!
Expression : CGAL::is_valid_polygon_mesh(tm)
File : D:\dev\vcpkg\installed\x64-windows\include\CGAL/Polygon_mesh_processing/orientation.h
Line : 190
Could you please help me solve this problem?
code
#include<iostream>
#include<io.h>
#include<fstream>
#include<algorithm>
#include<array>
#include<CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include<CGAL/Advancing_front_surface_reconstruction.h>
#include<CGAL/Surface_mesh.h>
#include<CGAL/disable_warnings.h>
#include<CGAL/draw_surface_mesh.h>
#include<ctime>
#include<string>
#include<CGAL/polygon_mesh_processing/corefinement.h>
#include<CGAL/polygon_mesh_processing/remesh.h>
#include<CGAL/boost/graph/selection.h>
#include<CGAL/polygon_mesh_processing/repair_self_intersections.h>
using std::cin;
using std::cout;
using std::endl;
using std::string;
namespace PMP = CGAL::Polygon_mesh_processing;
typedef std::array<std::size_t, 3> Facet;
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef Kernel::Point_3 Point_3;
typedef CGAL::Surface_mesh<Point_3> Mesh;
struct Construct {
Mesh& mesh;
template <typename PointIterator>
Construct(Mesh& mesh, PointIterator b, PointIterator e):mesh(mesh) {
for (; b != e; ++b) {
boost::graph_traits<Mesh>::vertex_descriptor v;
v = add_vertex(mesh);
mesh.point(v) = *b;
}
}
Construct& operator=(const Facet f) {
typedef boost::graph_traits<Mesh>::vertex_descriptor vertex_descriptor;
typedef boost::graph_traits<Mesh>::vertices_size_type size_type;
mesh.add_face(vertex_descriptor(static_cast<size_type>(f[0])),
vertex_descriptor(static_cast<size_type>(f[1])),
vertex_descriptor(static_cast<size_type>(f[2])));
return *this;
}
Construct& operator*() { return *this; }
Construct& operator++() { return *this; }
Construct& operator++(int) { return *this; }
};
int main() {
//load blank
Mesh blank, sv,res;
std::ifstream fin("blank.off");
fin>>blank;
fin.close();
CGAL::draw(blank);
//load sv
string filename = "hepoints49.txt" ;
std::cout << "load sv..."<< std::endl;
fin.open(filename);
std::vector<Point_3> points;
std::vector<Facet> facets;
std::copy(std::istream_iterator<Point_3>(fin),
std::istream_iterator<Point_3>(),
std::back_inserter(points));//load points
fin.close();
Construct construct(sv, points.begin(), points.end());
CGAL::advancing_front_surface_reconstruction(points.begin(), points.end(), construct);//convert sv to surface_mesh
CGAL::draw(sv);
std::cout << "start difference..." << std::endl;
bool valid_difference = PMP::corefine_and_compute_difference(blank,sv,res);
if (valid_difference) {
std::cout << "difference was successfully computed. " << std::endl;
CGAL::draw(res);
}
else {
std::cout << "difference could not be completed. Skip. " << endl << endl;
}
//CGAL::draw(res);
return 0;
}
Runtime environment
CGAL version: 5.3
IDE: VS2017
Solution Configuration: Debug x64
I tried to run this program in Release mode, of course there is no exception thrown. But the result I got turned out to be the opposite of what I want.
Files
Files that appearing in the code are provided below:
https://github.com/wenzaifou/for-stack-overflow-question3.git
Github link is provided because the file is relatively large.
The way the mesh is constructed from advancing front output does not filter out isolated vertices, which causes the exception to be raised. Adding a call to CGAL::Polygon_mesh_processing::remove_isolated_vertices(sv) will solve the problem.
Then you might encounter the issue that your meshes are not outward oriented (meaning then represent an infinite portion of space). Adding the following calls will solve the problem:
if (!CGAL::Polygon_mesh_processing::is_outward_oriented(blank))
CGAL::Polygon_mesh_processing::reverse_face_orientations(blank);
if (!CGAL::Polygon_mesh_processing::is_outward_oriented(sv))
CGAL::Polygon_mesh_processing::reverse_face_orientations(sv);
Doc refs here and there.

How to properly check for member at compile time with boost::hana?

I am writing a physics simulation program and I want to do the following:
I have a hana adapted struct and I want to check if this struct has member called "absorbedEnergy" at compile time using:
if constexpr ( ... )
What is the proper way of doing that in c++17 which I use?
Now using hana documentation I have came up with this:
struct HasAE { double absorbedEnergy };
struct HasNoAE {};
temaplate<typename Cell>
void irelevantFunction(Cell& cell){
auto has_absorbedEnergy = hana::is_valid(
[](auto &&p) -> decltype((void) p.absorbedEnergy) {});
if constexpr(has_absorbedEnergy(cell)) { ... }
}
HasAE cell;
HasNoAE anotherCell;
cell.absorbedEnergy = 42; //value known at runtime
irelevantFunction(cell);
irelevantFunction(anotherCell);
The thing is that this compiles just fine with g++ 7.4.0 and does what I expect but fails to compile with clang++-8. It gives an error:
constexpr if condition is not a constant expression
I suspect that this originates from the fact that argument of has_absorbedEnergy - cell is not and constant expression. Is there a way around this?
Your issue seems to be related to the requirement in the standard for the expession in if constexpr to be "contextually converted constant expression of type bool" (see this question). You can work around that by changing the if constexpr to:
if constexpr (decltype(has_absorbedEnergy(cell)){})
https://wandbox.org/permlink/hmMNLberLJmt0ueJ
Alternatively, you can use expression SFINAE to achieve what you want (see the cppreference.com documentation of std::void_t):
#include <type_traits>
#include <iostream>
template <typename, typename= std::void_t<>>
struct has_absorbedEnergy : std::false_type {};
template <typename T>
struct has_absorbedEnergy<T,
std::void_t<decltype(std::declval<T&>().absorbedEnergy)>>
: std::true_type {};
template <typename Cell>
void irelevantFunction([[maybe_unused]] Cell &cell) {
if constexpr (has_absorbedEnergy<Cell>::value)
std::cout << "Has absorbedEnergy\n";
else
std::cout << "Does not have absorbedEnergy\n";
}
struct HasAbsorbedEnergy
{ int absorbedEnergy; };
struct DoesNotHaveAbsorbedEnergy
{};
int main()
{
HasAbsorbedEnergy Has;
DoesNotHaveAbsorbedEnergy DoesNot;
irelevantFunction(Has);
irelevantFunction(DoesNot);
}
https://wandbox.org/permlink/0559JhpVQBOwHC0Z

Xbase Interpreter: Could not access field on instance: null

I am testing the idea of making my dsl Jvm compatible and I wanted to test the possibility of extending Xbase and using the interpreter. I have tried to make a minimal test project to use with the interpreter but I am getting a runtime error. I think I understand the general concepts of adapting Xbase, but am unsure about how the setup/entrypoints for the interpreter and could not find any information regarding the error I am getting or how to resolve. Here are the relevant files for my situation:
Text.xtext:
import "http://www.eclipse.org/xtext/xbase/Xbase" as xbase
import "http://www.eclipse.org/xtext/common/JavaVMTypes" as types
Program returns Program:
{Program}
'program' name=ID '{'
variables=Var_Section?
run=XExpression?
'}'
;
Var_Section returns VarSection:
{VarSection}
'variables' '{'
decls+=XVariableDeclaration+
'}'
;
#Override // Change syntax
XVariableDeclaration returns xbase::XVariableDeclaration:
type=JvmTypeReference name=ID '=' right=XLiteral ';'
;
#Override // Do not allow declarations outside of variable region
XExpressionOrVarDeclaration returns xbase::XExpression:
XExpression;
TestJvmModelInferrer:
def dispatch void infer(Program element, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) {
acceptor.accept(element.toClass(element.fullyQualifiedName)) [
documentation = element.documentation
if (element.variables !== null) {
for (decl : element.variables.decls) {
members += decl.toField(decl.name, decl.type) [
static = true
initializer = decl.right
visibility = JvmVisibility.PUBLIC
]
}
}
if (element.run !== null) {
members += element.run.toMethod('main', typeRef(Void::TYPE)) [
parameters += element.run.toParameter("args", typeRef(String).addArrayTypeDimension)
visibility = JvmVisibility.PUBLIC
static = true
body = element.run
]
}
]
}
Test case:
#Inject ParseHelper<Program> parseHelper
#Inject extension ValidationTestHelper
#Inject XbaseInterpreter interpreter
#Test
def void basicInterpret() {
val result = parseHelper.parse('''
program program1 {
variables {
int var1 = 0;
double var2 = 3.4;
}
var1 = 13
}
''')
result.assertNoErrors
var interpretResult = interpreter.evaluate(result.run)
println(interpretResult.result)
Partial stack trace:
java.lang.IllegalStateException: Could not access field: program1.var1 on instance: null
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._assignValueTo(XbaseInterpreter.java:1262)
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.assignValueTo(XbaseInterpreter.java:1221)
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:1213)
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:216)
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204)
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:190)
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:180)
The interpreter does only support expressions, but does not work with types that are created by a JvmModelInferrer. Your code tries to work with fields of such an inferred type.
Rather than using the interpreter, I'd recommend to use an InMemoryCompiler in your test. The domainmodel example may serve as an inspiration: https://github.com/eclipse/xtext-eclipse/blob/c2b15c3ec118c4c200e2b28ea72d8c9116fb6800/org.eclipse.xtext.xtext.ui.examples/projects/domainmodel/org.eclipse.xtext.example.domainmodel.tests/xtend-gen/org/eclipse/xtext/example/domainmodel/tests/XbaseIntegrationTest.java
You may find this project interesting, which (among other stuff) implements an interpreter for Xtend based on the Xbase interpreter. It might be a bit outdated, though, and also will not fully support all Xtend concepts. But it could be a starting point, and your contrbutions are welcome :-)
https://github.com/kbirken/xtendency

Trying to parse OpenCV YAML ouput with yaml-cpp

I've got a series of OpenCv generated YAML files and would like to parse them with yaml-cpp
I'm doing okay on simple stuff, but the matrix representation is proving difficult.
# Center of table
tableCenter: !!opencv-matrix
rows: 1
cols: 2
dt: f
data: [ 240, 240]
This should map into the vector
240
240
with type float. My code looks like:
#include "yaml.h"
#include <fstream>
#include <string>
struct Matrix {
int x;
};
void operator >> (const YAML::Node& node, Matrix& matrix) {
unsigned rows;
node["rows"] >> rows;
}
int main()
{
std::ifstream fin("monsters.yaml");
YAML::Parser parser(fin);
YAML::Node doc;
Matrix m;
doc["tableCenter"] >> m;
return 0;
}
But I get
terminate called after throwing an instance of 'YAML::BadDereference'
what(): yaml-cpp: error at line 0, column 0: bad dereference
Abort trap
I searched around for some documentation for yaml-cpp, but there doesn't seem to be any, aside from a short introductory example on parsing and emitting. Unfortunately, neither of these two help in this particular circumstance.
As I understand, the !! indicate that this is a user-defined type, but I don't see with yaml-cpp how to parse that.
You have to tell yaml-cpp how to parse this type. Since C++ isn't dynamically typed, it can't detect what data type you want and create it from scratch - you have to tell it directly. Tagging a node is really only for yourself, not for the parser (it'll just faithfully store it for you).
I'm not really sure how an OpenCV matrix is stored, but if it's something like this:
class Matrix {
public:
Matrix(unsigned r, unsigned c, const std::vector<float>& d): rows(r), cols(c), data(d) { /* init */ }
Matrix(const Matrix&) { /* copy */ }
~Matrix() { /* delete */ }
Matrix& operator = (const Matrix&) { /* assign */ }
private:
unsigned rows, cols;
std::vector<float> data;
};
then you can write something like
void operator >> (const YAML::Node& node, Matrix& matrix) {
unsigned rows, cols;
std::vector<float> data;
node["rows"] >> rows;
node["cols"] >> cols;
node["data"] >> data;
matrix = Matrix(rows, cols, data);
}
Edit It appears that you're ok up until here; but you're missing the step where the parser loads the information into the YAML::Node. Instead, se it like:
std::ifstream fin("monsters.yaml");
YAML::Parser parser(fin);
YAML::Node doc;
parser.GetNextDocument(doc); // <-- this line was missing!
Matrix m;
doc["tableCenter"] >> m;
Note: I'm guessing dt: f means "data type is float". If that's the case, it'll really depend on how the Matrix class handles this. If you have a different class for each data type (or a templated class), you'll have to read that field first, and then choose which type to instantiate. (If you know it'll always be float, that'll make your life easier, of course.)

...array<Object^>^ args

I'm reading C++/CLI. I see this stuff:
Object^ CreateInstanceFromTypename(String^ type, ...array<Object^>^ args)
{
if (!type)
throw gcnew ArgumentNullException("type");
Type^ t = Type::GetType(type);
if (!t)
throw gcnew ArgumentException("Invalid type name");
Object^ obj = Activator::CreateInstance(t, args);
return obj;
}
When calling it:
Object^ o = CreateInstanceFromTypename(
"System.Uri, System, Version=2.0.0.0, "
"Culture=neutral, PublicKeyToken=b77a5c561934e089",
"http://www.heege.net"
);
What is ...array^ args? If I remove ... ,there's a complied-error:
error C2665: 'CreateInstanceFromTypeName' : none of the 2 overloads could convert all the argument types
1> .\myFourthCPlus.cpp(12): could be 'System::Object ^CreateInstanceFromTypeName(System::String ^,cli::array<Type> ^)'
1> with
1> [
1> Type=System::Object ^
1> ]
1> while trying to match the argument list '(const char [86], const char [21])'
Like C++, C++/CLI has a mechanism for a variable amount of arguments. That is what the ... in front of the ...array<Object^>^ parameter means.
For type safety the C++/CLI designers added managed syntax to declare the type of the variable array.
Since it's just passing that parameter to the Activator::CreateInstance() function, I would look at what variable parameters the Activator function is looking for.