How to make a simple loop more generic - c++

The below method will concatenate all warnings into one string. It works but obviously need to create almost the same method again for info and error.
struct MyItem
{
std::vector<std::string> errors;
std::vector<std::string> warnings;
std::vector<std::string> infos;
};
std::vector<MyItem> items;
std::string GetWarnings()
{
std::string str;
for (auto item : items)
{
for (auto warn : item.warnings)
{
str += warn;
str += " ";
}
}
return str;
}
What would be a good generic way to implement one "Concatenate" method? One solution would be to define an enum (error\warning\item), pass it as input argument and make a switch case on argument value. Any more elegant solutions?

You can use a pointer to member to extract a specific field from an object:
auto concatenate(
const std::vector<MyItem>& items,
const std::vector<std::string> MyItem::* member
) {
std::string str;
for (const auto& item : items) {
for (const auto& element : item.*member) {
str += element;
str += " ";
}
}
return str;
}
Which can be used like so:
int main() {
std::vector<MyItem> items{
{{"err11", "err12"}, {"w11"}, {"i11", "i12", "i13"}},
{{"err21"}, {"w21", "w22", "w23", "w24"}, {"i21"}}
};
std::cout << "all errors: " << concatenate(items, &MyItem::errors) << '\n'
<< "all warnings: " << concatenate(items, &MyItem::warnings) << '\n'
<< "all infos: " << concatenate(items, &MyItem::infos) << '\n';
}
And, if you have members of different types in your struct (note that the above solution works only for vectors of strings), you can turn concatenate into a function template:
struct MyItem {
std::vector<std::string> errors;
std::vector<std::string> warnings;
std::vector<std::string> infos;
std::vector<char> stuff; // added
};
template <typename T> // now a function template
auto concatenate(
const std::vector<MyItem>& items,
const T MyItem::* member
) {
std::string str;
for (const auto& item : items) {
for (const auto& element : item.*member) {
str += element;
str += " ";
}
}
return str;
}
int main() {
std::vector<MyItem> items{
{{"err11", "err12"}, {"w11"}, {"i11", "i12", "i13"}, {'1' ,'2'}},
{{"err21"}, {"w21", "w22", "w23", "w24"}, {"i21"}, {'3'}}
};
std::cout << "all errors: " << concatenate(items, &MyItem::errors) << '\n'
<< "all warnings: " << concatenate(items, &MyItem::warnings) << '\n'
<< "all infos: " << concatenate(items, &MyItem::infos) << '\n'
<< "all stuffs: " << concatenate(items, &MyItem::stuff) << '\n';
}
Note that I also changed your auto item occurrences in your for() loops to const auto& items in order to avoid unncecessary copies.
Alternatively, you can use a projection to extract your elements. In this implementation, we use a template to accept any type of a function that will take your MyItem and return the desired element:
template <typename Proj>
auto concatenate(
const std::vector<MyItem>& items,
Proj projection
) {
std::string str;
for (const auto& item : items) {
for (const auto& element : projection(item)) {
str += element;
str += " ";
}
}
return str;
}
int main() {
std::vector<MyItem> items{
{{"err11", "err12"}, {"w11"}, {"i11", "i12", "i13"}, {'1' ,'2'}},
{{"err21"}, {"w21", "w22", "w23", "w24"}, {"i21"}, {'3'}}
};
auto errors_projection =
[](const MyItem& item) -> const std::vector<std::string>& {
return item.errors;
};
auto warnings_projection =
[](const MyItem& item) -> const std::vector<std::string>& {
return item.warnings;
};
auto infos_projection =
[](const MyItem& item) -> const std::vector<std::string>& {
return item.infos;
};
auto stuff_projection =
[](const MyItem& item) -> const std::vector<char>& {
return item.stuff;
};
std::cout << "all errors: " << concatenate(items, errors_projection) << '\n'
<< "all warnings: " << concatenate(items, warnings_projection) << '\n'
<< "all infos: " << concatenate(items, infos_projection) << '\n'
<< "all stuffs: " << concatenate(items, stuff_projection) << '\n';
}

Related

How to display a map of a map (C++)?

