How to add a list to a string? [duplicate] - c++

This question already has answers here:
Printing out contents of a list from the c++ list library [duplicate]
(5 answers)
Closed 7 months ago.
I am trying to add a list to a string.
int main() {
std::cout << "Hello, welcome to Jay's Coffee!!\n";
std::string name; std::cout <<"What is your name "; std::cin >> name;
std::cout <<"Hello " << name << ", thank you so much for coming in today";
std::list <std::string> menu = {"Black Coffee" "Espresso" "Latte" "Cappucino"};
std::cout << name <<",what would you like from our menu today? Here is what we are serving.\n" << menu;
}
Returns
invalid operands to binary expression ('basic_ostream<char>' and 'std::list<std::string>' (aka 'list<basic_string<char>>'))

There is no operator<< for lists. You have to write a loop. For example
for (auto& item : menu)
{
std::cout << item << '\n';
}
If you think about it it's obvious why you have to do this yourself. How are you doing to separate the list items? I've chosen to put each item on a new line. You might choose to separate them with commas or spaces or some fancy format. Because there is no obvious single way to print a list there is no predefined way in the C++ library.

You should write code this way. In c++, you can't print a list directly.
#include <string>
#include <list>
using namespace std;
int main() {
cout << "Hello, welcome to Jay's Coffee!!\n";
string name;
cout <<"What is your name ";
cin >> name;
cout <<"Hello " << name << ", thank you so much for coming in today";
list <string> menu = {"Black Coffee", "Espresso", "Latte", "Cappucino"};
cout << name <<",what would you like from our menu today? Here is what we are serving.\n" ;
for ( string& s : menu )
{
cout << s << '\n';
}
}

The error message means that the operator << that you are trying to use with your object menu of the type std::list<std::string> is not defined for the class std::list<std::string>.
Also you need to separate strings in the initializer list with commas.
std::list <std::string> menu = {"Black Coffee", "Espresso", "Latte", "Cappucino"};
Otherwise the list will contain only one string due to the concatenation of string literals.
You could define such an operator as shown in the demonstration program below.
#include <iostream>
#include <string>
#include <list>
std::ostream & operator <<( std::ostream &os, const std::list<std::string>& lst )
{
for ( const auto &s : lst )
{
os << s << '\n';
}
return os;
}
int main()
{
std::list <std::string> menu =
{
"Black Coffee", "Espresso", "Latte", "Cappucino"
};
std::cout << menu;
}
The program output is
Black Coffee
Espresso
Latte
Cappucino
Or just use the range-based for loop directly in main like
std::cout << name <<",what would you like from our menu today? Here is what we are serving.\n";
for ( const auto &s : menu )
{
std::cout << s << '\n';
}
Or place the range-based for loop in a separate function similar to the operator << shown above.

First thing, you didn't define a list: in the declaration of menu, initializer-list elems were not separated by comma (,).
To make it reusable, I'd do it something like this:
#include <iostream>
#include <list>
static const std::string list_sep = ", ";
template<typename C> struct FormattedContainer
{
FormattedContainer(const C& cont, const std::string& sep = list_sep)
: cont_(cont)
, sep_(sep) {}
friend std::ostream& operator<<(std::ostream& os, const FormattedContainer& fc)
{
bool first = true;
for (auto&& e : fc.cont_)
{
if (first)
{
os << e;
first = false;
}
else
{
os << fc.sep_ << e;
}
}
return os;
}
const C& cont_;
const std::string sep_;
};
template<typename C>
auto make_fc(const C& cont, const std::string& sep = list_sep)
-> FormattedContainer<C>
{
return FormattedContainer<C>(cont, sep);
}
int main() {
std::list <std::string> menu = {"Black Coffee", "Espresso", "Latte", "Cappucino"};
std::cout << "What would you like from our menu today? Here is what we are serving.\n" << make_fc(menu);
}
This way, you don't need to define operator<< for something in std namespace (which might result in ambiguous calls as others might also define it, don't need to import another namespace, just simply call a wrapper around the type. You can use it with basically any container or iterable type using this method.

