feeding a stringstream to a class member function - c++11

I am a newbie trying to learn by doing. I want to feed a stringstream into a class member function called "print()" but I get errors. Once this works, I can proceed to write more class member functions that work with the data I feed them.
For now I have created a class that has a member function 'print'.
class Month
{
public:
string m_month;
void print()
{
cout << m_month << endl;
}
};
Next, I initialized 12 months:
Month month1 = { "January" };
Month month2 = { "February" };
Month month3 = { "March" };
etc.
When I call "month1.print();" it prints January which is correct.
I used stringstream and a for loop to concatenate month + 1 to 12 and I want to feed the stringstream to the print function.
stringstream os;
string mValue = "month";
int iValue = 1;
for(int i = 0; i < 12; ++i)
{
os << mValue << "" << iValue << "\n";
iValue += 1;
}
However, the stringstream can't be combined with the print function.
os.print(); and os.str().print();
result in "error: ‘std::stringstream {aka class std::__cxx11::basic_stringstream}’ has no member named ‘print’"
Converting the stringstream to char and then feeding it into the print function results in "error: request for member ‘print’ in ‘cstr’, which is of non-class type ‘const char*’"
const string tmp = os.str();
const char* cstr = tmp.c_str();
cstr.print();
Long story short: What I am trying to do is concatenate month + 1 to 12 and feed that to the class member function "print". This seems trivial but I can't get it to work. Any suggestions?
Edit: Full code:
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
class Month
{
public:
string m_month;
void print()
{
cout << m_month << endl;
}
};
int main()
{
Month month1 = { "January" };
Month month2 = { "February" };
Month month3 = { "March" };
Month month4 = { "April" };
Month month5 = { "May" };
Month month6 = { "June" };
Month month7 = { "July" };
Month month8 = { "August" };
Month month9 = { "September" };
Month month10 = { "October" };
Month month11 = { "November" };
Month month12 = { "December" };
stringstream os; // Initialize stringstream "os"
string mValue = "month"; // Initialize mValue "month"
int iValue = 1; // Initialize iValue "1"
for(int i = 0; i < 12; ++i)
{
os << mValue << "" << iValue << "\n"; // Glue mValue and iValue
// together
iValue += 1; // Increment iValue by one
}
send stringstream "os" to the print function // mock code: Here I want to send month1.print(); month2.print(); etc. to the print function. The output should be January, February etc.
return 0;
}