I made the following map to store data from a .log :
map<string,pair(int,map<string,int>)>.
I manage to store the data but not to retrieve it. What I do is:
cout<< "first string: "<< debut->first
<< " pair (first int): " << debut->second.first << endl;
(debut is a constant iterator of a map)
With that I get the first string and the int of the pair, but I don't know how to get the content of the map. I tried different syntaxes as debut->second.second->first or debut->second.second.first but of them work.
Does someone have an idea?
I don't know how to get the content of the map.
You can print out the content of the innermost map as shown below([DEMO]):
auto beg = debut->second.second.cbegin();
auto end = debut->second.second.cend();
while(beg!=end) //can also use for loop
{
std::cout<<beg->first<<" ";
std::cout<<beg->second<<std::endl;
++beg;
}
Here is a more complete example for printing the all the content of the maps, just for demonstration:
#include <iostream>
#include <map>
int main()
{
std::map<std::string,std::pair<int,std::map<std::string,int>>> m{ { "first", { 5, {{"a", 10}, {"b", 20}} }} ,
{ "second", { 6, {{"c", 100}, {"d", 200},{"e", 300} }}},
{ "third", { 7, {{"f", 400}} } }};
//////////////////////////////////////////////////////////////////
//PRINTING THE CONTENT OF THE MAP
auto debutBegin = m.cbegin(); //const iterator to beginning of outer map
auto debutEnd = m.cend(); //const iterator to end of outer map
while(debutBegin!=debutEnd)
{
auto beg = debutBegin->second.second.cbegin(); //const iterator to beginning of inner map
auto end = debutBegin->second.second.cend();//const iterator to end of inner map
std::cout<<debutBegin->first<<" ---- "<<debutBegin->second.first<<std::endl;
while(beg!=end)
{
std::cout<<beg->first<<" ";
std::cout<<beg->second<<std::endl;
++beg;
}
debutBegin++;
}
return 0;
}
Output
The output of the above program is:
first ---- 5
a 10
b 20
second ---- 6
c 100
d 200
e 300
third ---- 7
f 400
You probably need to iterate over the second map as well. Here's one way to do this:
map<string,pair<int,map<string,int>>> mp1;
map<string, int> mp2;
mp2["str2 inside mp1"] = 1;
mp1["str1 (key)"] = {2, mp2};
for(auto pair1 : mp1) {
cout << "First string: " << pair1.first << "\n"; // str1 (key)
cout << "First int: " << pair1.second.first << "\n"; // First int: 2
for(auto pair2 : pair1.second.second) {
cout << "Second string: " << pair2.first << "\n"; // Second string: str2 inside mp1
cout << "Second int: " << pair2.second << "\n"; // Second int: 1
}
}
However (as the comments suggest) this is a complicated approach, maybe you need to submit a new question explaining your usecase. My thought would be to seperate this into to seperate maps somehow.
I would recommend to:
split your printing into functions,
print one map at a time, and
use whatever number of variables/references you need to make your code clearer.
The code below is just an example that tries to follow that intent.
[Demo]
#include <iostream> // cout
#include <map>
#include <string>
#include <utility> // pair
using inner_map_t = std::map<std::string, int>;
using outer_map_t = std::map<std::string, std::pair<int, inner_map_t>>;
std::ostream& operator<<(std::ostream& os, const inner_map_t& m)
{
os << "\t{\n";
bool first_elem{true};
for (auto& [key, value] : m)
{
auto& second_string{ key };
auto& second_int{ value };
os
<< "\t\t" << (first_elem ? "" : ", ") << "second string: " << second_string << ", "
<< "second int: " << second_int << "\n";
first_elem = false;
}
os << "\t}\n";
return os;
}
std::ostream& operator<<(std::ostream& os, const outer_map_t& m)
{
os << "{\n";
bool first_elem{ true };
for (auto& [key, value] : m)
{
auto& first_string{ key };
auto& first_int{ value.first };
auto& inner_map{ value.second };
os
<< "\t" << (first_elem ? "" : ", ") << "first string: " << first_string << ", "
<< "first int: " << first_int << "\n"
<< inner_map;
first_elem = false;
}
os << "}\n";
return os;
}
int main()
{
const outer_map_t m{
{ "one", { 1, {{"uno", 10}, {"cien", 100}} } },
{ "two", { 2, {{"dos", 20}} } },
{ "three", { 3, {{"tres", 30}} } }
};
std::cout << m << "\n";
}
// Outputs:
//
// {
// first string: one, first int: 1
// {
// second string: cien, second int: 100
// , second string: uno, second int: 10
// }
// , first string: three, first int: 3
// {
// second string: tres, second int: 30
// }
// , first string: two, first int: 2
// {
// second string: dos, second int: 20
// }
// }
And another implementation, probably a bit neater.
[Demo]
#include <iostream> // cout
#include <map>
#include <string>
#include <utility> // pair
using inner_map_t = std::map<std::string, int>;
using pair_t = std::pair<int, inner_map_t>;
using outer_map_t = std::map<std::string, pair_t>;
template <typename T, typename U>
std::ostream& print_map(std::ostream& os, const std::map<T,U> m, size_t level_of_indent)
{
std::string indent(4*level_of_indent, ' ');
std::string next_indent(4*(level_of_indent + 1), ' ');
os << "{\n";
bool first{ true };
for (auto& [key, value] : m)
{
os << (first ? "" : ",\n") << next_indent << "'" << key << "' : " << value;
first = false;
}
os << "\n" << indent << "}";
return os;
}
std::ostream& operator<<(std::ostream& os, const inner_map_t& m) {
return print_map(os, m, 1);
}
std::ostream& operator<<(std::ostream& os, const pair_t& p) {
return os << "(" << p.first << ", " << p.second << ")";
}
std::ostream& operator<<(std::ostream& os, const outer_map_t& m) {
return print_map(os, m, 0);
}
int main()
{
const outer_map_t m{
{ "Alien", { 1, {{"speed", 90}, {"power", 90}} } },
{ "Jabba", { 2, {{"speed", 10}} } },
{ "T1000", { 3, {{"speed", 70}} } }
};
std::cout << m << "\n";
}
// Outputs:
//
// {
// 'Alien' : (1, {
// 'power' : 90,
// 'speed' : 90
// }),
// 'Jabba' : (2, {
// 'speed' : 10
// }),
// 'T1000' : (3, {
// 'speed' : 70
// })
// }