All other answers are correct but here is the better way to do the same
#include <iostream>
#include <algorithm>
#include <list>
template <typename T>
std::ostream & operator << (std::ostream & os, const std::list<T> & vec){
std::for_each (vec.begin () , vec.end() , [&](const auto& val){
std::cout << val << " ";
});
return os;
}
int main () {
std::list <std::string> menu = {"Black Coffee", "Espresso", "Latte", "Cappucino"};
std::cout << menu << "\n";
return 0;
}

Related

How to use pointer with struct to refer to the field of each struct

Below code is the normal way to get the input from a text and store it in an array in a structure.
Wanted to ask how can i use pointer to store all these data into the array of structure ? Like p1->Years (this is without array, but how can i apply this to way of writing in below code)
Any better suggestion to use pointer to take in the input?
int years = 4;
struct maju_company {
int Year;
float quarter1, quarter2, quarter3, quarter4, total_sales, average_sales;
};
int main() {
string line;
maju_company p1[years];
fstream yeecinnfile("MajuSales.txt");
if(yeecinnfile.is_open()) {
//ignoring the first four line of code and store the rest of the code
string line1,line2,line3,line4;
getline(yeecinnfile,line1);
getline(yeecinnfile,line2);
getline(yeecinnfile,line3);
getline(yeecinnfile,line4);
while(!yeecinnfile.eof()) {
for(int i =0; i<years; i++) {
yeecinnfile>>p1[i].Year>>p1[i].quarter1>>p1[i].quarter2>>p1[i].quarter3>>p1[i].quarter4;
}
}
for(int i =0; i<years; i++) {
cout<<p1[i].Year<<setw(10)<<p1[i].quarter1<<setw(10)<<p1[i].quarter2<<setw(10)<<p1[i].quarter3<<setw(10)<<p1[i].quarter4<<endl;
}
cout<<endl;
}
}
I see nothing wrong with the way you do this.
However, you could create a pointer to each record inside the loop
maju_company* p = &p1[i];
and then use p-> instead of p1[i]., but I really don't see this as an improvement.
If the reading loop looks too complicated, I would rather move the code to a separate function, perhaps
void read_record(maju_company& company);
or maybe
maju_company read_record();
and then only have to handle a single company inside the function (so no indexing and no ponters there).
I think you wouldn't need pointers at all for your example.
Use a std::vector to hold all your data and then there are other
things from C++ I think you should learn to use, example here :
(if you have questions let me know)
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
// dont use : using namespace std;
struct maju_company_data
{
int year;
float quarter1, quarter2, quarter3, quarter4, total_sales, average_sales;
};
// describe how to stream data to an output stream (like std::cout)
std::ostream& operator<<(std::ostream& os, const maju_company_data& data)
{
os << "-----------------------------------------------------\n";
os << "Company data for year : " << data.year << "\n";
os << "Quarter 1 : " << data.quarter1 << "\n";
os << "Quarter 2 : " << data.quarter1 << "\n";
os << "Quarter 3 : " << data.quarter1 << "\n";
os << "Quarter 4 : " << data.quarter1 << "\n";
os << "\n";
return os;
}
int main()
{
// no need to manage pointers yourself use a vector
std::vector<maju_company_data> company_yearly_data; // give variables a meaningful name
std::ifstream ifile("MajuSales.txt"); // ifstream your using file as input
std::string line1, line2, line3, line4;
// ignore first line
ifile >> line1;
while (ifile >> line1 >> line2 >> line3 >> line4) // probably you need to read a few more lines here
{
maju_company_data data;
// convert read strings to numbers
data.year = std::stoi(line1);
data.quarter1 = std::stof(line2);
data.quarter2 = std::stof(line3);
data.quarter3 = std::stof(line4);
//..
//data.quarter4 = std::stof(line5);
//data.total_sales = std::stof(line6);
company_yearly_data.push_back(data);
};
// this is a range based for loop
// it is prefered since you cant go out of bounds
// const auto& means that data will be an unmodifiable
// reference to each of the structs stored in the vector
for (const auto& data : company_yearly_data)
{
std::cout << data; // since we overloaded << this loop will be nice and clean
}
return 0;
}
A C++ approach to this to overload the istream operator>> and ostream operator<< for your specific type. E.g.
#include <algorithm>
#include <array>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <string>
static constexpr auto years{4};
struct maju_company {
int Year{};
float quarter1{}, quarter2{}, quarter3{}, quarter4{};
float total_sales{}, average_sales{}; // ALWAYS init your floats.
};
auto& operator>>(std::istream& is, maju_company& mc) {
is >> mc.Year
>> mc.quarter1 >> mc.quarter2 >> mc.quarter3 >> mc.quarter4
>> mc.total_sales >> mc.average_sales;
return is;
}
auto& operator<<(std::ostream& os, maju_company const& mc) {
os << mc.Year
<< std::setw(10) << mc.quarter1
<< std::setw(10) << mc.quarter2
<< std::setw(10) << mc.quarter3
<< std::setw(10) << mc.quarter4;
return os;
}
You can then go on to use the type using the std library, e.g.
int main() {
auto p1{std::array<maju_company, years>{}};
{
auto fs{std::fstream("MajuSales.txt")};
if (!fs.is_open()) return -1;
{
// throw away 4 lines
auto dummy{std::string{}};
for (auto i{0}; i < 4; ++i) getline(fs, dummy);
}
std::copy_n(std::istream_iterator<maju_company>{fs},
years,
begin(p1));
}
std::copy(cbegin(p1), cend(p1),
std::ostream_iterator<maju_company>{std::cout, "\n"});
}

