writing the vector map to a file in Omnetpp - c++

I am having problem in writing the vector map to a file. I would like to know the detail value inside the wsmdata. I know that inorder to access the detail information I need to use operator overloading like “std::ostream& operator<<(std::ostream& os, map& );” in header file as well as in .cc file. But I don’t know how to use it in detail to access the vector data or output the vector data in the file. I have bee stuck in this problem for a long time. Can anybody help ?
Here is the portion of codes:
.h file:
using std::map;
typedef std::vector<WaveShortMessage*> WaveShortMessages;
std::map<long,WaveShortMessages> receivedWarningMap;
.cc file:
// add warning message to received messages storage
receivedWarningMap[wsm->getTreeId()].push_back(wsm->dup());
std::cout<<"Wsm dup() values/ receivedWarningMap="<<wsm->dup()<<endl;
std::ofstream tracefile;
tracefile.clear();
tracefile.open("traceFile1.txt", std::ios_base::app);
for (UINT i = 0; i < receivedWarningMap[wsm->getTreeId()].size(); i++)
{
std::cout << receivedWarningMap[wsm->getTreeId()][i] << std::endl;
EV<< "MyID="<<getMyID()<< "Recepient ID"<<wsm->getRecipientAddress()<<"Neighbor ID="<< wsm->getSenderAddress()<< std::endl;
}
tracefile.close();

First of all define operator << for your class WaveShortMessage, for example this way:
std::ostream & operator<<(std::ostream &os, WaveShortMessage * wsm) {
os << "Recepient ID=" << wsm->getRecipientAddress() << "; ";
os << "Neighbor ID=" << wsm->getSenderAddress() << "; ";
// and any other fields of this class
//...
return os;
}
Then use the following code to write map to text file:
// remember to add this two includes at the beginning:
// #include <fstream>
// #include <sstream>
std::ofstream logFile;
logFile.open("log.txt"); // if exists it will be overwritten
std::stringstream ss;
for (auto it = receivedWarningMap.begin(); it != receivedWarningMap.end(); ++it) {
ss << "id=" << static_cast<int>(it->first) << "; wsms=";
for (auto it2 : it->second) {
ss << it2 << "; ";
}
ss << endl;
}
logFile << ss.str();
logFile.close();

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"});
}

Serializing a map<fs::path, fs::path>?

I'm using the cereal library to serialize my classes into files, but am running into trouble with std::map - specifically, maps that use std::filesystem::path.
Say I have an Object class that contains only a map<fs::path, fs::path>, as well as the required serialize function for cereal:
struct Object
{
map<fs::path, fs::path> _map;
template<class Archive>
void serialize(Archive & archive)
{
archive(_map);
}
friend ostream& operator<<(ostream& outs, const Object c)
{
for (auto const &pair: c._map)
std::cout << "{" << pair.first << ": " << pair.second << "}\n";
return outs;
}
};
In my main function, I have:
int main()
{
// create Object
cout << "Creating object..." << endl;
Object o;
fs::path a = "bye.txt";
fs::path b = "hello.txt";
o._map[a] = b;
o._map[b] = a;
cout << "Object created: " << endl;
cout << o;
// serialize
cout << "Serializing object...." << endl;
stringstream ss;
cereal::BinaryOutputArchive oarchive(ss);
oarchive(o);
cout << "Object serialized." << endl;
// write to file
cout << "Writing serialized object to file...." << endl;
ofstream file("serialized_object");
file << ss.str();
file.close();
cout << "Object written to file." << endl;
// read from file
cout << "Reading from file..." << endl;
stringstream ss2;
fs::path ins = "serialized_object";
ifstream file_stream(ins, ios::binary);
ss2 << file_stream.rdbuf();
cereal::BinaryInputArchive iarchive(ss2);
Object out;
iarchive(out);
cout << "Object read from file." << endl;
cout << out;
}
In my output, I see the error when it reads from the serialized file:
Creating object...
Object created:
{"bye.txt": "hello.txt"}
{"hello.txt": "bye.txt"}
Serializing object....
Object serialized.
Writing serialized object to file....
Object written to file.
Reading from file...
terminate called after throwing an instance of 'cereal::Exception'
what(): Failed to read 2573 bytes from input stream! Read 28
My includes are:
#include <iostream>
#include <filesystem>
#include <fstream>
#include <string.h>
#include <map>
#include "cereal/types/map.hpp"
#include "cereal/archives/binary.hpp"
#include "cereal/types/string.hpp"
And I have included the following code at the beginning in order to be able to serialize fs::path:
namespace std
{
namespace filesystem
{
template<class Archive>
void CEREAL_LOAD_MINIMAL_FUNCTION_NAME(const Archive&, path& out, const string& in)
{
out = in;
}
template<class Archive>
string CEREAL_SAVE_MINIMAL_FUNCTION_NAME(const Archive& ar, const path& p)
{
return p.string();
}
}
}
I'm sure I'm missing something obvious, as this is my first time using cereal. Does anyone have any insight as to why I'm running into this issue?
Based on the cereal documentation, you must always use the ios::binary flag when utilizing the BinaryArchive. Here is the specific section from this page in their documentation:
When using a binary archive and a file stream (std::fstream), remember to specify the binary flag (std::ios::binary) when constructing the stream. This prevents the stream from interpreting your data as ASCII characters and altering them.
The other issue is based on how Cereal guarantees that the serialization has completed. Per this link below, the output archive object should go out of scope (invoking the destructor) prior to any attempt to read the archive. They utilize the RAII approach, just like ifstream and ofstream as noted here.
You can greatly simplify your code by allowing the Cereal library perform the writes and reads internally. Here is a modified version of your main function:
int main()
{
// create Object
cout << "Creating object..." << endl;
Object o;
fs::path a = "bye.txt";
fs::path b = "hello.txt";
o._map[a] = b;
o._map[b] = a;
cout << "Object created: " << endl;
cout << o;
cout << "Serializing object...." << endl;
// scope boundary (a function call would be cleaner)
{
ofstream ofstm("serialized_object", ios::binary);
cereal::BinaryOutputArchive oarchive(ofstm);
// serialize
oarchive(o);
}
cout << "Object serialized and written." << endl;
// deserialize the object by reading from the archive
Object out;
// scope boundary (a function would be cleaner)
{
ifstream istm("serialized_object", ios::binary);
cereal::BinaryInputArchive iarchive(istm);
//deserialize
iarchive(out);
}
cout << "Object read from file." << endl;
cout << out;
}
I hope this helps. Please test this all out prior to utilization.