C++" no match for operator<<" when printing a std::vector<std::pair>

I want to print the vector dicVec in order to check if everything is as I want. But I can´t. I already looked it up, but nothing helped.
This is my current code:
void Translate::read(){
int i = 0;
std::ifstream dic("ende.dic");
if (dic.is_open())
{
std::string dicLine;
while (std::getline(dic, dicLine)){
std::vector<std::pair<std::string, std::string>> dicVec;
std::pair<std::string, std::string> dicPair;
std::size_t pos = dicLine.find(";");
dicPair.first = dicLine.substr(0, pos);
//std::cout << dicPair.first << " : ";
dicPair.second= dicLine.substr(pos+1);
//std::cout << dicPair.second << std::endl;
std::cout << dicVec[i];
i++;
}
}
else {
throw std::runtime_error("File could not be opened");
}
}
I also tried to print it using this:
std::cout << dicVec.at(i);
and this:
for (auto i = dicVec.begin(); i != dicVec.end(); ++i)
std::cout << *i << ' ';
But there´s always this error:
no match for operator<<
What do I have to do in order to print my vector?
Thank you for your help.
You are trying to print out a std::pair, but there is no standard operator<< implemented for std::pair, so you will have to write you own.
Even if you fix that, your code will still fail, because dicVec is empty when you call std::cout << dicVec[i]; You are not adding dicPair to dicVec before printing the contents of dicVec. In fact, dicVec should be declared above the loop, not inside of it.
Try something more like this:
typedef std::pair<std::string, std::string> stringPair;
typedef std::vector<stringPair> stringPairVec;
std::ostream& operator<<(std::ostream &out, const stringPair &p) {
out << p.first << " : " << p.second << "\n";
return out;
}
std:ostream& operator<<(std::ostream &out, const stringPairVec &vec) {
for (size_t i = 0; i < vec.size(); ++i) {
out << vec[i];
}
return out;
}
void Translate::read(){
std::ifstream dic("ende.dic");
if (!dic.is_open()) {
throw std::runtime_error("File could not be opened");
}
stringPairVec dicVec;
std::string dicLine;
while (std::getline(dic, dicLine)) {
std::size_t pos = dicLine.find(";");
stringPair dicPair = std::make_pair(
dicLine.substr(0, pos),
dicLine.substr(pos+1)
);
dicVec.push_back(dicPair);
}
std::cout << dicVec;
}
Or, in C++11 and later, you can do this instead:
using stringPair = std::pair<std::string, std::string>;
using stringPairVec = std::vector<stringPair>;
std::ostream& operator<<(std::ostream &out, const stringPair &p) {
out << p.first << " : " << p.second << "\n";
return out;
}
std:ostream& operator<<(std::ostream &out, const stringPairVec &vec) {
for (const auto &p : vec) {
out << p;
}
return out;
}
void Translate::read(){
std::ifstream dic("ende.dic");
if (!dic.is_open()) {
throw std::runtime_error("File could not be opened");
}
stringPairVec dicVec;
std::string dicLine;
while (std::getline(dic, dicLine)) {
std::size_t pos = dicLine.find(";");
dicVec.emplace_back(
dicLine.substr(0, pos),
dicLine.substr(pos+1)
);
}
std::cout << dicVec;
}