creating a class vector that does not delete it's content

I am a beginner , so i wanted to ask , can we create a class object vector/array , that does not delete it's content when i close the program like , so like I want a customer record , but whenever if we try to restart the program we need to enter the customer details again and again ...
how to prevent that from happening
#include <iostream>
#include <vector>
using namespace std;
class customer{
public:
int balance;
string name;
int password;
};
int main(){
vector <customer> cus;
...
if(choice == 1){
cout << cus[i].balance
}
return 0;
}
As a complement to Adam's answer, it is possible to encapsulate the serialization in the container class itself. Here is an simplified example:
The header file defining a persistent_vector class that saves its content to a file:
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <initializer_list>
namespace {
// Utility functions able to store one element of a trivially copyable type
template <class T>
std::ostream& store1(std::ostream& out, const T& val) {
out.write(reinterpret_cast<const char*>(&val), sizeof(val));
return out;
}
template <class T>
std::istream& load1(std::istream& in, T& val) {
in.read(reinterpret_cast<char*>(&val), sizeof(val));
return in;
}
// Specialization for the std::string type
template <>
std::ostream& store1<std::string>(std::ostream& out, const std::string& val) {
store1<size_t>(out, val.size());
if (out) out.write(val.data(), val.size());
return out;
}
template <>
std::istream& load1<std::string>(std::istream& in, std::string& val) {
size_t len;
load1<size_t>(in, len);
if (in) {
char* data = new char[len];
in.read(data, len);
if (in) val.assign(data, len);
delete[] data;
}
return in;
}
}
template <class T>
class persistent_vector {
const std::string path;
std::vector<T> vec;
// load the vector from a file
void load() {
std::ifstream in(path);
if (in) {
for (;;) {
T elt;
load1(in, elt);
if (!in) break;
vec.push_back(elt);
}
if (!in.eof()) {
throw std::istream::failure("Read error");
}
in.close();
}
}
// store the vector to a file
void store() {
std::ofstream out(path);
size_t n = 0;
if (out) {
for (const T& elt : vec) {
store1(out, elt);
if (!out) break;
++n;
}
}
if (!out) {
std::cerr << "Write error after " << n << " elements on " << vec.size() << '\n';
}
}
public:
// a bunch of constructors, first ones load data from the file
persistent_vector(const std::string& path) : path(path) {
load();
}
persistent_vector(const std::string& path, size_t sz) :
path(path), vec(sz) {
load();
};
// last 2 constructors ignore the file because they do receive data
persistent_vector(const std::string& path, size_t sz, const T& val) :
path(path), vec(sz, val) {
};
persistent_vector(const std::string& path, std::initializer_list<T> ini) :
path(path), vec(ini) {
}
// destructor strores the data to the file before actually destroying it
~persistent_vector() {
store();
}
// direct access to the vector (const and non const versions)
std::vector<T>& data() {
return vec;
}
const std::vector<T>& data() const {
return vec;
}
};
It can, out of the box, handle any trivially copyable type and std::string. User has to provide specializations of store1 and load1 for custom types.
Here is a trivial program using it:
#include <iostream>
#include <string>
#include "persistent_vector.h"
int main() {
std::cout << "Create new vector (0) or read an existing one (1): ";
int cr;
std::cin >> cr;
if (!std::cin || (cr != 0 && cr != 1)) {
std::cout << "Incorrect input\n";
return 1;
}
if (cr == 0) {
persistent_vector<std::string> v("foo.data", 0, "");
// skip to the end of line...
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
for (;;) {
std::string line;
std::cout << "Enter a string to add to the vector (empty string to end program)\n";
std::getline(std::cin, line);
if (line.empty()) break;
v.data().push_back(line);
}
}
else {
persistent_vector<std::string> v("foo.data");
for (const std::string& i : v.data()) {
std::cout << i << '\n';
}
}
return 0;
}
When a programmer creates a vector class, he must ensure that the resources acquired for that vector are released when they are no longer needed. (See RAII)
C++ Reference : https://en.cppreference.com/w/cpp/language/raii
Wikipedia : https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization
Stack Overflow : What is meant by Resource Acquisition is Initialization (RAII)?
Microsoft : https://learn.microsoft.com/en-us/cpp/cpp/object-lifetime-and-resource-management-modern-cpp?view=msvc-170
Before the program closes, all resources must be released.
(No leaking resources, memory included)
It is not possible to create a vector class that does not delete its contents after closing a program. Secure operating systems will release program resources when the program is closed.
If you want the program not to lose customer information after closing, you need to save the information in persistent (non-volatile) storage device, such as a disk.
As CinCout, 김선달, Serge Ballesta say, you have to save the customer information to a file, and write the program so that you can read that file during the start of the program.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
struct customer {
std::string name;
int balance;
int password;
};
int main() {
std::vector <customer> customers;
std::ifstream ifs("info.txt");
{
customer customer{};
while (ifs >> customer.name >> customer.balance >> customer.password)
customers.push_back(customer);
}
for (const auto& [name, balance, password] : customers) {
std::cout <<
"\nName : " << name <<
"\nBalance : " << balance <<
"\nPassword : " << password <<
'\n';
}
std::cout << "\n\nWelcome\n\n";
std::ofstream ofs("info.txt", std::ios_base::app);
char cont{};
do {
customer customer{};
std::cout << "Name : ";
std::cin >> customer.name;
std::cout << "Balance : ";
std::cin >> customer.balance;
std::cout << "Password : ";
std::cin >> customer.password;
ofs << customer.name << ' ' << customer.balance << ' ' << customer.password << '\n';
std::cout << "Add another customer? (Y/N) : ";
std::cin >> cont;
} while (cont == 'Y');
for (const auto& [name, balance, password] : customers) {
std::cout <<
"\nName : " << name <<
"\nBalance : " << balance <<
"\nPassword : " << password <<
'\n';
}
}
CPlusPlus : https://www.cplusplus.com/doc/tutorial/files/
LearnCpp : https://www.learncpp.com/cpp-tutorial/basic-file-io/
(About File I/O)
This program is a prototype, I left some things incomplete (like check readings, user-defined I/O operators, duplicate code, formatting, reallocations of customers, ifs is not required after range-for + structured binding,...).
I suggest you read the book "Programming: Principles and Practice Using C+", I’m reading it and it helped me a lot.
(I’m also a beginner)
Edit: I also suggest you use "using namespace std;" only for small projects, examples or simple exercises.
Do not use "using namespace std;" for real projects, large projects or projects that may include other dependencies because the use of "using namespace std;" could lead to a possible naming collisions between names within std and the names of other codes and libraries.
It’s not good practice to use it all the time.