print vector of objects within an object

I'm trying to print an object Order (actually a vector of Orders). Order has some data members, including a vector with other objects, Purchase.
I can print the vector<Purchase> to cout on its own, and I can print vector<Objects> if I ignore the vector<Purchase> member. But the tricky part is to print vector<Objects> with vector<Purchase> included.
Here is my code:
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <algorithm>
#include <sstream>
using namespace std;
struct Purchase {
string name;
double unit_price;
int count;
};
struct Order {
string name;
string adress;
double data;
vector<Purchase> vp;
};
template<typename Iter> //this is my general print-vector function
ostream& print(Iter it1, Iter it2, ostream& os, string s) {
while (it1 != it2) {
os << *it1 << s;
++it1;
}
return os << "\n";
}
ostream& operator<<(ostream& os, Purchase p) {
return os << "(" << p.name << ", " << p.unit_price << ", " << p.count << ")";
}
ostream& operator<<(ostream& os, Order o) {
vector<Purchase> vpo = o.vp;
ostringstream oss;
oss << print(vpo.begin(), vpo.end(), oss, ", "); //This is what I would like to do, but the compiler doesn't like this conversion (from ostream& to ostringstream)
os << o.name << "\n" << o.adress << "\n" << o.data << "\n"
<< oss << "\n";
return os;
}
int main() {
ifstream infile("infile.txt");
vector<Order> vo;
read_order(infile, vo); //a function that reads a txt-file into my vector vo
print(vo.begin(), vo.end(), cout, "");
return 0;
}
As you can see, I had the idea to use ostringstreams as a temporary variable, that I would store the vector<Purchase> before I pass it on to the ostream& os. But this is a no go. What would be a good solution to this problem?
I am fairly new to C++ and are just learning the different uses of streams, so please bear with me if this is a stupid question.
Looks like you have two minor typos.
First, remove the indicated portion:
oss << print(vpo.begin(), vpo.end(), oss, ", ")
// ↑↑↑↑↑↑↑
Then, later in that same function, you cannot stream a stringstream, but you can stream the string serving as its underlying buffer, so use std::stringstream::str():
os << o.name << "\n" << o.adress << "\n" << o.data << "\n"
<< oss.str() << "\n";
// ↑↑↑↑↑↑
With those fixes in place, and the missing read_order function abstracted away, your program compiles.
The easiest way is to write an overload of operator<< that takes a const reference to a std::vector<Purchase> and then just stream the vector into the ostream:
std::ostream& operator<<(std::ostream& os, const std::vector<Purchase>& v);

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.

Why is the size of my vector full of structs so large when I load it's serialization in from a file?