Convert vector<string> to other types

I've a class withmap<string,vector<string>>.
I want to give a user a member function to receive the value for a key in different formats than std::vector(string):
vector(string),
string,
vector(int),
vector(float) and
bool
example:
bool x = (bool) myClass["dummy_boolean_type"]
x = myClass["dummy_boolean_type"].as<bool>
int y = (int) myClass["dummy_boolean_type"]
Can someone have an example what is the best way to achieve it ?
(without a use in Boost)
You cannot provide your class with any member function or functions
that will support the two constructions you want:
T x = myClassInstance["key"].as<T> // (1)
T x = (T) myClassInstance["key"] // (2)
In the case of (1), the immediate reason is simply that the construction is
not legal C++. So let's suppose it is replaced by:
T x = myClassInstance["key"].as<T>() // (1a)
But this doesn't help, and the reason is the same for both (1a) and (2):
The expression myClassInstance["key"] will have to evaluate to some object e or reference to such that is returned by:
myClass::operator[](std::string const &);
This e is the vector<string> to which key maps in your
map<string,vector<string> data member of myClass. It is not myClassInstance.
So what you are asking for are member functions of myClass that will support
the constructions:
T x = e.as<T> // (1b)
T x = (T) e // (2b)
where e is an std::vector<std::string>.
Clearly, nothing you can do in myClass can address your requirement. In
(1b) and (2b), myClass is nowhere to be seen.
Is there any template member function of std::vector<std::string>:
template<typename T>
T as() const;
that has the behaviour you want for (1b)?
Does std::vector<std::string> have any template member function:
template<typename T>
operator T() const;
that has the behaviour you want for (2b)?
No and No.
However...
You can implement an as template member function of myClass that
supports conversions such as you mention. Its signature - of course -
would be:
template<typename T>
T myClass::as(std::string const & key) const;
and you would invoke it like:
T x = myClassInstance.as<T>("key") // (1c)
I'll sketch an implemention that assumes we're content with the
conversions from std::vector<std::string> to:
std::string
std::vector<int>
bool
which will be enough to get you under way.
To convert an std::vector<std::string> vs to std::string I'll concatenate
the elements of vs.
To convert an std::vector<std::string> vs to std::vector<int> I'll convert each element of vs from a decimal numeral, if I can, to the
integer the numeral represents, and return a vector of those integers. Without
prejudice to other policies I'll throw an std::invalid_argument exception
when an element doesn't trim to a decimal numeral.
I am spoilt for choice as to what you might mean by converting an std::vector<std::string>
to bool, so I will arbitrarily say that vs is true if I can convert it
to a vector<int> of which any element is non-0 and is false if I can convert
it to a vector<int> in which all elements are 0.
The sketch:
#include <type_traits>
#include <map>
#include <vector>
#include <string>
#include <algorithm>
namespace detail {
template<typename T>
struct convert_to
{
static T from(std::vector<std::string> const & vs) {
static constexpr bool always_false = !std::is_same<T,T>::value;
static_assert(always_false,
"Calling `convert_to<T>::from` for unimplemented `T`");
return *(T*)nullptr;
}
};
template<>
std::string convert_to<std::string>::from(std::vector<std::string> const & vs)
{
std::string s;
for ( auto const & e : vs ) {
s += e;
}
return s;
}
template<>
std::vector<int>
convert_to<std::vector<int>>::from(std::vector<std::string> const & vs)
{
auto lamb = [](std::string const & s) {
std::size_t lastoff = s.find_last_not_of(" \t\f\v\n\r");
int i;
try {
std::size_t nlen;
i = std::stoi(s,&nlen);
if (nlen <= lastoff) {
throw std::invalid_argument("");
}
}
catch(std::invalid_argument const & e) {
throw std::invalid_argument(
"Cannot convert \"" + s + "\" to int");
}
return i;
};
std::vector<int> vi;
std::transform(vs.begin(),vs.end(),std::back_inserter(vi),lamb);
return vi;
}
template<>
bool convert_to<bool>::from(std::vector<std::string> const & vs)
{
auto vi = convert_to<std::vector<int>>::from(vs);
for (auto const & i : vi) {
if (i) {
return true;
}
}
return false;
}
} // namespace detail
struct myClass // Your class
{
// Whatever...
std::vector<std::string> & operator[](std::string const & key) {
return _map[key];
}
template<typename T>
T as(std::string const & key) {
return detail::convert_to<T>::from(_map[key]);
}
// Whatever...
private:
std::map<std::string,std::vector<std::string>> _map;
};
The one take-away point here is the use of template<typename T>
detail::struct convert_to, with its solitary static member function:
T from(std::vector<std::string> const & vs)
which in the default instantiation will provoke a static_assert failure
reporting that no conversion to T from std::vector<std::string> has been
defined.
Then, for each type U to which you want a conversion, you have just to
write a specializing definition:
template<>
U convert_to<U>::from(std::vector<std::string> const & vs);
as you see fit, and the construction (1c) will use it as per:
template<typename T>
T myClass::as(std::string const & key) {
return detail::convert_to<T>::from(_map[key]);
}
Here's an illustrative progam you can append to the sketch:
#include <iostream>
using namespace std;
template<typename T>
static void print_vec(std::vector<T> const & v)
{
cout << "{ ";
for (auto const & e : v) {
cout << e << " ";
}
cout << "}\n";
}
static void print_vec(std::vector<std::string> const & v)
{
cout << "{ ";
for (auto const & e : v) {
cout << '\"' << e << "\" ";
}
cout << "}\n";
}
int main()
{
myClass f;
f["int_vec"] = vector<string>{"0","1 "," 2"};
cout << "f[\"int_vec\"] = "; print_vec(f["int_vec"]);
f["true_vec"] = vector<string>{"0"," 1 ","0"};
cout << "f[\"true_vec\"] = "; print_vec(f["true_vec"]);
f["false_vec"] = vector<string>{"0"," 0","0 "};
cout << "f[\"false_vec\"] = "; print_vec(f["false_vec"]);
f["not_int_vec0"] = vector<string>{"0","1","2",""};
cout << "f[\"not_int_vec0\"] = "; print_vec(f["not_int_vec0"]);
f["not_int_vec1"] = vector<string>{"0","#","2",};
cout << "f[\"not_int_vec1\"] = "; print_vec(f["not_int_vec1"]);
f["not_int_vec2"] = vector<string>{"0"," 1$","2",};
cout << "f[\"not_int_vec2\"] = "; print_vec(f["not_int_vec2"]);
cout << "f.as<string>(\"int_vec\") = \""
<< f.as<string>("int_vec") << '\"' << endl;
cout << "f.as<string>(\"true_vec\") = \""
<< f.as<string>("true_vec") << '\"' << endl;
cout << "f.as<string>(\"false_vec\") = \""
<< f.as<string>("false_vec") << '\"' << endl;
cout << "f.as<string>(\"not_int_vec0\") = \""
<< f.as<string>("not_int_vec0") << '\"' << endl;
cout << "f.as<string>(\"not_int_vec1\") = \""
<< f.as<string>("not_int_vec1") << '\"' << endl;
cout << "f.as<string>(\"not_int_vec2\") = \""
<< f.as<string>("not_int_vec2") << '\"' << endl;
vector<int> va = f.as<vector<int>>("int_vec");
cout << "f.as<vector<int>>(\"int_vec\") = ";
print_vec(f.as<vector<int>>("int_vec"));
cout << boolalpha << "f.as<bool>(\"true_vec\") = "
<< f.as<bool>("true_vec") << endl;
cout << boolalpha << "f.as<bool>(\"false_vec\") = "
<< f.as<bool>("false_vec") << endl;
try {
cout << "f.as<vector<int>>(\"not_int_vec0\")...";
auto b = f.as<vector<int>>("not_int_vec0");
(void)b;
}
catch(std::invalid_argument const & e) {
cout << e.what() << endl;
}
try {
cout << "f.as<vector<int>>(\"not_int_vec1\")...";
auto b = f.as<vector<int>>("not_int_vec1");
(void)b;
}
catch(std::invalid_argument const & e) {
cout << e.what() << endl;
}
try {
cout << "f.as<vector<int>>(\"not_int_vec2\")...";
auto b = f.as<vector<int>>("not_int_vec2");
(void)b;
}
catch(std::invalid_argument const & e) {
cout << e.what() << endl;
}
// char ch = f.as<char>("int_vec"); <- static_assert fails
return 0;
}
It outputs:
f["int_vec"] = { "0" "1 " " 2" }
f["true_vec"] = { "0" " 1 " "0" }
f["false_vec"] = { "0" " 0" "0 " }
f["not_int_vec0"] = { "0" "1" "2" "" }
f["not_int_vec1"] = { "0" "#" "2" }
f["not_int_vec2"] = { "0" " 1$" "2" }
f.as<string>("int_vec") = "01 2"
f.as<string>("true_vec") = "0 1 0"
f.as<string>("false_vec") = "0 00 "
f.as<string>("not_int_vec0") = "012"
f.as<string>("not_int_vec1") = "0#2"
f.as<string>("not_int_vec2") = "0 1$2"
f.as<vector<int>>("int_vec") = { 0 1 2 }
f.as<bool>("true_vec") = true
f.as<bool>("false_vec") = false
f.as<vector<int>>("not_int_vec0")...Cannot convert "" to int
f.as<vector<int>>("not_int_vec1")...Cannot convert "#" to int
f.as<vector<int>>("not_int_vec2")...Cannot convert " 1$" to int
(gcc 5.1, clang 3.6, C++11)