C++ Displaying a vector

I'm learning C++ so my question might seem a bit stupid. I wanted to build a function that print every element in a vector. I come up with that but it seems to display the address of every element. I google it and find a nice solution but i wanted to do it this way so if anyone can explain me where i'm doing something wrong.
My code :
void display_vector(std::vector<int>& to_display);
int main()
{
std::vector<int> vector_to_sort = { 2,6,7,2,1,80,2,59,8,9 };
display_vector(vector_to_sort);
}
void display_vector(std::vector<int> &to_display)
{
for (int i = 0; i < to_display.size(); i++)
{
std::cout << to_display[i] << ', ';
}
std::cout << '\n';
}
The solution i found on internet :
#include <iterator>
void display_vector(const vector<int> &v)
{
std::copy(v.begin(), v.end(),
std::ostream_iterator<int>(std::cout, " "));
}
Output of my code :
21129661129671129621129611129680112962112965911296811296911296
You use ", " instead of ', '.
You can use any of the following print mechanism in the display() function:
void display_vector(std::vector<int> &to_display)
{
//by using Normal for loop
for (auto i = to_display.begin(); i != to_display.end(); ++i) {
cout << *i << " ";
}
cout << endl;
//by using Range based for loop
for (int & i : to_display) {
cout << i << " ";
}
std::cout << '\n';
}
In this statement
std::cout << to_display[i] << ', ';
^^^^^^
you are using a multybyte character literal that has an implementation defined value.
Substitute it for string literal ", ".
As for the function then for starters if the vector is not being changed in the function then the parameter should be a constant reference.
You can use the range-based for loop to outfput elements of the vector like for example
#include <iostream>
#include <vector>
std::ostream & display_vector( const std::vector<int> &to_display, std::ostream &os = std::cout );
int main()
{
std::vector<int> vector_to_sort = { 2,6,7,2,1,80,2,59,8,9 };
display_vector(vector_to_sort) << '\n';
}
std::ostream & display_vector( const std::vector<int> &to_display, std::ostream &os )
{
for ( const auto &item : to_display )
{
os << item << ", ";
}
return os;
}
Using such a function you can for example output the vector in a text file.
Just replace below line
std::cout << to_display[i] << ', ';
with
std::cout << to_display[i] << ", ";
Also note that if you just want to display vector in function then declare parameter as const reference as shown below
void display_vector(const std::vector<int> &to_display);
The debuggers make it easy to examine vectors but I include a simple template to print out vectors of standard types and often use it when debugging data that I wish to look at with other tools.
template<class T>
void print(const std::vector<T>& v){
for (auto x: v)
std::cout << x << std::endl;
}