This doesn't do what you think it does:
for(int i = 0; i < 12; ++i)
{
// iValue is actually unnecessary. You could have just used (i + 1)
os << mValue << "" << iValue << "\n";
iValue += 1;
}
All this does is fill the stringstream with the string:
"month1\nmonth2\nmonth3\nmonth4\nmonth5\nmonth6\nmonth7\nmonth8\nmonth9\nmonth10\nmonth11\nmonth12"
Your intent seemed to be to concat a number to the end of a "month" string, and have them act as the month1, month2... variables that you defined above. That's not how it works. You can't (and shouldn't) try to "dynamically" reference variables like that. In os.print();, the stringstream doesn't act as Month simply because it contains a string with the same name as a Month variable.
Instead, add the variables to some kind of container (like a std::vector), and loop over it:
std::vector<Month> months{ month1, month2, month3, ..., month12 }
for (unsigned int i = 0; i < months.size(); i++)
{
months[i].print();
}

A stringstream should be thought of as a stream like any other, except that it happens to be text and held in memory. So it's cheap to convert it to a string, and in fact they are often used for building strings.
But a "Print" method of a class has no business knowing that a stream is a stringstream. All it should care is that it gets a stream which is text, and is input. In fact the former is a bit hard to enforce due to historical weaknesses stretching back a long way. If you just read the stream byte by byte, pass to std::cout, and terminate on EOF then that's probably OK.

Related

C++ Struct attributes can change within function, but remain unchanged outside scope of function

I'm working on a self imposed challenge which involves implementing a linked list and an append function for it, which is giving me issues seemingly related to variable scope.
The append function loops through each link element until it reads a NULL value and then changes the data value associated with that link to the function input. The test outputs within the function seem to show it is working as intended, but when performing the same test outside the function, even after it is called gives a different output.
template <class T>
struct atom{
T data;
atom<T>* link = NULL;
};
template <class T>
void append_LL(atom<T> first, T input_data){
atom<T>* current_node = &first;
atom<T>* next_node = current_node->link;
int i = 0;
while (i < 4 && next_node != NULL) {
current_node = next_node;
next_node = next_node->link;
i ++;
}
current_node->data = input_data;
current_node->link = (atom<T>*)malloc(sizeof(atom<T>));
cout << "leaving node as: " << current_node->data << endl; //outputs 5
cout << "input nodes data: " << first.data << endl; //outputs 5
}
int main() {
int dd = 5;
atom<int> linked_list;
linked_list.data = 999;
append_LL(linked_list, dd);
cout << linked_list.data << endl; //outputs 999
}
Because you are not sending the same atom. You see the program is making a copy of the linked_list in the main function and sending that copy to the function.
If you want to modify the same linked_list then change
void append_LL(atom<T> first, T input_data){
to
void append_LL(atom<T> &first, T input_data){
That way you are sending the really atom not a copy of it.

Omnet++, A cRuntimeError exception is about to be thrown

I'm currently using Omnet++, and veins, and I have this runtime error appearing suddenly, and I am not able to understand it in order to fix it properly.
Error in module (TraCIDemoRSU11p) RSUExampleScenario.rsu[0].appl
(id=8) at event #6180, t=53.956510612297: Array of size 220 indexed by
220.
TRAPPING on the exception above, due to a debug-on-errors=true configuration option. Is your debugger ready?
I am assuming that it might be related to this message I am sending from the RSU to the vehicles with this code, but I am not sure how it's related.
cplusplus {{
#include "veins/modules/messages/WaveShortMessage_m.h"
}}
class WaveShortMessage;
message DelayedFromControllerMessage extends WaveShortMessage {
string vehiclesList [220] ;
}
I am using omnet++ Version: 5.0 and Veins 4.4
Edited, I'm using the array in these places:
1-
void TraCIDemoRSU11p::sendDelayedMessage(std::list<const char *> vehicleList) {
sentDelayedMessage = true;
//vehicleList = {};
t_channel channel = dataOnSch ? type_SCH : type_CCH;
DelayedFromControllerMessage* dsm = prepareDelayedSM("delayed",dataLengthBits, channel, dataPriority, -1,2,vehicleList);
std::list<const char *>::iterator it = vehicleList.begin();
//const char * v;
char* vx = new char [100];
vx[0] = '\0';
for(int i=0; i<vehicleList.size(); i++){
//v =*it;
strcpy(vx,*it);
//vx = *it;
++it;
dsm->setVehiclesList(i, vx);
}
if (sendDelayedEvt->isScheduled()) {
cancelAndDelete(sendDelayedEvt);
}else {
delete sendDelayedEvt;
}
sendDelayedEvt = new cMessage("delayed evt", SEND_DELAYED_EVT); // create event object to use it in timing
simtime_t offSet = dblrand() * (par("beaconInterval").doubleValue());
TimeStart = simTime() + offSet;
scheduleAt(TimeStart, sendDelayedEvt);
sendDelayedSM(dsm);
}
2-
DelayedFromControllerMessage* BaseWaveApplLayer:: prepareDelayedSM(const char * name, int lengthBits, t_channel channel, int priority, int rcvId,int serial,std::list<const char *>vehicleList ) {
DelayedFromControllerMessage* dsm = new DelayedFromControllerMessage(name);
dsm->addBitLength(headerLength);
dsm->addBitLength(lengthBits);
switch (channel) {
case type_SCH: dsm->setChannelNumber(Channels::SCH1); break; //will be rewritten at Mac1609_4 to actual Service Channel. This is just so no controlInfo is needed
case type_CCH: dsm->setChannelNumber(Channels::CCH); break;
}
dsm->setPsid(0);
dsm->setPriority(priority);
dsm->setWsmVersion(1);
dsm->setTimestamp(simTime());
dsm->setSenderAddress(myId);
dsm->setRecipientAddress(rcvId);
dsm->setSenderPos(curPosition);
dsm->setSerial(serial);
std::list<const char *>::iterator it = vehicleList.begin();
const char * v;
for(int i=0; i<vehicleList.size(); i++){
v =*it;
++it;
VLvar1.push_back(v);
dsm->setVehiclesList(i, v);
}
if ((std::string)name == "beacon") {
DBG << "Creating Beacon with Priority " << priority << " at Applayer at " << dsm->getTimestamp() << std::endl;
}
if ((std::string)name == "delayed") {
DBG << "Creating Data with Priority " << priority << " at Applayer at " << dsm->getTimestamp() << std::endl;
}
return dsm;
}
3-
void MyTraCIDemo11p::onDataDelayed(DelayedFromControllerMessage* dsm) {
int x = 0;
std::string vehichleId = mobility->getExternalId();
for (int i=0 ; i < dsm->getVehiclesListArraySize();i++)
{
vehicleList.push_back(std::string(dsm->getVehiclesList(i)));
}
ttry = std::find(vehicleList.begin(), vehicleList.end(), vehichleId);
if (vehichleId == *ttry){
x = 1;
}
if (state == QUEUING && x == 1){
findHost()->bubble("Received ");
state = WAITING;
stateToString(state);
}
}
The message should be sent from the RSU to the vehicles.
Even without seeing the actual code from the application (appl) or from the configuration file you are using, I am guessing you are trying to get the last element (element 220) from the array.
The error message already tells what the problem is. Your array has a size of 220 and you are trying to use the index 220 which is not possible, since array indexes start at 0. Therefore for addressing the last element in your array, you have to use index 221.
I am not sure if this is the reason for your error:
ttry = std::find(vehicleList.begin(), vehicleList.end(), vehichleId);
if (vehichleId == *ttry){
x = 1;
}
But, It is better to write it this way:
if (std::find(vehicleList.begin(), vehicleList.end(), vehichleId) != vehicleList.end()){
x = 1;
}
I don't recommend referencing thefind iterator (i.e., *ttry) if it is not found, it is like referencing *(vehicleList.end()).
it seems to me you have two veichleList variables, an array in the dsm and another one which is a vector. is this correct? if yes, you should make sure that the veichleList.size() is always less or equal 220.
Check this tutorial to learn how to debug your project in omnet++:
https://docs.omnetpp.org/tutorials/tictoc/part2/
So i ended up finding a solution for this issue just now.
it was by doing this:
cplusplus {{
#include "veins/modules/messages/WaveShortMessage_m.h"
}}
class WaveShortMessage;
message DelayedFromControllerMessage extends WaveShortMessage {
string vehiclesList [] ;
}
=====
DelayedFromControllerMessage* BaseWaveApplLayer:: prepareDelayedSM(const char * name, int lengthBits, t_channel channel, int priority, int rcvId,int serial,std::list<const char *>vehicleList ) {
DelayedFromControllerMessage* dsm = new DelayedFromControllerMessage(name);
dsm->addBitLength(headerLength);
dsm->addBitLength(lengthBits);
switch (channel) {
case type_SCH: dsm->setChannelNumber(Channels::SCH1); break; //will be rewritten at Mac1609_4 to actual Service Channel. This is just so no controlInfo is needed
case type_CCH: dsm->setChannelNumber(Channels::CCH); break;
}
dsm->setPsid(0);
dsm->setPriority(priority);
dsm->setWsmVersion(1);
dsm->setTimestamp(simTime());
dsm->setSenderAddress(myId);
dsm->setRecipientAddress(rcvId);
dsm->setSenderPos(curPosition);
dsm->setSerial(serial);
int NS = 0;
std::list<const char *>::iterator itPD = vehicleList.begin();
const char * vPD;
int i0 = 0;
while(itPD != vehicleList.end()){
vPD = *itPD;
++itPD;
++NS;
dsm->setVehiclesListArraySize(NS);
dsm->setVehiclesList(i0, vPD);
++i0;
VLvar1.push_back(vPD);
}
if ((std::string)name == "beacon") {
DBG << "Creating Beacon with Priority " << priority << " at Applayer at " << dsm->getTimestamp() << std::endl;
}
if ((std::string)name == "delayed") {
DBG << "Creating Data with Priority " << priority << " at Applayer at " << dsm->getTimestamp() << std::endl;
}
return dsm;
}
I removed the array size and placed with a manual counter in BaseWaveApplLayer:: prepareDelayedSM using a while loop. I thought about posting the solution to help others when facing a similar problem. :)

What could cause my program to only read 2 lines of a 3 line input

I have a program that is supposed to take in a paragraph like
Testing#the hash#tag
#program!#when #beginning? a line
or #also a #comma,
and output something like
#the
#tag
#program
#when
#beginning
#also
#comma,
I feel like the logic makes sense, but obviously not because the program never seems to get into the line of input. The problem is almost definitely in the last source file below.
Here is the main source program
#include "HashTagger.h"
#include <string>
#include <iostream>
using namespace hw02;
using namespace std;
int main() {
// Construct an object for extracting the
// hashtags.
HashTagger hashTagger;
// Read the standard input and extract the
// hashtags.
while (true) {
// Read one line from the standard input.
string line;
getline(cin, line);
if (!cin) {
break;
}
// Get all of the hashtags on the line.
hashTagger.getTags(line);
}
// Print the hashtags.
hashTagger.printTags();
// Return the status.
return 0;
}
my header file
#ifndef HASHTAGGER_H
#define HASHTAGGER_H
#include <string>
namespace hw02 {
class HashTagger {
public:
void getTags(std::string line);
void printTags();
private:
std::string hashtags_;
};
}
#endif
and a source file
the test in the source file seems to show that the program only gets to the second line and then stops before grabbing the last 2 hashtags
#include "HashTagger.h"
#include <iostream>
using namespace std;
using namespace hw02;
void HashTagger::getTags(string line) {
// Loop over all characters in a line that can begin a hashtag
int b = 0;
string hashtags_ = "";
for (unsigned int j = 0; j < line.length(); ++j) {
char c = line.at(j);
// if "#" is found assign beginning of capture to b
if (c == '#') {
b = j;
// if the beginning is less than the end space, newline, ".", "?", or "!" found, add substring of the hashtag to hashtags_
}
if (b < j && (c == ' ' || c == '\n' || c == '.' || c == '?' || c == '!' )) {
hashtags_ = hashtags_ + "\n" + line.substr(b, j - b + 1);
b = 0;
//Test// cout << b << "/" << j << "/" << c << "/" << hashtags_ << "/" << endl;
}
}
}
void HashTagger::printTags() {
// print out hashtags_ to the console
cout << hashtags_ << endl;
}
You are redeclaring hashtags_ inside your getTags function. Therefore, all string modifications operate on a local variable instead of the class member variable.
Change the line
string hashtags_ = "";
to
hashtags_ = "";
in order to avoid the redeclaration and operate on the class member variable used for the output later on.
Also, make sure that your input is terminated with two newline characters (\n\n), to avoid breaking out of the main loop too early, or move your check and break statement after the getTags call:
while (true) {
// Read one line from the standard input.
string line;
getline(cin, line);
// Get all of the hashtags on the line.
hashTagger.getTags(line);
if (!cin) {
break;
}
}

What Time Is This Returning

Deep in the sauce here. I haven't worked with time to much so I'm a little confused here. I know there is FILETIME and SYSTEMTIME. What I am trying to get at this point (because it might change) are file that are less than a 20 seconds old. This returning the files and their size and something in seconds, What I'd like to know is where it is filtering by time if it is, and how can I adjust it to suit my needs. Thank you.
using namespace std;
typedef vector<WIN32_FIND_DATA> tFoundFilesVector;
std::wstring LastWriteTime;
int getFileList(wstring filespec, tFoundFilesVector &foundFiles)
{
WIN32_FIND_DATA findData;
HANDLE h;
int validResult=true;
int numFoundFiles = 0;
h = FindFirstFile(filespec.c_str(), &findData);
if (h == INVALID_HANDLE_VALUE)
return 0;
while (validResult)
{
numFoundFiles++;
foundFiles.push_back(findData);
validResult = FindNextFile(h, &findData);
}
return numFoundFiles;
}
void showFileAge(tFoundFilesVector &fileList)
{
unsigned _int64 fileTime, curTime, age;
tFoundFilesVector::iterator iter;
FILETIME ftNow;
//__int64 nFileSize;
//LARGE_INTEGER li;
//li.LowPart = ftNow.dwLowDateTime;
//li.HighPart = ftNow.dwHighDateTime;
CoFileTimeNow(&ftNow);
curTime = ((_int64) ftNow.dwHighDateTime << 32) + ftNow.dwLowDateTime;
for (iter=fileList.begin(); iter<fileList.end(); iter++)
{
fileTime = ((_int64)iter->ftLastWriteTime.dwHighDateTime << 32) + iter->ftLastWriteTime.dwLowDateTime;
age = curTime - fileTime;
cout << "FILE: '" << iter->cFileName << "', AGE: " << (_int64)age/10000000UL << " seconds" << endl;
}
}
int main()
{
string fileSpec = "*.*";
tFoundFilesVector foundFiles;
tFoundFilesVector::iterator iter;
int foundCount = 0;
getFileList(L"c:\\Mapper\\*.txt", foundFiles);
getFileList(L"c:\\Mapper\\*.jpg", foundFiles);
foundCount = foundFiles.size();
if (foundCount)
{
cout << "Found "<<foundCount<<" matching files.\n";
showFileAge(foundFiles);
}
system("pause");
return 0;
}
I don't know what you've done to try to debug this but your code doesn't work at all. The reason is you're passing getFileList() a wstring but then passing that to the ANSI version of FindFirstFile(). Unless you #define UNICODE or use the appropriate compiler option, all system calls will expect char *, not UNICODE.
The easiest fix is to simply change the declaration of getFileList() to this:
int getFileList(const char * filespec, tFoundFilesVector &foundFiles)
Change the call to FindFirstFile() to this:
h = FindFirstFile((LPCSTR)filespec, &findData);
And then change the calls to it to this:
getFileList("c:\\Mapper\\*.txt", foundFiles);
getFileList("c:\\Mapper\\*.jpg", foundFiles);
Your other option is to switch all char strings to wide chars, but either way you need to be consistent throughout. Once you do that the program works as expected.
As for your final question, your program is not filtering by time at all.
Not quite an answer, but you might want to read about file system tunneling.
It may prevent you from what you're trying to do in some situations.

File Isn't Deleting

I have a code that finds files that are less than 20 seconds old. It's finding them, but I can't get them deleted. I am using remove(). I've tried using remove with wildcards but no luck. It has to be something with the syntax. Any help is appreciated. Thank you.
using namespace std;
typedef vector<WIN32_FIND_DATA> tFoundFilesVector;
std::wstring LastWriteTime;
int getFileList(const char * filespec, tFoundFilesVector &foundFiles)
{
WIN32_FIND_DATA findData;
HANDLE h;
int validResult=true;
int numFoundFiles = 0;
h = FindFirstFile((LPCSTR)filespec, &findData);
if (h == INVALID_HANDLE_VALUE)
return 0;
while (validResult)
{
numFoundFiles++;
foundFiles.push_back(findData);
validResult = FindNextFile(h, &findData);
}
return numFoundFiles;
}
void showFileAge(tFoundFilesVector &fileList)
{
unsigned _int64 fileTime, curTime, age;
tFoundFilesVector::iterator iter;
FILETIME ftNow;
CoFileTimeNow(&ftNow);
curTime = ((_int64) ftNow.dwHighDateTime << 32) + ftNow.dwLowDateTime;
for (iter=fileList.begin(); iter<fileList.end(); iter++)
{
fileTime = ((_int64)iter->ftLastWriteTime.dwHighDateTime << 32) + iter- >ftLastWriteTime.dwLowDateTime;
age = curTime - fileTime;
if (age <= (_int64)200000000UL)
{
wcout << " Delete: '" <<endl;
wcout << "FILE: '" << iter->cFileName << "', AGE: " << (_int64)age/10000000UL << " seconds" << endl;
remove("C:\\mapper\\iter->cFileName");
}
else
{
//wcout << " Quit: '" <<endl;
//return;
}
}
}
int main()
{
string fileSpec = "*.*";
tFoundFilesVector foundFiles;
tFoundFilesVector::iterator iter;
int foundCount = 0;
getFileList("c:\\Mapper\\*.txt", foundFiles);
getFileList("c:\\Mapper\\*.jpg", foundFiles);
foundCount = foundFiles.size();
if (foundCount)
{
wcout << "Found "<<foundCount<<" matching files.\n";
showFileAge(foundFiles);
}
system("pause");
return 0;
}
You need to concatenate the strings "C:\mapper\" with whatever is in iter->cFileName. The most simple way of doing that would be to use the std::string class.
The result would look something like this:
remove(string("c:\\mapper\\").append(string(iter->cFileName)).c_str());
This assumes you do not use unicode encoding of your strings, otherwise you need to use std::wstring. To use these classes you will also need to include
#include <string>
in the top of your file.

Resources