I was experimenting with C++17 feature std::optional
The optional return type is std::optional<std::pair<int, int>>. I call the
sum_pair function in print_answer function and wanted a optional print.
In print_answer function I wanted to check whether the required pair holds something to show.
like in the example given in: optional-returning factory functions are usable as conditions of while and if
Following is the code: here is it live with error
#include <iostream>
#include <vector>
#include <unordered_map>
#include <optional>
typedef std::optional<std::pair<int, int>> returnType;
// following algorithum works fine: just to show,
// how I have used the std::optional
returnType sum_pair(const std::vector<int>& vec, const int sum)
{
std::unordered_map<int, int> compIndexMap;
int index = 0;
for(const int& ele: vec)
{
if(auto check = compIndexMap.find(sum - ele); check != compIndexMap.cend())
return returnType{std::make_pair(check->second, index)};
compIndexMap.emplace(sum - ele, index);
++index;
}
return std::nullopt;
}
// problem is here:
void print_answer(const std::vector<int>& vec, const int sum)
{
// if I uncomment the if-else, everything works
/*if*/(auto Pair = sum_pair(vec, sum) )?
std::cout << "Resulting indexes are: " << Pair->first << " " << Pair->second << std::endl: //;
//else
std::cout << "Nothing found!\n";
}
int main()
{
std::vector<int> vec0{ 1,3,2,8 };
const int sum = 8;
print_answer(vec0, sum);
return 0;
}
When I use the if-else statement in the following format
(condion) ? print something: print something else;
I get the following two errors. (used GCC 7.1)
||=== Build: Debug in MyTestProgram (compiler: GNU GCC Compiler) ===|
|25|error: expected primary-expression before 'auto'|
|25|error: expected ')' before 'auto'|
Can somebody explain, why I need to use if-else, but not with "operator ?" ?
if(auto Pair = sum_pair(vec, sum) )
std::cout << "Resulting indexes are: " << Pair->first << " " << Pair->second << std::endl;
else
std::cout << "Nothing found!\n";
this is valid C++. You are allowed to put a declaration in the opening condition of an if clause.
(auto Pair = sum_pair(vec, sum) )?
std::cout << "Resulting indexes are: " << Pair->first << " " << Pair->second << std::endl
:
std::cout << "Nothing found!\n";
this is not valid C++. Declarations are not expressions. There are places where expressions are allowed, but declararions are not. The left hand side of ?, the trinary operator, is one of them.
Related
Here is my code:
bool isNotValid (char a) {
if (isalpha(a) || a == '_')
{
cout << "\n- isalpha";
return 0;
}
else
{
cout << "\n- notalpha";
return 1;
}
}
bool test123(const string& test)
{
return find_if(test.begin(), test.end(), isNotValid) != test.end();
}
int main()
{
string test;
cout << "Test input: ";
cin >> test;
if (!test123(test))
cout << "\n- Valid\n";
else
cout << "\n- Not Valid\n";
return 0;
}
This is part of my code to check the validity of username in my program. I don't really understand what exactly I am iterating through when I insert the string into my function as address of the string. CPP reference states that find_if iterates from first to last position of a sequence.
Poked through the code with cout at different location, still didn't quite catch what is going on.
You are iterating your string. You did not pass the address of the string. The function takes the string as a reference to const, meaning it passes the actual string (no copy is made) and the function is not allowed to modify the string. You are iterating character by character in your string and calling your function isNotValid() on each character.
Notes:
Instead of returning 1 or 0 from isNotValid(), return true or false.
Consider flipping your logic and renaming the function to isValid() instead. You would also have to change test123() to use std::find_if_not(). Finally, you would check if the returned iterator is end() and not if it's not.
But, if you do change isNotValid() to isValid(), you'd be better off switching from std::find_if() entirely to to std::all_of(). It makes more sense, is more readable, and returns a bool directly (No need to compare against end()).
But if you want to keep your function isNotValid(), the comment that suggests using std::any_of() is what I would recommend for the same reasons.
Here's my take on your code:
#include <algorithm>
#include <cctype>
#include <iostream>
#include <string>
bool isValid(char a) {
return std::isalpha(static_cast<unsigned char>(a)) || a == '_'; // !
}
bool test123(const std::string& test) {
return std::all_of(test.begin(), test.end(), isValid); // !
}
int main() {
std::string testOne{"i_am_valid"};
std::string testTwo{"i_am_invalid_123"};
std::cout << "Testing: " << testOne << " : " << std::boolalpha
<< test123(testOne) << '\n';
std::cout << "Testing: " << testTwo << " : " << std::boolalpha
<< test123(testTwo) << '\n';
}
Output:
❯ ./a.out
Testing: i_am_valid : true
Testing: i_am_invalid_123 : false
I would argue that readability has stayed largely the same, but the mental load has been shifted; the Boolean flips make a bit more sense.
As you progress in your learning, you might not even want to have the function isValid() if it's a one-off thing. C++11 introduced lambdas, or functions as objects. C++20 also introduced ranges, so you don't have to pass a pair of iterators if you intend to iterate the whole container anyway.
#include <algorithm>
#include <cctype>
#include <iostream>
#include <string>
bool test123(const std::string& test) {
return std::ranges::all_of(test, [](const auto& c) {
return std::isalpha(static_cast<unsigned char>(c)) || c == '_';
}); // !
}
int main() {
std::string testOne{"i_am_valid"};
std::string testTwo{"i_am_invalid_123"};
std::cout << "Testing: " << testOne << " : " << std::boolalpha
<< test123(testOne) << '\n';
std::cout << "Testing: " << testTwo << " : " << std::boolalpha
<< test123(testTwo) << '\n';
}
That's a bit hairy to read if you're not familiar with lambdas, but I find lambdas useful for checks like this where you're just doing it the one time.
I was experimenting with C++17 feature std::optional
The optional return type is std::optional<std::pair<int, int>>. I call the
sum_pair function in print_answer function and wanted a optional print.
In print_answer function I wanted to check whether the required pair holds something to show.
like in the example given in: optional-returning factory functions are usable as conditions of while and if
Following is the code: here is it live with error
#include <iostream>
#include <vector>
#include <unordered_map>
#include <optional>
typedef std::optional<std::pair<int, int>> returnType;
// following algorithum works fine: just to show,
// how I have used the std::optional
returnType sum_pair(const std::vector<int>& vec, const int sum)
{
std::unordered_map<int, int> compIndexMap;
int index = 0;
for(const int& ele: vec)
{
if(auto check = compIndexMap.find(sum - ele); check != compIndexMap.cend())
return returnType{std::make_pair(check->second, index)};
compIndexMap.emplace(sum - ele, index);
++index;
}
return std::nullopt;
}
// problem is here:
void print_answer(const std::vector<int>& vec, const int sum)
{
// if I uncomment the if-else, everything works
/*if*/(auto Pair = sum_pair(vec, sum) )?
std::cout << "Resulting indexes are: " << Pair->first << " " << Pair->second << std::endl: //;
//else
std::cout << "Nothing found!\n";
}
int main()
{
std::vector<int> vec0{ 1,3,2,8 };
const int sum = 8;
print_answer(vec0, sum);
return 0;
}
When I use the if-else statement in the following format
(condion) ? print something: print something else;
I get the following two errors. (used GCC 7.1)
||=== Build: Debug in MyTestProgram (compiler: GNU GCC Compiler) ===|
|25|error: expected primary-expression before 'auto'|
|25|error: expected ')' before 'auto'|
Can somebody explain, why I need to use if-else, but not with "operator ?" ?
if(auto Pair = sum_pair(vec, sum) )
std::cout << "Resulting indexes are: " << Pair->first << " " << Pair->second << std::endl;
else
std::cout << "Nothing found!\n";
this is valid C++. You are allowed to put a declaration in the opening condition of an if clause.
(auto Pair = sum_pair(vec, sum) )?
std::cout << "Resulting indexes are: " << Pair->first << " " << Pair->second << std::endl
:
std::cout << "Nothing found!\n";
this is not valid C++. Declarations are not expressions. There are places where expressions are allowed, but declararions are not. The left hand side of ?, the trinary operator, is one of them.
During my work, it would be a requirement for me to use Eigen::VectorXcd as state type, to solve a huge linear ODE system. In that project, the matrix in the ODE system is sparse. Multiplying a it with a dense vector can be computed in parallel in a simple way using Eigen. However, I have faced some problems in that situation that I will tell in details below. Currently I am applying a not-so efficient solution, namely, I use arma::cx_vec as state type, and declare the matrix corresponding the ode arma::cx_mat, which is a dense matrix type. Unfortunately, sparse-matrix-dense vector multiplication cannot be parallelized with Armadillo since it supports only the single-threaded version of SuperLU. According to my experience, in my specific case, sparse matrix-dense vector multiplication is three times faster than dense matrix-dense vector multiplication. Since a single running lasts for tens or hundreds of hours even when using 20 CPU cores simultaneously, it would be highly required for me to be able to use the Eigen library along with boost::odeint to gain a factor of three in runtime.
For testing purposes, I tried to use Eigen::VectorXd as a state type. I have also found very similar situations, see: Using Boost::odeint with Eigen::Matrix as a state vector or using several eigen matrices as statetypes in boost/odeint. However, none of the solutions worked for me. In my real project, I have to use the Bulirsch-Stoer method to integrate my ODE system since high precision and therefore error control is necessary for me. Below I will post my test code, corresponding to Eigen::VectorXd state type. As you can see, for first, I followed the steps described in the boost::odeint documentation: I defined my state vector resizeable and overloaded the mathematical operators used by the vector space algebra of the controlled stepper. After these steps, the code still did not compile, overloading of the addition operator for adding vectors to scalars was also necessary. Here is my code:
#include <iostream>
#include <vector>
#include <complex>
#include <Eigen/Eigen>
#include <boost/numeric/odeint.hpp>
namespace boost {
namespace numeric {
namespace odeint {
template<>
struct is_resizeable<Eigen::VectorXd>
{
typedef boost::true_type type;
static const bool value = type::value;
};
template<>
struct same_size_impl<Eigen::VectorXd, Eigen::VectorXd>
{
static bool same_size(const Eigen::VectorXd& v1, const Eigen::VectorXd& v2)
{
std::cout << "same_size: " << v1.transpose() << " and " << v2.transpose() << std::endl;
std::cout << "same_size: " << v1.rows() << " == " << v2.rows() << std::endl;
return v1.rows() == v2.rows();
}
};
template<>
struct resize_impl<Eigen::VectorXd, Eigen::VectorXd>
{
static void resize(Eigen::VectorXd& v1, const Eigen::VectorXd& v2)
{
std::cout << "resize_impl: " << v1.transpose() << " and " << v2.transpose() << std::endl;
std::cout << "resize_impl: (" << v1.rows() << " x " << v1.cols() << ") to (" << v2.rows() << " x " << v2.cols() << ")" << std::endl;
v1.conservativeResize(v2.size());
std::cout << "resize_impl: new size: " << v1.rows() << " x " << v1.cols() << std::endl;
}
};
template<>
struct vector_space_norm_inf<Eigen::VectorXd>
{
typedef double result_type;
double operator()(const Eigen::VectorXd& v) const
{
std::cout << "vector_space_norm_inf(" << v.transpose() << ") = " << v.lpNorm<Eigen::Infinity>() << std::endl;
return v.lpNorm<Eigen::Infinity>();
}
};
}
}
}
namespace Eigen {
//VectorXd operator*(const double& a, const VectorXd& v)
//{
// std::cout << a << "*(" << v.transpose() << ") = " << a*(v.transpose()) << std::endl;
// return a*v;
//}
//
//VectorXd operator*(const VectorXd& v, const double& a)
//{
// std::cout << "(" << v.transpose() << ")*" << v.transpose() << " = " << a*(v.transpose()) << std::endl;
// return a*v;
//}
//
//VectorXd& operator*=(const double& a, VectorXd& v)
//{
// std::cout << "operator*=(const double& a, VectorXd& v): (" << v.transpose() << ") *= " << a << ": ";
// v *= a;
// std::cout << v.transpose() << std::endl;
// return v;
//}
//
//VectorXd& operator*=(VectorXd& v, const double& a)
//{
// std::cout << "operator*=(VectorXd& v, const double& a): (" << v.transpose() << ") *= " << a << ": ";
// v *= a;
// std::cout << v.transpose() << std::endl;
// return v;
//}
//
//VectorXd& operator+=(VectorXd& v1, const VectorXd& v2)
//{
// std::cout << "(" << v1.transpose() << ") += (" << v2.transpose() << "): ";
// v1 += v2;
// std::cout << v1.transpose() << std::endl;
// return v1;
//}
VectorXd operator/(const VectorXd& v1, const VectorXd& v2)
{
const char* sizeeq;
if(v1.size() == v2.size())
sizeeq = "True";
else
sizeeq = "FALSE!";
std::cout << "operator/: size check: " << v1.size() << " ?= " << v2.size() << ": " << sizeeq << std::endl;
VectorXd result(v1.cwiseQuotient(v2));
std::cout << "(" << v1.transpose() << ") / (" << v2.transpose() << ") = " << result.transpose() << std::endl;
return result;
}
VectorXd operator+(double d, const VectorXd& v)
{
std::cout << d << " + (" << v.transpose() << ") = " << d+v.transpose() << std::endl;
return d+v;
}
VectorXd operator+(const VectorXd& v, double d)
{
std::cout << "(" << v.transpose() << ") + " << d << " = " << d+v.transpose() << std::endl;
return d+v;
}
VectorXd abs(const VectorXd& v)
{
std::cout << "|(" << v.transpose() << ")| = " << v.cwiseAbs().transpose() << std::endl;
return v.cwiseAbs();
}
}
typedef Eigen::VectorXd state_type;
typedef Eigen::MatrixXd coeff_matrix_type;
class lin_diff_eq
{
public:
lin_diff_eq(coeff_matrix_type gam): m_gam(gam) {}
coeff_matrix_type get_matrix() const { return m_gam; }
void operator()(const state_type& x, state_type& dxdt, const double t)
{
std::cout << "size: " << x.size() << std::endl;
std::cout << x.transpose() << std::endl;
dxdt = m_gam*x;
}
private:
coeff_matrix_type m_gam;
};
using namespace boost::numeric::odeint;
int main()
{
coeff_matrix_type A = coeff_matrix_type(2,2);
state_type x = state_type(2);
double epsabs = 1e-4;
double epsrel = 1e-4;
A << 0, 1,
-2, 0;
x << 1, 0;
lin_diff_eq lde(A);
std::cout << lde.get_matrix() << std::endl;
integrate_adaptive(bulirsch_stoer<state_type, double, state_type, double, vector_space_algebra>(epsabs, epsrel), lin_diff_eq(A), x, 0.0, 1.0, 0.1);
}
All these steps made my code compile, however, during runtime my program crashes with segmentation fault. As you can see, I overloaded all the operators such that they produce some kind of a very simple log, I couldn't figure out how to produce a more detailed log to locate exactly where and why the segmentation fault occurs. According to the boost::odeint documentation, this can happen during runtime when the vector space algebra doesn't know how to resize the vectors, but I already defined how to do it. On my computer, it can be seen that boost::odeint generates some internal states, resizes them correctly, computes elementwise abs() correctly, but after that it crashes.
The next thing I tried was to use the support to Eigen provided by the boost::odeint developers. In this case, I did not use any of the template specializations and the operator overloads provided above, but instead, I included
boost/numeric/odeint/external/eigen/eigen_algebra.hpp
after including odeint.hpp. The code did not compile, I got the following error messages:
/usr/local/include/boost/numeric/odeint/external/eigen/eigen_algebra.hpp|35|error: ‘scalar_add_op’ in namespace ‘Eigen::internal’ does not name a type|
/usr/local/include/boost/numeric/odeint/external/eigen/eigen_algebra.hpp|35|error: expected template-argument before ‘<’ token|
/usr/local/include/boost/numeric/odeint/external/eigen/eigen_algebra.hpp|35|error: expected ‘>’ before ‘<’ token|
/usr/local/include/boost/numeric/odeint/external/eigen/eigen_algebra.hpp|37|error: wrong number of template arguments (1, should be 2)|
/usr/local/include/Eigen/src/Core/util/ForwardDeclarations.h|91|error: provided for ‘template<class UnaryOp, class MatrixType> class Eigen::CwiseUnaryOp’|
/usr/local/include/boost/numeric/odeint/external/eigen/eigen_algebra.hpp|38|error: expected ‘::’ before ‘operator’|
/usr/local/include/boost/numeric/odeint/external/eigen/eigen_algebra.hpp|38|error: expected identifier before ‘operator’|
/usr/local/include/boost/numeric/odeint/external/eigen/eigen_algebra.hpp||In function ‘const int Eigen::operator+(const Eigen::MatrixBase<Derived>&, const typename Eigen::internal::traits<T>::Scalar&)’:|
/usr/local/include/boost/numeric/odeint/external/eigen/eigen_algebra.hpp|41|error: ‘scalar_add_op’ in namespace ‘Eigen::internal’ does not name a type|
/usr/local/include/boost/numeric/odeint/external/eigen/eigen_algebra.hpp|41|error: expected template-argument before ‘<’ token|
/usr/local/include/boost/numeric/odeint/external/eigen/eigen_algebra.hpp|41|error: expected ‘>’ before ‘<’ token|
/usr/local/include/boost/numeric/odeint/external/eigen/eigen_algebra.hpp|43|error: wrong number of template arguments (1, should be 2)|
/usr/local/include/Eigen/src/Core/util/ForwardDeclarations.h|91|error: provided for ‘template<class UnaryOp, class MatrixType> class Eigen::CwiseUnaryOp’|
/usr/local/include/boost/numeric/odeint/external/eigen/eigen_algebra.hpp|43|error: expected ‘::’ before ‘(’ token|
/usr/local/include/boost/numeric/odeint/external/eigen/eigen_algebra.hpp|43|error: expected identifier before ‘(’ token|
/usr/local/include/boost/numeric/odeint/external/eigen/eigen_algebra.hpp|43|error: ‘scalar_add_op’ is not a member of ‘Eigen::internal’|
/usr/local/include/boost/numeric/odeint/external/eigen/eigen_algebra.hpp|44|error: expected ‘(’ before ‘>’ token|
/usr/local/include/boost/numeric/odeint/external/eigen/eigen_algebra.hpp|50|error: ‘scalar_add_op’ in namespace ‘Eigen::internal’ does not name a type|
/usr/local/include/boost/numeric/odeint/external/eigen/eigen_algebra.hpp|50|error: expected template-argument before ‘<’ token|
/usr/local/include/boost/numeric/odeint/external/eigen/eigen_algebra.hpp|50|error: expected ‘>’ before ‘<’ token|
/usr/local/include/boost/numeric/odeint/external/eigen/eigen_algebra.hpp|52|error: wrong number of template arguments (1, should be 2)|
/usr/local/include/Eigen/src/Core/util/ForwardDeclarations.h|91|error: provided for ‘template<class UnaryOp, class MatrixType> class Eigen::CwiseUnaryOp’|
/usr/local/include/boost/numeric/odeint/external/eigen/eigen_algebra.hpp|53|error: expected ‘::’ before ‘operator’|
/usr/local/include/boost/numeric/odeint/external/eigen/eigen_algebra.hpp|53|error: expected identifier before ‘operator’|
/usr/local/include/boost/numeric/odeint/external/eigen/eigen_algebra.hpp||In function ‘const int Eigen::operator+(const typename Eigen::internal::traits<T>::Scalar&, const Eigen::MatrixBase<Derived>&)’:|
/usr/local/include/boost/numeric/odeint/external/eigen/eigen_algebra.hpp|56|error: ‘scalar_add_op’ in namespace ‘Eigen::internal’ does not name a type|
/usr/local/include/boost/numeric/odeint/external/eigen/eigen_algebra.hpp|56|error: expected template-argument before ‘<’ token|
/usr/local/include/boost/numeric/odeint/external/eigen/eigen_algebra.hpp|56|error: expected ‘>’ before ‘<’ token|
/usr/local/include/boost/numeric/odeint/external/eigen/eigen_algebra.hpp|58|error: wrong number of template arguments (1, should be 2)|
/usr/local/include/Eigen/src/Core/util/ForwardDeclarations.h|91|error: provided for ‘template<class UnaryOp, class MatrixType> class Eigen::CwiseUnaryOp’|
/usr/local/include/boost/numeric/odeint/external/eigen/eigen_algebra.hpp|58|error: expected ‘::’ before ‘(’ token|
/usr/local/include/boost/numeric/odeint/external/eigen/eigen_algebra.hpp|58|error: expected identifier before ‘(’ token|
/usr/local/include/boost/numeric/odeint/external/eigen/eigen_algebra.hpp|58|error: ‘scalar_add_op’ is not a member of ‘Eigen::internal’|
/usr/local/include/boost/numeric/odeint/external/eigen/eigen_algebra.hpp|59|error: expected ‘(’ before ‘>’ token|
||=== Build failed: 32 error(s), 0 warning(s) (0 minute(s), 7 second(s)) ===|
I also tried to include
boost/numeric/odeint/external/eigen/eigen.hpp
instead of eigen_algebra.hpp and received the same error messages when including eigen_algebra.hpp. It seems like Boost::odeint v1.65.1 is not compatible with Eigen v3.3.4. My question is if there is an (un)official header file that provides a support of Eigen 3.3.4 to Boost::odeint 1.65.1, or did I make something wrong during template specializations and/or operator overloading for the vector space algebra?
I also found a solution for "typecasting" std std::vector<> to Eigen::VectorXd
and vice versa, but I think that using std::vector as a state stype, and then internally "casting it" to Eigen::VectorXd, then "casting the result back" to std::vector<> would result in a lower performance than that of mentioned in the introduction since my vectors are huge and ten tousands or more accepted steps are required to perform an integration. I hope my question was accurate enough and the community can kindly help me solving my problem.
I have a function which "zips" multiple containers for range based foor loop iteration. (taken from a another Stack Overflow post)
template<class... Conts>
auto zip_range(Conts&... conts)
-> decltype(boost::make_iterator_range(boost::make_zip_iterator(boost::make_tuple(conts.begin()...)),
boost::make_zip_iterator(boost::make_tuple(conts.end()...)))) {
return {boost::make_zip_iterator(boost::make_tuple(conts.begin()...)),
boost::make_zip_iterator(boost::make_tuple(conts.end()...))};
}
I can use this function like so:
std::vector<int> x({1,2,3});
std::vector<int> y({4,5,6});
for ( auto const& entries : zip_range(x,y) ) {
std::cout << entries.get<0>() << " " << entries.get<1>() << std::endl;
}
And get expected behavior:
1 4
2 5
3 6
If I try to define a function which takes two std::initizalizer_list arguments, passes them to a vector, and tries to loop:
template <class T1, class T2>
void foo(const std::initializer_list<T1>& il1,
const std::initializer_list<T2>& il2) {
std::vector<T1> v1(il1);
std::vector<T2> v2(il2);
for ( auto const& zipitr : zip_range(v1,v2) ) {
std::cout << zipitr.get<0>() << " " << zipitr.get<1>() << std::endl;
}
}
I get a compilation error that isn't very helpful:
testing.cpp: In function ‘void foo(const std::initializer_list<_Tp>&, const std::initializer_list<T2>&)’:
testing.cpp:34:32: error: expected primary-expression before ‘)’ token
std::cout << zipitr.get<0>() << " " << zipitr.get<1>() << std::endl;
^
testing.cpp:34:58: error: expected primary-expression before ‘)’ token
std::cout << zipitr.get<0>() << " " << zipitr.get<1>() << std::endl;
Why am I unable to do this? (much less go straight to giving the initializer lists to the function zip_range)
The type of zipitr is dependent on T1 and T2 and that type has a nested member function template get. So to tell the compiler get is a dependent function template, you need to add the template keyword immediately before it.
std::cout << zipitr.template get<0>() << " " << zipitr.template get<1>() << std::endl;
// ^^^^^^^^ ^^^^^^^^
Read this answer for details of why this is needed.
An alternative that avoids the template keyword is to use the boost::get free function.
std::cout << boost::get<0>(zipitr) << " " << boost::get<1>(zipitr) << std::endl;
Your zip_range function can be simplified (or replaced) using boost::combine
#include <boost/range/combine.hpp>
template<class... Conts>
auto zip_range(Conts&... conts)
-> decltype( boost::combine(conts...) )
{
return boost::combine(conts...);
}
Live demo
The reason you can't pass {1,2,3} directly to zip_range is that a braced-init-list is not an expression, so it doesn't have a type, which means template argument deduction cannot work.
auto has a special rule to deal with them that allows it to deduce the type as std::initializer_list<T>, so the following works:
auto l1 = {1,2,3};
auto l2 = {4,5,6};
for ( auto const& zipitr : zip_range(l1,l2) ) {
std::cout << boost::get<0>(zipitr) << " " << boost::get<1>(zipitr) << std::endl;
}
Live demo
I am using boost spirit to parse some text files into a data structure and now I am beginning to generate text from this data structure (using spirit karma).
One attempt at a data structure is a boost::fusion::map (as suggested in an answer to
this question). But although I can use boost::spirit::qi::parse() and get data in it easily, when I tried to generate text from it using karma, I failed.
Below is my attempt (look especially at the "map_data" type). After some reading and playing around with other fusion types, I found boost::fusion::vector and BOOST_FUSION_DEFINE_ASSOC_STRUCT. I succeeded to generate output with both of them, but they don't seem ideal: in vector you cannot access a member using a name (it is like a tuple) -- and in the other solution, I don't think I need both ways (member name and key type) to access the members.
#include <iostream>
#include <string>
#include <boost/spirit/include/karma.hpp>
#include <boost/fusion/include/map.hpp>
#include <boost/fusion/include/make_map.hpp>
#include <boost/fusion/include/vector.hpp>
#include <boost/fusion/include/as_vector.hpp>
#include <boost/fusion/include/transform.hpp>
struct sb_key;
struct id_key;
using boost::fusion::pair;
typedef boost::fusion::map
< pair<sb_key, int>
, pair<id_key, unsigned long>
> map_data;
typedef boost::fusion::vector < int, unsigned long > vector_data;
#include <boost/fusion/include/define_assoc_struct.hpp>
BOOST_FUSION_DEFINE_ASSOC_STRUCT(
(), assocstruct_data,
(int, a, sb_key)
(unsigned long, b, id_key))
namespace karma = boost::spirit::karma;
template <typename X>
std::string to_string ( const X& data )
{
std::string generated;
std::back_insert_iterator<std::string> sink(generated);
karma::generate_delimited ( sink, karma::int_ << karma::ulong_, karma::space, data );
return generated;
}
int main()
{
map_data d1(boost::fusion::make_map<sb_key, id_key>(234, 35314988526ul));
vector_data d2(boost::fusion::make_vector(234, 35314988526ul));
assocstruct_data d3(234,35314988526ul);
std::cout << "map_data as_vector: " << boost::fusion::as_vector(d1) << std::endl;
//std::cout << "map_data to_string: " << to_string(d1) << std::endl; //*FAIL No 1*
std::cout << "at_key (sb_key): " << boost::fusion::at_key<sb_key>(d1) << boost::fusion::at_c<0>(d1) << std::endl << std::endl;
std::cout << "vector_data: " << d2 << std::endl;
std::cout << "vector_data to_string: " << to_string(d2) << std::endl << std::endl;
std::cout << "assoc_struct as_vector: " << boost::fusion::as_vector(d3) << std::endl;
std::cout << "assoc_struct to_string: " << to_string(d3) << std::endl;
std::cout << "at_key (sb_key): " << boost::fusion::at_key<sb_key>(d3) << d3.a << boost::fusion::at_c<0>(d3) << std::endl;
return 0;
}
Including the commented line gives lots of pages of compilation errors, among which notably something like:
no known conversion for argument 1 from ‘boost::fusion::pair’ to ‘double’
no known conversion for argument 1 from ‘boost::fusion::pair’ to ‘float’
Might it be that to_string needs the values of the map_data, and not the pairs? Though I am not good with templates, I tried to get a vector from a map using transform in the following way
template <typename P>
struct take_second
{
typename P::second_type operator() (P p)
{
return p.second;
}
};
// ... inside main()
pair <char, int> ff(32);
std::cout << "take_second (expect 32): "
<< take_second<pair<char,int>>()(ff) << std::endl;
std::cout << "transform map_data and to_string: "
<< to_string(boost::fusion::transform(d1, take_second<>())); //*FAIL No 2*
But I don't know what types am I supposed to give when instantiating take_second and anyway I think there must be an easier way to get (iterate over) the values of a map (is there?)
If you answer this question, please also give your opinion on whether using an ASSOC_STRUCT or a map is better.
I think I noticed your question on the [spirit-general] list earlier.
It got 14 views there - and I did some fairly deep investigation. Sadly, to the best of my knowledge I don't think Spirit has any support for associate Fusion structures.
In fact, outside MSM and Phoenix, there was no place in boost where I see fusion::map being used.
Is there a chance you could just use std::map/std::pair instead? Here's a tiny proof of concept:
#include <boost/spirit/include/karma.hpp>
#include <boost/fusion/adapted.hpp>
int main()
{
const auto data = std::map<std::string, double> {
{ "pi", 3.1415925 },
{ "e", 2.718281828 },
{ "Answer", 42 } };
namespace karma = boost::spirit::karma;
std::cout << karma::format((karma::string << " = " << karma::double_) % karma::eol, data)
<< std::endl;
}
Output:
Answer = 42.0
e = 2.718
pi = 3.142