using iterator in ostream fails

I am attempting to implement a std::list to replace a linked list in this assignment. I am not allowed to change the declarations and can only change code in the .cpp file. For the most part I am making progress but I am having trouble implementing this
std::ostream& operator<< (std::ostream& out, const Section& section);
namely when I try to create an iterator it fails. I've used the iterator elsewhere in the code so I don't understand why it's failing here, I believe it's because it's private but I'm not sure how to resolve the issue without changing the .h file which was explicitly prohibited:
std::ostream& operator<< (std::ostream& out, const Section& section)
{
// 1. print the section header
out << setw(8) << left << section.getCourse()
<< setw(6) << left << section.getCallNumber();
out << ": " << section.getNumberOfStudents() << " students\n";
// 2. collect the students, sort, and print
Student* students = new Student[section.getNumberOfStudents()];
{
int i = 0;
for ( auto pos = section.students.begin();
pos != section.students.end(); pos++)
{
students[i] = pos;
++i;
}
}
sort (students, students+section.getNumberOfStudents());
for (int i = 0; i < section.getNumberOfStudents(); ++i)
out << " " << students[i] << "\n";
out << flush;
return out;
}
students[i] = pos;
should be changed to
students[i] = *pos;
because you want to copy the Student the iterator references, not the iterator itself.
But why a dynamic array of Student rather than a std::vector<Student>? Currently you have a memory leak because you don't delete[] students;
Edit 1
Removed.
Edit 2
Other than that, all I can see that it wrong is a missing std:: in front of
sort (students, students+section.getNumberOfStudents());
this is assuming there is no custom sort method being used.
Edit 3
Going off the rails here:
students[i] = *pos;
copies a Student from the list into the dynamic array students. This could be expensive, so here is an alternative:
First the bits and pieces needed to prove this out: Required includes
#include <iostream>
#include <list>
#include <vector>
#include <algorithm>
#include <functional>
a minimal Student class
class Student
{
std::string name;
public:
Student(std::string inname):name(inname)
{
}
const std::string & getname() const
{
return name;
}
friend bool operator<(const Student & a, const Student &b)
{
return a.name < b.name;
}
};
a minimal Section class
class Section
{
public:
std::list<Student> students;
};
a minimal outstream operator
std::ostream& operator<<(std::ostream& out, const Section& section)
{
A std::vector instead of an array, and a vector of constant references so we don't have to copy the students.
std::vector<std::reference_wrapper<const Student>> students;
Store references in the vector. Probably could do a one liner with std::copy and std::back_inserter, but this is getting a bit too much to absorb for one example.
for (const auto & student: section.students)
{
students.push_back(std::ref(student));
}
Sort the vector
std::sort(students.begin(), students.end());
print the vector
for (const auto & student: students)
{
out << student.get().getname() << " ";
}
return out;
}
and one main to rule them all and in the darkness bind them
int main()
{
Section s;
s.students.emplace_front("Tom");
s.students.emplace_front("Dick");
s.students.emplace_front("Harry");
std::cout << s;
}
And all in one easy to cut-n-paste block:
#include <iostream>
#include <list>
#include <vector>
#include <algorithm>
#include <functional>
class Student
{
public:
std::string name; // this is me being lazy. name should be private
Student(std::string inname):name(inname)
{
}
const std::string & getname() const
{
return name;
}
friend bool operator<(const Student & a, const Student &b)
{
return a.name < b.name;
}
};
class Section
{
public:
std::list<Student> students;
};
std::ostream& operator<<(std::ostream& out, const Section& section)
{
std::vector<std::reference_wrapper<const Student>> students;
// store references in the `vector`.
for (const auto & student: section.students)
{
students.push_back(std::ref(student));
}
// Sort the `vector`
std::sort(students.begin(), students.end());
// print the `vector`
for (const auto & student: students)
{
out << student.get().getname() << " ";
}
return out;
}
int main()
{
Section s;
s.students.emplace_front("Tom");
s.students.emplace_front("Dick");
s.students.emplace_front("Harry");
std::cout << s;
}
Or do what Remy suggested and use a std::vector<Student *> and a custom comparator to dereference the pointers for std::sort.
As others have stated, the error is because you are not dereferencing the iterator when populating your students[] array:
students[i] = pos; // <-- should be *pos instead!
I would suggest an alternative approach that should be faster and more efficient:
std::ostream& operator<< (std::ostream& out, const Section& section)
{
// 1. print the section header
out << setw(8) << left << section.getCourse()
<< setw(6) << left << section.getCallNumber();
out << ": " << section.getNumberOfStudents() << " students\n";
// 2. collect the students, sort, and print
std::vector<const Student*> students;
students.reserve(section.getNumberOfStudents());
for ( auto pos = section.students.cbegin();
pos != section.students.cend(); ++pos)
{
students.push_back(&(*pos));
}
sort (students.begin(), students.end(),
[](const Student *a, const Student *b) { return (*a < *b); }
);
for ( auto pos = students.cbegin();
pos != students.cend(); ++pos)
{
out << " " << *(*pos) << "\n";
}
out << flush;
return out;
}
I appreciate all your answers. Ended up being a much more basic issue. I had to implement the Section iterators to return student iterators.
Section::iterator Section::begin() {
return students.begin();
}
Section::const_iterator Section::begin() const {
return students.begin();
}
Section::iterator Section::end() {
return students.begin();
}
Section::const_iterator Section::end() const {
return students.begin();
}