C++ cout auto separator

I am wondering if there is way that std::cout automatically will insert some predefined value between printed sequences.
For example:
std::cout << 2 << 3 << 33 << 45 << std::endl;
outputs
233345
and I would like it to output
2 3 33 45
and I know, that it's easy to:
std::cout << 2 << " " << 3 << " " << 33 << " " << 45 << std::endl;
But I'am wondering if there is a way to automate this, such as:
std::cout << set_some_separator(" ") << 2 << 3 << 33 << 45 << std::endl;
Anyone aware of something like this being possible?
Well, I got beaten to it. I'll post this anyway.
Edit : well, after reading Nim's answer, mine does achieve the exact syntax OP wished for.
#include <iostream>
#include <algorithm>
struct with_separator {
with_separator(std::string sep)
: sep(std::move(sep)) {}
std::string sep;
};
struct separated_stream {
separated_stream(std::ostream &stream, std::string sep)
: _stream(stream), _sep(std::move(sep)), _first(true) {}
template <class Rhs>
separated_stream &operator << (Rhs &&rhs) {
if(_first)
_first = false;
else
_stream << _sep;
_stream << std::forward<Rhs>(rhs);
return *this;
}
separated_stream &operator << (std::ostream &(*manip)(std::ostream&)) {
manip(_stream);
return *this;
}
private:
std::ostream &_stream;
std::string _sep;
bool _first;
};
separated_stream operator << (std::ostream &stream, with_separator wsep) {
return separated_stream(stream, std::move(wsep.sep));
}
int main()
{
std::cout << with_separator(", ") << 1 << 2 << 3 << std::endl;
}
Output :
1, 2, 3
Simple answer is No, however, you can roll your own...
#include <iostream>
#include <sstream>
using namespace std;
struct set_some_separator{
set_some_separator(const char* sep) : _sep(sep)
{ };
template <typename T>
set_some_separator& operator<<(const T& v)
{
_str << v << _sep;
return *this;
}
friend
ostream& operator<<(ostream& os, const set_some_separator& s)
{ return os << s._str.str(); }
const char* _sep;
ostringstream _str;
};
int main()
{
cout << (set_some_separator(" ") << 2 << 3 << 33 << 45) << endl;
}
Okay the format of the cout is slightly different, hey-ho...
Not quite the same thing, but:
#include <array>
#include <iostream>
#include <iterator>
int main() {
std::array<int, 3> data = { 1, 2, 3 };
std::ostream_iterator<int> out(std::cout, " ");
std::copy(data.begin(), data.end(), out);
std::cout << '\n';
return 0;
}
How about using the ostream_iterator
int main()
{
std::vector<int> data {2,3,33,45};
std::copy(std::begin(data), std::end(data),
std::ostream_iterator(std::cout, " "));
std::cout << "\n";
}
A C++17 fold expression with the comma operator can create a nice one-liner:
[](auto &&...xs){ ((std::cout << xs << ',') , ...); }(2,3,33,45);
Simple solution, maybe you can tweak it to use <<
template<typename T>
void myout(T value)
{
std::cout << value << std::endl;
}
template<typename First, typename ... Rest>
void myout(First first, Rest ... rest)
{
std::cout << first << " ";
myout(rest...);
}
myout('a',"Hello",1,2,3,22/7.0);
If you're just looking for a way to print a vector with its elements properly delimited (and without an extra delimiter at the end), try this:
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v{1, 2, 3};
auto it = v.begin();
if (it != v.end())
{
std::cout << *it;
++it;
}
for (; it != v.end(); ++it)
{
std::cout << ", " << *it;
}
}

