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
Related
I want to create an Eigen::Replicate object that can be accessed like a vector, i.e. with a single index. I got that to work with the fixed-size replicate<Index,Index>(), which I can't use in reality, the non-one factor is not a compile-time constant. It also works when manually creating a Replicate object, but I feel like I'm just overlooking the obvious way of using a replicate function to achieve this:
#include <Eigen/Dense>
#include <iostream>
using namespace Eigen;
int main(){
Vector3i v (3);
v << 0,1,2;
constexpr int nReplications {2};
auto replDynamic { v.replicate(nReplications, 1) };
/* with a dynamic replication, two indexes are required to access a coeff */
std::cout << "5th entry: " << replDynamic(4,0) << '\n';
auto replFixed { v.replicate<nReplications, 1>() };
/* I want to use only one index, but I require the number of replications
* in one dimension to be dynamic */
std::cout << "5th entry: " << replFixed(4) << '\n';
/* don't know how to access the VectorwiseOp variant */
// auto replVector { v.replicate(nReplications) };
// std::cout << "5th entry: " << replVector(4) << '\n';
/* this function doesn't exist */
// auto replDefined { v.replicate<Dynamic,1>(nReplications, 1) };
// std::cout << "5th entry: " << replDefined(4) << '\n';
/* I'd rather not define it manually (it's not the intended way), but it works */
Replicate<Vector3i,Dynamic,1> replManual { v, nReplications, 1 };
std::cout << "5th entry: " << replManual(4) << '\n';
return 0;
}
The source code shows VectorwiseOp<...>::replicate(Index factor) in line 134, which sounds like what I need, but I don't seem to be able to access it.
And a function such as replicate<Index,Index>(Index,Index) doesn't exist.
Assuming I understand what you are asking, since a Vector3i is a one column Eigen::Matrix, you can get a VectorwiseOp<...> expression template from a Vector3i (say) by using the colwise() function and then call the one argument replicate with that.
That is,
#include <Eigen/Dense>
#include <iostream>
using namespace Eigen;
int main() {
Vector3i v(3);
v << 0, 1, 2;
auto foo = v.colwise().replicate(2);
std::cout << "5th entry: " << foo(4) << '\n';
return 0;
}
Note though that using type deduction on a expression template, or "pseudo expression" as they are called in the Eigen documentation, is generally a bad idea i.e. writing Eigen::Matrix<int, 6, 1> foo = v.colwise().replicate(2) is safer; the Eigen documentation mentions the issue here.
By adding a .reshaped() after the replicate(...) call, the ColsAtCompileTime are set to 1, and therefore, the resulting object can be accessed like a vector:
#include <Eigen/Dense>
#include <iostream>
using namespace Eigen;
int main(){
Vector3i v (3);
v << 0,1,2;
constexpr int nReplications {2};
auto replReshaped { v.replicate(nReplications, 1).reshaped() };
std::cout << "5th entry: " << replReshaped(4) << '\n';
return 0;
}
I found this code on cppreference.com. I was wondering if boost provides a similar function for its variant type. I found the boost documentation really awful and can't find anything.
int main()
{
std::variant<int, std::string> v = "abc";
std::cout << std::boolalpha
<< "variant holds int? "
<< std::holds_alternative<int>(v) << '\n'
<< "variant holds string? "
<< std::holds_alternative<std::string>(v) << '\n';
}
Although not exactly the same, you can use the pointer based get function:
boost::variant<int, std::string> v = "abc";
std::cout << std::boolalpha
<< "variant holds int? "
<< (boost::get<int>(&v) != nullptr) << '\n'
<< "variant holds string? "
<< (boost::get<std::string>(&v) != nullptr) << '\n';
You can create a simple wrapper that will work just like the standard one. Use the fact that boost::get has multiple overloads and when passed a pointer, it will also return a (possibly null) pointer.
template <typename T, typename... Ts>
bool holds_alternative(const boost::variant<Ts...>& v) noexcept
{
return boost::get<T>(&v) != nullptr;
}
It will be also picked up by ADL, so it doesn't matter much where you put it.
No but, you can use the type() method:
#include <iostream>
#include <boost/variant.hpp>
int main()
{
boost::variant<int, std::string> v = "abc";
std::cout << std::boolalpha
<< "variant holds int? "
<< (v.type() == typeid(int)) << '\n'
<< "variant holds string? "
<< (v.type() == typeid(std::string)) << '\n';
}
But it will not protect you against having the same type twice (boost::variant<int, int, std::string>) as std::holds_alternative would do.
In C++ macros you can use #var to get the literal string of the argument passed:
#define PRINT_SIZE(type) \
(std::cout << sizeof(type) << " " << #type << std::endl)
Using this macro, I can write a very simple program which will give me the lengths of specific types on my machine:
PRINT_SIZE(bool);
PRINT_SIZE(char);
…
This does work but I would like to use C++ templates instead. Obtaining the size is easy with the following template function:
template <typename T>
void print_size() {
std::cout << sizeof(T) << std::endl;
}
I can call this function with any type and it will output the size:
print_size<bool>();
print_size<char>();
…
Is there any way I could get a literal "bool" from this anywhere such that the output would be as nice as the one with macros?
It can sortof be done using RTTI (runtime type inference) using typeid:
#include <iostream>
#include <typeinfo>
template <typename T>
void print_size() {
T a;
std::cout << typeid(a).name() << ": " << sizeof(T) << std::endl;
}
int main(){
print_size<bool>();
print_size<char>();
print_size<long>();
return 0;
}
This outputs:
b: 1
c: 1
l: 8
You can use typeid() as in:
int i;
cout << typeid(i).name(); // Most likely will print int but g++ prints i
cout << typeid(int).name(); // Also most likely will print int but g++ prints i
Note that the name it returns depends on the compiler and may be some unusual form of the type. More information here.
Edit:
g++ (4.8) prints only the first letter of fundamental types. For user defined classes, it prints the number of characters in the name followed by the name. Ex:
#include <iostream>
#include <typeinfo>
class myclass
{
myclass() {}
};
class my
{
my() {}
};
int main()
{
std::cout << typeid(int).name() << std::endl;
std::cout << typeid(bool).name() << std::endl;
std::cout << typeid(myclass).name() << std::endl;
std::cout << typeid(my).name() << std::endl;
}
Produces the following output:
i
b
7myclass
2my
I just want to iterate through the members of an unordered map.
There are many simple examples on the web, including on this site, and yet none of them will compile. Apparently some examples are from a previous non-standard STL version, some are just old, and some are so new that my gcc 4.7.2 can't handle them. Please do not suggest the new auto iterator from C++11. I will get there some day when all my libraries are validated for that. Until then, I just want the old one to work. (see below for what I have tried)
Here is my test code:
#include <iostream>
#include <boost/unordered_map.hpp>
#include <string>
int main(int argc,char *argv[]) {
boost::unordered::unordered_map<std::string,int> umap;
//can't get gcc to accept the value_type()...
//umap.insert(boost::unordered_map::value_type("alpha",1));
//umap.insert(boost::unordered_map::value_type("beta",2));
//umap.insert(boost::unordered_map::value_type("gamma",3));
umap["alpha"]=1; //this works
umap["beta"]=2;
umap["gamma"]=3;
//can't get gcc to compile the iterator
//for (boost::unordered_map::iterator it=umap.begin();it!=umap.end();++it)
// std::cout << it->first <<", " << it->second << std::endl;
//gcc does not like it this way either
//for (int x=0;x<umap.size();x++)
// std::cout << x << " : " << umap[x].first << " = " << umap[x].second << std::endl;
//will gcc take this? No it does not
//for (int x=0;x<umap.size();x++)
// std::cout << x << " : " << umap[x] << std::endl;
//this does not work
//boost::unordered::unordered_map::iterator<std::string,int> it;
//this does not work
//boost::unordered::unordered_map::iterator it;
//for (it=umap.begin();it!=umap.end();++it)
// std::cout << it->first <<", " << it->second << std::endl;
//this does not work
//BOOST_FOREACH(boost::unordered_map::value_type value, umap) {
// std::cout << value.second;
// }
//std::cout << std::endl;
//this does not work either
//BOOST_FOREACH(boost::unordered_map::value_type<std::string,int> value, umap) {
// std::cout << value.second;
// }
//std::cout << std::endl;
std::cout << "umap size: " << umap.size() << std::endl;
std::cout << "umap max size: " << umap.max_size() << std::endl;
std::cout << "find alpha: " << (umap.find("alpha")!=umap.end()) << std::endl;
std::cout << "count beta: " << umap.count("beta") << std::endl;
}
Most of the errors are a variation of this:
error: 'template<class K, class T, class H, class P, class A> class boost::unordered::unordered_map' used without template parameters
Here is my build command:
g++ -I..\boost umap.cpp
I should be embarrassed for getting stuck on such a beginner's question, but from the volume of similar questions I am finding, this is just hard enough to stop a lot of people in their tracks. I have written hash containers before (back when it was recommended NOT to use STL) and I am very tempted to just write my own... but the right thing to do is learn to use as many existing tools as possible... help!
I've looked at the following questions on stackoverflow where I haven't found an answer:
iterate through unordered_map using boost_foreach
I tried:
BOOST_FOREACH(boost::unordered_map::value_type& value, umap) {
but it gives the same error I show below.
Unordered_map iterator invalidation
This one is close, but not quite my issue:
Iterator invalidation in boost::unordered_map
This one uses auto
and I can't switch compilers at this time.
C++ some questions on boost::unordered_map & boost::hash
This one is mostly about the theory of maps:
how to use boost::unordered_map
This is a rather complicated example, but you will see in my code I am already trying to declare iterators... they just won't compile.
How to use BOOST_FOREACH with an Unordered_map?
This is a nice example, but
it just does not compile. I tried a version of this in my code.
IT WORKS !
Here is the working code:
#include <iostream>
#include <boost/unordered_map.hpp>
#include <string>
int main(int argc,char *argv[]) {
boost::unordered::unordered_map<std::string,int> umap;
umap["alpha"]=1;
umap["beta"]=2;
umap["gamma"]=3;
boost::unordered::unordered_map<std::string,int>::iterator it;
for (it=umap.begin();it!=umap.end();++it)
std::cout << it->first <<", " << it->second << std::endl;
std::cout << "umap size: " << umap.size() << std::endl;
std::cout << "umap max size: " << umap.max_size() << std::endl;
std::cout << "find alpha: " << (umap.find("alpha")!=umap.end()) << std::endl;
std::cout << "count beta: " << umap.count("beta") << std::endl;
}
It was a syntax error. I was putting the type in the wrong place when declaring the iterator.
Thanks everyone for your responses.
try changing boost::unordered::unordered_map::iterator it; it to boost::unordered::unordered_map<std::string,int>::iterator it;
NOTE:
It is also possible, and a good idea in more complex situations, to create a typedef of it, such as typedef boost::unordered::unordered_map<std::string,int>::iterator UMapStringIntIt;, or whatever you may call it.
The answer is in the question, but the simple solution is here for your convenience:
#include <iostream>
#include <boost/unordered_map.hpp>
#include <string>
int main(int argc,char *argv[])
{
boost::unordered::unordered_map<std::string,int> umap;
umap["alpha"]=1;
umap["beta"]=2;
umap["gamma"]=3;
for ( auto it= umap.begin(); it != umap.end(); ++it )
std::cout << it->first <<", " << it->second << std::endl;
}
I want to just hard code these values into a table. when I try to use 2D arrays, I run into the problem of dealing with characters and integers. When I do a struct I have this so far but it doesn't divide the information up in columns, and I'm not sure how to format it that way. (I only did 3 rows to start off with, if I get them working, the rest will just be the same)
#include <iostream>
#include <string>
#include <iomanip>
using namespace std;
typedef struct table
{
std::string game;
int year;
float rating;
int num_voters;
}t;
void processTab(t*);
int main()
{
t tabl[2] = {0,0};
int i;
processTab(tabl);
for(i=0; i<2; i++)
{
std::cout << "Game: " << setw(20) << tabl[i].game;
std::cout << "\tYear: " << setw(4) << tabl[i].year;
std::cout << "\tRating: " << fixed << setprecision(2) << tabl[i].rating;
std::cout << "\tVoters: " << setw(6) << tabl[i].num_voters;
}
system("pause");
return 0;
}
void processTab(t*tab)
{
(tab[0].game, "twilight struggles");
tab[0].year = 2005;
tab[0].rating = 8.226;
tab[0].num_voters = 10690;
(tab[1].game, "Agricloa");
tab[1].year = 2007;
tab[1].rating = 8.17;
tab[1].num_voters = 23738;
(tab[2].game, "Puerto Rico");
tab[2].year = 2002;
tab[2].rating = 8.163;
tab[2].num_voters = 27433;
}
Table Data:
Game (0) Year (1) Rating (2) Num Voters (3)
Twilight Struggle 2005 8.226 10690
Agricola 2007 8.17 23738
Puerto Rico 2002 8.163 27433
Through the Ages 2006 8.153 8137
Power Grid 2004 8.02 21655
Le Havre 2008 7.972 9258
Eclipse 2011 7.968 3194
Brass 2007 7.911 5814
Dominion: Intrigue 2009 7.895 10889
Caylus 2005 7.878 13878
What I think you are looking for is <iomanip>
#include <iomanip>
std::cout << "Game: " << setw(20) << tabl[i].game;
std::cout << "\tYear: " << setw(4) << tabl[i].year;
std::cout << "\tRating: " << fixed << setprecision(3) << tabl[i].rating;
std::cout << "\tVoters: " << setw(6) << tabl[i].num_voters;
std::cout << std::end;
Notes:
setw adds padding when writing out stuff, so it will always be at least a certain width
setprecision specifies how many decimal places to display
fixed makes floating point never use scientific notation
Your game member is a letter, and you're attemptying to assign it a string. Don't use strcpy in C++, use the std::string class instead.
#include <string>
struct table
{
std::string game;
int year;
double rating;
int num_voters;
};
Avoid using namespace std;, when you get to complex code with many namespaces, those few letters are a small price to pay for avoiding confusion.
Avoid endl: it flushes buffers which is slow. If you just want a newline, use '\n'.
Also, you can use the new initialization syntax to initialize your list:
std::vector<table> tbal = {
{"twilight struggles ", 2005, 8.226, 10690},
{"Agricola ", 2007, 8.17 , 23738},
{"Puerto Rico ", 2002, 8.163, 27433}
};
Short answer: use std::vector, not a raw array.
The following is as close to your original code as I could make it, introducing a minimum number of new features for you:
#include <assert.h> // assert
#include <iostream> // std::cout, std::endl
#include <stddef.h> // ptrdiff_t
#include <string> // std::string
#include <utility> // std::begin, std::end
#include <vector> // std::vector
using namespace std;
typedef ptrdiff_t Size;
template< class Container >
Size countOf( Container& c ) { return end( c ) - begin( c ); }
struct Game
{
string game;
int year;
double rating;
int num_voters;
};
void initialize( vector<Game>& games )
{
assert( countOf( games ) == 0 );
games.resize( 3 );
games[0].game = "twilight struggles";
games[0].year = 2005;
games[0].rating = 8.226;
games[0].num_voters = 10690;
games[1].game = "Agricloa";
games[1].year = 2007;
games[1].rating = 8.17;
games[1].num_voters = 23738;
games[2].game = "Puerto Rico";
games[2].year = 2002;
games[2].rating = 8.163;
games[2].num_voters = 27433;
}
int main()
{
vector<Game> games;
initialize( games );
for( int i = 0; i < countOf( games ); ++i )
{
cout << i << endl;
cout <<"\tGame: " << games[i].game << endl;
cout<<"\tYear: " << games[i].year << endl;
cout<<"\trating: " << games[i].rating << endl;
cout<<"\tnum voters: " << games[i].num_voters << endl;
}
}
There are ways to just declare the data more directly, including brace initializers; check out your C++ textbook.
First you need to define your table (bad name for a struct, by the way) correctly. You're trying to use game to store a string, but have defined it as only a single char. You probably want to change that to a std::string instead.
Then you probably want to do your formatting in an operator<< overloaded to take a reference to table as the type. #MooingDuck has already covered the formatting itself quite well, so it's mostly a matter of how you package that:
std::ostream &operator<<(std::ostream &os, table const &t) {
os << "Game: " << setw(20) << t.game;
os << "\tYear: " << setw(4) << t.year;
os << "\tRating: " << fixed << setprecision(2) << t.rating;
return os << "\tVoters: " << setw(6) << t.num_voters;
}
Along with that, you almost certainly want to change tabl from an array to std::vector<table>:
std::vector<tabl> tabl;
Then processing the table becomes:
std::copy(tabl.begin(), tabl.end(), std::ostream_iterator<table>(std::cout, "\n"));
One other minor detail: you seem to have two entirely different/separate functions, both named processTab. You probably want to rename at least one of those. Just glancing at them, I'd probably call one initializeTable or something on that order.