C++ Serializing a std::map to a file

I have a C++ STL map, which is a map of int and customType.
The customType is a struct, which has string and a list of string, How can i serialize this to a file.
sample struct:
struct customType{
string;
string;
int;
list<string>;
}
If you are not afraid of BOOST, try BOOST Serialize:
(template code, here can be some errors...)
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/serialization/map.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/list.hpp>
struct customType{
string string1;
string string2;
int i;
list<string> list;
// boost serialize
private:
friend class boost::serialization::access;
template <typename Archive> void serialize(Archive &ar, const unsigned int version) {
ar & string1;
ar & string2;
ar & i;
ar & list;
}
};
template <typename ClassTo>
int Save(const string fname, const ClassTo &c)
{
ofstream f(fname.c_str(), ios::binary);
if (f.fail()) return -1;
boost::archive::binary_oarchive oa(f);
oa << c;
return 0;
}
Usage:
Save< map<int, customType> >("test.map", yourMap);
A simple solution is to output each member on a line on its own, including all the strings in the list. Each record start with the key to the map, and ends with a special character or character sequence that can not be in the list. This way you can read one line at a time, and know the first line is the map key, the second line the first string in the structure and so on, and when you reach your special record-ending sequence you know the list is done and it's time for the next item in the map. This scheme makes the files generated readable, and editable if you need to edit them outside the program.
C++ doesn't have reflection capabilities like Java and others, so there's no 'automatic' way of doing that. You'll have to do all the work yourself: open the file, output each element in a loop, and close the file. Also there's no standard format for the file, you'd need to define one that meets your needs. Of course, there are libraries out there to help in this, but they aren't part of the language. Take a look at this question:
Is it possible to automatically serialize a C++ object?
Also take a look at:
http://s11n.net/
If you are asking this, then probably you already know that you cannot serialize this by means of:
file.write( (const char *) &mapOfCustom, sizeof( mapOfCustom ) );
The problem has to do with complex objects (and in C++, even a string variable is a complex object), i.e., those objects that are not self-contained. Actually, even simple serialization has problems, which range from platform compatibilty to even compiler compatibilty (different paddings, etc.).
One way to go is use a simple XML library such as tinyXML:
http://www.grinninglizard.com/tinyxml/
And write save to XML, and restore from XML procedures.
You can try this: cxx-prettyprint
Hi I wrote a standalone C11 header to achieve this. Your example
of a map of custom classes, I just added - to make sure it worked 8)
https://github.com/goblinhack/simple-c-plus-plus-serializer
#include "c_plus_plus_serializer.h"
class Custom {
public:
int a;
std::string b;
std::vector c;
friend std::ostream& operator<<(std::ostream &out,
Bits my)
{
out << bits(my.t.a) << bits(my.t.b) << bits(my.t.c);
return (out);
}
friend std::istream& operator>>(std::istream &in,
Bits my)
{
in >> bits(my.t.a) >> bits(my.t.b) >> bits(my.t.c);
return (in);
}
friend std::ostream& operator<<(std::ostream &out,
class Custom &my)
{
out << "a:" << my.a << " b:" << my.b;
out << " c:[" << my.c.size() << " elems]:";
for (auto v : my.c) {
out << v << " ";
}
out << std::endl;
return (out);
}
};
static void save_map_key_string_value_custom (const std::string filename)
{
std::cout << "save to " << filename << std::endl;
std::ofstream out(filename, std::ios::binary );
std::map< std::string, class Custom > m;
auto c1 = Custom();
c1.a = 1;
c1.b = "hello";
std::initializer_list L1 = {"vec-elem1", "vec-elem2"};
std::vector l1(L1);
c1.c = l1;
auto c2 = Custom();
c2.a = 2;
c2.b = "there";
std::initializer_list L2 = {"vec-elem3", "vec-elem4"};
std::vector l2(L2);
c2.c = l2;
m.insert(std::make_pair(std::string("key1"), c1));
m.insert(std::make_pair(std::string("key2"), c2));
out << bits(m);
}
static void load_map_key_string_value_custom (const std::string filename)
{
std::cout << "read from " << filename << std::endl;
std::ifstream in(filename);
std::map< std::string, class Custom > m;
in >> bits(m);
std::cout << std::endl;
std::cout << "m = " << m.size() << " list-elems { " << std::endl;
for (auto i : m) {
std::cout << " [" << i.first << "] = " << i.second;
}
std::cout << "}" << std::endl;
}
void map_custom_class_example (void)
{
std::cout << "map key string, value class" << std::endl;
std::cout << "============================" << std::endl;
save_map_key_string_value_custom(std::string("map_of_custom_class.bin"));
load_map_key_string_value_custom(std::string("map_of_custom_class.bin"));
std::cout << std::endl;
}
Output:
map key string, value class
============================
save to map_of_custom_class.bin
read from map_of_custom_class.bin
m = 2 list-elems {
[key1] = a:1 b:hello c:[2 elems]:vec-elem1 vec-elem2
[key2] = a:2 b:there c:[2 elems]:vec-elem3 vec-elem4
}
Let me know if this helps - or you find bugs. It's quite a simple serializer and really just a learning tool for me. Heavier weight approaches like Cereal might work for you better.