String representation of std::vector<T>

Is it possible to implement a C++ function which gives a string representation of every std::vector<T>, as long as the element of type T can be appended to an output stream like
T x;
...
std::cout << x << std::endl;
The string representation should look like
[x, y, z]
I've attempted the following, but what should ? be?
template <typename T> std::string vectorToString(std::vector<T>& vec) {
std::string s;
for (T element : vec) {
?
}
return s;
}
You'll want a stringstream to do the formatting:
std::ostringstream ss;
ss << '['
bool first = true;
for (T const & element : vec) {
if (!first) {
ss << ", ";
}
ss << element;
first = false;
}
ss << ']';
return ss.str();
Use a std::ostringstream instance for ? and return std::ostringstream::str():
std::ostringstream s;
s << "[";
for (auto i(vec.begin()); i != vec.end(); i++)
{
if (vec.begin() != i) s << ", ";
s << *i;
}
s << "]";
return s.str();
If you are working on C++11, you can use this simple version:
#include <sstream>
#include <algorithm>
using namespace std;
template<typename T>
string format(vector<T> const& v)
{
if (v.empty()) return "[]";
ostringstream ss;
ss << "[" << v[0];
for_each(begin(v) + 1, end(v), [&ss] (T const& s) { ss << ", " << s; });
ss << "]";
return ss.str();
}
If you want to make it generic for other types of collections (not just vector) or even for sub-ranges of a collection, you can generalize it this way:
#include <sstream>
#include <algorithm>
using namespace std;
template<typename It>
string format(It b, It e)
{
if (b == e) return "[]";
ostringstream ss;
ss << "[" << *b;
for_each(++b, e, [&ss] (decltype(*b)& s) { ss << ", " << s; });
ss << "]";
return ss.str();
}
template<typename C>
string format(C const& c)
{
return format(begin(c), end(c));
}
int main()
{
vector<int> v = { 4, 5, 5, 8 };
cout << format(v) << endl;
return 0;
}
Using algorithms, which usually simplify code (not sure if in this case, but added for the sake of completion):
template <typename T>
std::string toString( std::vector<T> const & v ) {
if (v.empty())
return "[]";
typename std::vector<T>::const_iterator last = std::prev(v.end());
std::ostringstream st;
st << "[ ";
std::copy( v.begin(), last, std::ostream_iterator<T>(st,", ") );
st << *last << " ]";
return st.str();
}
A little bit faster version
template <typename T> std::string vectorToString( const std::vector<T>& vec ) {
if ( vec.empty() ) {
return "[]";
}
std::ostringstream s;
s << "[" << vec.front();
for (auto i = vec.begin() + 1, e = vec.end(); i != e; i++)
{
s << ", " << *i;
}
s << "]";
return s.str();
}
Another moment: maybe it would be right to specialize this function for strings and quote them, because if a string in vector begins or ends with , it would be hard to understand how many strings was printed.
template <>
std::string vectorToString< std::string >( const std::vector<std::string>& vec ) {
if ( vec.empty() ) {
return "[]";
}
std::ostringstream s;
s << "[" << vec.front();
for (auto i = vec.begin() + 1, e = vec.end(); i != e; i++)
{
s << ", \"" << *i << "\"";
}
s << "]";
return s.str();
}