As most of you may be following my line of questions already know, i'm trying to create a program that can serialize multiple structs to a .dat file, read them back in via loading it's serialization, edit the contents, and then re-write them to the file and so on. It's a inventory program I am trying to do and I can't get it to work for the life of me.
The file i'm loading in, is blank. My program takes like 10 seconds to even load and now I know why. It's cause the size of my vector is like 250 thousand. Oh wait... this time I ran it the size of my vector was 5,172,285. Thats a pretty big vector full of structs. There aren't any run-time or compile errors, but I am pretty sure I am doing something wrong. The file i'm loading in is completely blank too.
Code:
// Project 5.cpp : main project file.
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
using namespace System;
using namespace std;
#pragma hdrstop
int checkCommand (string line);
template<typename T>
void writeVector(ofstream &out, const vector<T> &vec);
template<typename T>
vector<T> readVector(ifstream &in);
struct InventoryItem {
string Item;
string Description;
int Quantity;
int wholesaleCost;
int retailCost;
int dateAdded;
} ;
int main(void)
{
cout << "Welcome to the Inventory Manager extreme! [Version 1.0]" << endl;
ifstream in("data.dat");
if (in.is_open()) { cout << "File \'data.dat\' has been opened successfully." << endl; } else { cout << "Error opening data.dat" << endl; return 0;}
cout << "Loading data..." << endl;
vector<InventoryItem> structList = readVector<InventoryItem>( in );
cout <<"Load complete." << endl;
while (1)
{
string line = "";
cout << "There are currently " << structList.size() << " items in memory.";
cout << endl;
cout << "Commands: " << endl;
cout << "1: Add a new record " << endl;
cout << "2: Display a record " << endl;
cout << "3: Edit a current record " << endl;
cout << "4: Exit the program " << endl;
cout << endl;
cout << "Enter a command 1-4: ";
getline(cin , line);
int rValue = checkCommand(line);
if (rValue == 1)
{
cout << "You've entered a invalid command! Try Again." << endl;
} else if (rValue == 2){
cout << "Error calling command!" << endl;
} else if (!rValue) {
break;
}
}
system("pause");
return 0;
}
int checkCommand (string line)
{
int intReturn = atoi(line.c_str());
int status = 3;
switch (intReturn)
{
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
status = 0;
break;
default:
status = 1;
break;
}
return status;
}
template<typename T>
void writeVector(ofstream &out, const vector<T> &vec)
{
out << vec.size();
for(vector<T>::const_iterator i = vec.begin(); i != vec.end(); i++)
{
out << *i;
}
}
ostream &operator<<(ostream &out, const InventoryItem &i)
{
out << i.Item << i.Description;
out << i.Quantity;
out << i.wholesaleCost << i.retailCost;
out << i.dateAdded;
return out;
}
istream &operator>>(istream &in, InventoryItem &i)
{
in >> i.Item >> i.Description;
in >> i.Quantity;
in >> i.wholesaleCost >> i.retailCost;
in >> i.dateAdded;
return in;
}
template<typename T>
vector<T> readVector(ifstream &in)
{
size_t size;
in >> size;
vector<T> vec;
vec.reserve(size);
for(unsigned int i = 0; i < size; i++)
{
T tmp;
in >> tmp;
vec.push_back(tmp);
}
return vec;
}
Can someone simply show me how to turn this in to a program that can actually write serialized vectors full of structs to a file, and then read them back in so I can edit them and store them back for loading later? Oh my goodness what a ride this has been!
THANK YOU for any help you can provide!
You say the file is actually empty. The first line in readVector is this:
in >> size;
What do you suppose is actually going to end up in size? Since it is empty, this will result in an error, which you aren't detecting. The variable size will remain uninitialised - hence you see weird values in it, since it takes whatever value happened to be in memory at that time. You can check the state of the stream by using the check:
if (in)
Treating it in boolean context tells you if an error occurred - this will also cover things like it being unable to read in a valid integer variable. I suggest you should figure out how to run your program in a debugger, you will be able to step through your code and see what the values of the variables are at a given time.
So if your file is blank and your doing:
size_t size;
in >> size;
vector<T> vec;
vec.reserve(size);
What do you think will happen? Size cannot be read and uses a random value
If your input file is blank, then the vector should be empty. You shouldn't go past the line:
if (in.is_open())
-- your program (when I run it on my machine) quits.
Can you explain why you have the following?
#include <String>
using namespace System;
#pragma hdrstop
The easiest to implement is to read in the file contents in one go, keep the items in memory, edit them in-memory and write out to file once through with editing. This is of course not a very good technique from memory footprint or performance point of view. But then, the problem you are trying to solve is not trivial. Read FAQ 36 to gain a better understanding of your task.
You are using blank files, but when you go to load the file, you are looking for a size. When the file does not have a size in the file, you may be getting garbage into your size variable.
So trying putting a zero in the first line of the 'data.dat' file.
Edit:
The above suggestion was just a temporary fix. You could try this:
in >> size;
//input operation failed
if(in.fail())
//return, or whatever you need to do