I am trying to parse the data obtained from GET / POST request using C in a MCU based project. I must say upfront that I haven't work with c and c++ for some time, so any suggestion is welcome.
So, the data arriving the MCU has the following markup:
HTTP/1.1
Authorization: Basic blablah
Content-Type: application/json
{
redLedBrightness:50,
redLedIsOn:true,
greenLedBrightness:70,
greenLedIsOn:false,
}
where the end beginnning of the lines are given respectively by \r and \n.
My approach entailed processing line by line, and searching the corresponding key (e.g. Authorization or redLedBrightness) and its value (e.g. blahblah or 50). I wrote the following code, which seemed to be working fine in the compiler I was using, but not in the MCU.
One of problems seems to be in the statement *tempInDataP = *index; since the MCU stops hanging if I comment it out. Of course I need this line, but I cannot figure out where the problem(s) is (are). I suspect the compiler I am using is not showing all the errors.
Here is an adaption of the code:
#include <iostream>
#include <string>
#include <cstring>
#define IN_DATA_SIZE 100
#define TEMP_IN_DATA_SIZE 10
using namespace std;
char mock[] = "Authentication:YERL90,\r\nredLedBri:400,\r\nredLedOn:true\r\n";
char * mockP = mock;
char inData[IN_DATA_SIZE];
char * inDataP = inData;
char tempInData[TEMP_IN_DATA_SIZE];
char * tempInDataP = tempInData;
const char * auth = "YERL90";
bool isAuthenticated = false;
void printBuffer(char * buf) {
for (char *p = buf; *p; p++) {
cout << *p;
}
}
void resetBuffer(char * buf, int _size)
{
memset(buf, 0, _size);
}
/**
* Returns true if keyWord exists and update the variable tempInData
* with the response / value
*/
bool parseResponse(char * res, const char * keyWord, int dataShift ) {
cout << "\nParsing:" << res << endl;
char * index;
// Reset temp in data buffer and its pointer
resetBuffer(tempInData, TEMP_IN_DATA_SIZE);
tempInDataP = &tempInData[0];
// Search for a member /keyword inside the response
index = strstr(res, keyWord);
if (index) {
// Keyword exists in the response
// Move the index to the beginning of the value (key:value)
index = index + strlen(keyWord) + dataShift;
while ((*index != '\r') && (*index != ',')) {
*tempInDataP = *index;
tempInDataP++;
index++;
}
// Terminate the string with a null
*tempInDataP = '\0';
return true;
} else {
cout << "-bul" << endl;
//Object was not found
return false;
}
}
bool checkAuth(const char * _auth) {
if (strcmp (auth, _auth) == 0)
return true;
else
return false;
}
int main()
{
while(*mockP) {
// Simulating, reading data from the channel
*inDataP=*mockP;
//Parsing line by line
if (*inDataP == '\n') {
//Check for authentication if not done yet
if(!isAuthenticated) {
cout << "Authenticating ..." << endl;
if(parseResponse(inData, "Authentication", 1)) {
cout << "Key exists ..." << endl;
if(checkAuth(tempInData)) {
cout << "Correct Password" << endl;
isAuthenticated = true;
} else {
cout << "Password Invalid" << endl;
// Prepare headers
// Send response with unauthorized access
// stop connection
break;
}
} else {
cout << "Data not found" << endl;
}
} else {
// User is logged in
cout << "User is logged in" << endl;
if(parseResponse(inData, "redLedBri", 1)) {
cout << "Data exists:" << tempInData << endl;
// update local variables
} else {
cout << "Data not found" << endl;
}
}
resetBuffer(inData, IN_DATA_SIZE);
inDataP = &inData[0];
}
else {
inDataP++;
}
mockP++;
}
//close connection and log the user out
return 0;
}
I am open for other alternatives, as this implementation seems a bit cumbersome depending on the JSON file sent to the MCU.
I see potential buffer overruns. You trust your input too much. You assume it will end with '\r' or ',' characters, and if it doesn't then you will overrun the bounds of your array(s) and access arbitrary memory.
At the very least, pass the maximum length with each array pointer, and check in each loop iteration that you NEVER access an element beyond the last address of the array.
Related
I'm having a little trouble with my code. It's pretty much supposed to open two files, and compare the first twenty line of the file "StudentAnswers.txt" [inputted as a char into a char array] against a char value in (each line of another file) "CorrectAnswers.txt" in another array at the same position (index). It's like a linear search, but the same position in the arrays. Then a report should be displayed, detailing which question the student missed, the given answer, the correct answer, and if the student passed (got >= 70%) or not, like the following:
Report for Student X:
2 (A/D), 3 (C/D), 5(D/A)
This student passed the exam!
Then it should clear the SAArray, and feed the next twenty lines from StudentAnswers.txt, and start the process all over again. I guess the program has to determine the number of students from (lines of 'StudentAnswers.txt' file / 20).
I'm having trouble displaying the report, and having the array clear itself after the program. I'm guessing this can be done with a while loop and an accumulator for the number of students (to be determined by above equation).
Also, Visual Studio seems to go to "Missed __ questions for a total of ___ %", and then keep looping -858993460.
Any help would be appreciated.
#include <iostream>
#include <fstream>
#include <string>
#include <array>
#include <algorithm>
using namespace std;
void GradeReturn(char[], char[], int, int, int);
string PassFail(float);
int main()
{
ifstream SA("StudentAnswers.txt");
ifstream CA("CorrectAnswers.txt");char CAArray[20];
char SAArray[20];
// char SA2Array[20];
bool isCorrect;
int correct;
int incorrect;
int counter;
correct = 0;incorrect = 0;
counter = 0;
cout << endl;
if (!SA.fail())
{
cout << "'StudentAnswers.txt' file opened successfully." << endl;
cout << "'CorrectAnswers.txt' file opened successfully." << endl << endl;
int a = 0;
int b = 0;
while (a < 20)
{
CA >> CAArray[a];
a++;
} // while loop to feed char into the array
while (b < 20)
{
SA >> SAArray[b];
b++;
}
} // while loop to feed char into array
CA.close(); // closing "CorrectAnswers.txt"
SA.close(); // closing "StudentAnswers.txt"
GradeReturn(&CAArray[counter], &SAArray[counter], correct, incorrect, counter);
return 0;
}
void GradeReturn(char CAArray[], char SAArray[], int correct, int incorrect, int counter)
{
float percent;
float hundred;
int student;
int catcher[20];
int writeCatcher; int starter;
int catcher_size;
student = 0;
writeCatcher = 0;
catcher_size = ((sizeof catcher) / 4);
while (counter < 20)
{
if ((CAArray[counter]) == (SAArray[counter]))
{
correct++;
cout << "Good job!" << endl;
} // correct handling
else
{
incorrect++;
cout << "You got question " << counter << " wrong." << endl;
counter >> catcher[writeCatcher];
writeCatcher++;
} // incorrect handling
counter++;
} // while loop to determine if a student got a question right or wrong
static_cast <float> (incorrect); // float conversion
cout << endl; // for cleanliness
percent = ((static_cast <float> (correct)) / 20); // percentage
hundred = percent * 100;
PassFail(percent);
if (PassFail(percent) == "pass")
{
student++;
cout << "Report for Student " << student << ":" << endl;
cout << "-----------------------------" << endl;
cout << "Missed " << incorrect << " questions out of 20 for ";
cout << hundred << " % correct." << endl << endl;
starter = 0;
while (starter < (sizeof catcher)
{
if(1=1)
{
catcher_size
}
else
{
cout << "";
starter++;
}
}
}
else if (PassFail(percent) == "fail")
{
student++;
cout << "Missed " << incorrect << " questions out of 20 for ";
cout << hundred << " % correct." << endl << endl;
while (starter < catcher_size)
{
if ((catcher[starter]) == -858993460)
{
starter++;
}
else
{
cout << "";
starter++;
}
}
}
return;
}
string PassFail(float percent)
{
if (percent >= 0.70) // if <pass>
{
return "pass";
}
else // if <fail>
{
return "fail";
}
cout << endl;
}
To get a loop you should keep streams open instead of closing them after reading 20 lines.
As pseudo code that would be:
a = 0;
while(streams_not_empty)
{
CA >> CAArray[a];
SA >> SAArray[a];
++a;
if (a == 20)
{
GradeReturn(&CAArray[counter], &SAArray[counter], correct, incorrect, counter);
a = 0; // Reset a
}
}
CA.close(); // closing "CorrectAnswers.txt"
SA.close(); // closing "StudentAnswers.txt"
You would also need to pass correct, incorrect, counter by reference so that the GradeReturn can change their value and their by do the accumulation.
Like:
void GradeReturn(char CAArray[], char SAArray[], int& correct, int& incorrect, int& counter)
Further you shouldn't rely on being able to read exactly Nx20 lines from the files every time. A file could have, e.g. 108 (5x20 + 8) lines, so you code should be able to handle the with only 8 lines. In other words, don't hard code 20 in your function like while (counter < 20). Instead pass the number of lines to be handled and do while (counter < number_to_handle).
Something like this as pseudo code:
a = 0;
while(streams_not_empty)
{
CA >> CAArray[a];
SA >> SAArray[a];
++a;
if (a == 20)
{
GradeReturn(&CAArray[counter], &SAArray[counter], correct, incorrect, counter, a);
// ^
a = 0; // Reset a
}
}
if (a != 0)
{
// Process the rest
GradeReturn(&CAArray[counter], &SAArray[counter], correct, incorrect, counter, a);
}
CA.close(); // closing "CorrectAnswers.txt"
SA.close(); // closing "StudentAnswers.txt"
One problem you have is you're trying to compare C-style strings with the == operator. This will compare them essentially as if they were pointers to char, i.e. compare whether they point at the same location in memory, not compare the contents of the string. I urge you to look up array-decay and c-string variables to understand more.
Specifically, if (PassFail(percent) == "pass") isn't going to do what you want it to. strcomp doc, strncmp doc using std::string variables instead of c-style strings would all work, but it would be better simply to compare percent to a value, i.e. if(percent >= 0.70 directly instead of calling PassFail and comparing a string.
There are many other issues here also, you at one point call PassFail but do nothing with the return value. The only side affect of PassFail is cout << endl, if that's what you intend, it's a poor decision and hard to read way to put a newline on the console.
Try asking your compiler for more warnings, that's often helpful in finding these types of issues. -Wall -Wextra work for gcc, you may have to read your compiler manual...
I'm working on a project that deals with creating two strings, a username and a password. The two elements make an object of an Account. In the main, there is an Array of Accounts that is initialized at 10.
I have a Save & Quit option, which saves the Username on one line and the Password on the next in the same file. A pair of lines signifies another account.
My question is, how do you properly save the data from the Array of Accounts, then load the data from the previous Array of Accounts?
I get a std::bad_alloc memory error every time I try the loadAccounts() function. I've several different methods, but to no avail.
So far I've come up with this for saving the array (works just as it should so far) :
void saveAccounts(Account accs [], int numIndexes)
{
std::ofstream savefile("savedata.sav", std::ofstream::binary); // By re-initializing the file, the old contents are overwritten.
for (int i = 0; i < numIndexes; i++)
{
savefile << accs[i].getUsername() << endl;
savefile << accs[i].getPassword() << endl;
}
savefile.close();
}
As for my loading function I have :
Account* loadAccounts() // Load the data from the file to later print to make sure it works correctly.
{
cout << "LOADING ACCOUNTS!" << endl;
std::ifstream loadfile("savedata.sav", std::ifstream::binary);
Account * acc_arr; // The "Array" to be returned.
Account tmp_arr [10]; // The array to help the returned "Array."
acc_arr = tmp_arr; // Allowing the "Array" to be used and returned because of the actual array.
if (loadfile.is_open())
{
int i = 0;
while (loadfile.good())
{
cout << "Loadfile is good and creating Account " << i+1 << "." << endl; // For my own benefit to make sure the data being read is good and actually entering the loop.
std::string user;
std::getline(loadfile, user);
std::string pass;
std::getline(loadfile, pass);
Account tmpAcc(user, pass);
tmp_arr[i] = tmpAcc;
++i;
}
Account endAcc = Account(); // The default constructor sets Username to "NULL."
tmp_arr[i] = endAcc;
}
loadfile.close();
cout << "ACCOUNTS LOADED SUCCESSFUL!" << endl;
return acc_arr;
}
I've gathered that I can return an array by using a pointer and an actual array to do that same, since an array can't actually be returned.
I try to use the returned array here, which I'm trying to "copy" over the loaded array to the Array that will actually be printed. Later, I'll print the array (acc_arr) to ensure that the loaded array was loaded successfully :
else if (selection == 'l' || selection == 'L')
{
Account * tmp_acc_arr = new Account [10];
tmp_acc_arr = loadAccounts();
_getch();
for (size_t i = 0; i < size_t(10); i++)
{
if (tmp_acc_arr[i].getUsername() == "NULL")
{
break;
}
acc_arr[i] = tmp_acc_arr[i];
cout << "Added Account " << i << " succesfully." << endl;
}
}
The error is caused by this last block of code. I've checked to make sure the data copied correctly by using
EDIT: Awkward... by using an if statement to make sure the data within the tmp_acc_arr actually has data stored once it was returned and initialized in the main.
tmp_arr ist local in loadAccounts and on the stack. It will be invalid once loadAccounts() returns. Your return value is an invalid stack-pointer.
You could hand your pointer tmp_acc_arr to the function as an argument and fill it with the values from your file.
You should also check for overflow or better use STL containers like std::vector.
edit
void loadAccounts(Account * acc_memory, std::allocator<Account> alloc, size_t acc_array_size) // Load the data from the file to later print to make sure it works correctly.
{
Account *end_of_construction = acc_memory;
try
{
cout << "LOADING ACCOUNTS!" << endl;
std::ifstream loadfile("savedata.sav", std::ifstream::binary);
if (loadfile.is_open() && loadfile.good())
{
size_t i = 0;
for (size_t i=0; i<acc_array_size; ++i)
{
if (loadfile.good())
{
cout << "Loadfile is good and creating Account " << i+1 << "." << endl; // For my own benefit to make sure the data being read is good and actually entering the loop.
std::string user, pass;
std::getline(loadfile, user);
if (loadfile.good())
{
std::getline(loadfile, pass);
alloc.construct(end_of_construction++, user, pass);
}
else alloc.construct(end_of_construction++);
}
else alloc.construct(end_of_construction++);
}
}
loadfile.close();
cout << "ACCOUNTS LOADED SUCCESSFUL!" << endl;
}
catch (...)
{
size_t num_constructed = end_of_construction-acc_memory;
for (size_t i=0; i<num_constructed; ++i) alloc.destroy(acc_memory + i);
throw;
}
}
Used like
size_t const num_elements = 10;
std::allocator<Account> acc_alloc;
Account * tmp_acc_arr = acc_alloc.allocate(num_elements);
loadAccounts(tmp_acc_arr, acc_alloc, num_elements);
// do stuff
for (size_t i=0; i<num_elements; ++i) acc_alloc.destroy(tmp_acc_arr + i);
acc_alloc.deallocate(tmp_acc_arr, num_elements);
You return a pointer that points to an array that is destroyed as soon as you exit the function. Using that pointer leads to undefined behavior, that is BAD.
As you observed, array is not a valid return type, so to return it actually you shall put it inside a struct, and return the struct.
// AnE.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include<conio.h>
#include<string.h>
#include<stdlib.h>
#include<iostream>
using namespace std;
// The maximum number of patients in queue
#define MAXPATIENTS 30
// define structure for patient information
struct patient
{
char FirstName[50];
char LastName[50];
char ID[20];
};
// define class for queue
class queue
{
public:
queue (void);
int RegisterPatien (patient p);
int RegisterPatientAtBeginning (patient p);
patient GetNextPatient (void);
int CancelAll (patient * p);
void OutputList (void);
char DepartmentName[50];
private:
int ShowAllPatient;
patient List[MAXPATIENTS];
};
// declare member functions for queue
queue::queue ()
{
// Constructor
ShowAllPatient = 0;
}
int queue::RegisterPatien (patient p)
{
// To add a patient (normally) to the queue (to the end).
// returns 1 if successful, 0 if queue is full.
if (ShowAllPatient >= MAXPATIENTS)
{
// queue is full
return 0;
}
// put in new patient
else
List[ShowAllPatient] = p; ShowAllPatient++;
return 1;
}
int queue::RegisterPatientAtBeginning (patient p)
{
// adds a critically ill patient to the beginning of the queue.
// returns 1 if successful, 0 if queue is full.
int i;
if (ShowAllPatient >= MAXPATIENTS)
{
// queue is full
return 0;
}
// move all patients one position back in queue
for (i = ShowAllPatient-1; i >= 0; i--)
{
List[i+1] = List[i];
}
// put in new patient
List[0] = p; ShowAllPatient++;
return 1;
}
patient queue::GetNextPatient (void)
{
// gets the patient that is first in the queue.
// returns patient with no ID if queue is empty
int i; patient p;
if (ShowAllPatient == 0) {
// queue is empty
strcpy(p.ID,"");
return p;}
// get first patient
p = List[0];
// move all remaining patients one position forward in queue
ShowAllPatient--;
for (i=0; i<ShowAllPatient; i++)
{
List[i] = List[i+1];
}
// return patient
return p;
}
int queue::CancelAll (patient * p)
{
// removes a patient from queue.
// returns 1 if successful, 0 if patient not found
int i, j, found = 0;
// search for patient
for (i=0; i<ShowAllPatient; i++)
{
if (stricmp(List[i].ID, p->ID) == 0)
{
// patient found in queue
*p = List[i]; found = 1;
// move all following patients one position forward in queue
ShowAllPatient--;
for (j=i; j<ShowAllPatient; j++)
{
List[j] = List[j+1];
}
}
}
return found;
}
void queue::OutputList (void)
{
// lists entire queue on screen
int i;
if (ShowAllPatient == 0)
{
cout<< "Queue is empty";
}
else
{
for (i=0; i<ShowAllPatient; i++)
{
cout << "First Name : " << List[i].FirstName<<endl;
cout << "Last Name : " << List[i].LastName<<endl;
}
}
}
// declare functions used by main:
patient InputPatient (void)
{
// this function asks user for patient data.
patient p;
cout<<endl<<endl;
cout << "Please enter the information of the Patient"<<endl<<endl;
cout << "First name: "<<endl<<endl;
cin.getline(p.FirstName, sizeof(p.FirstName));
cout << "Last name: "<<endl<<endl;
cin.getline(p.LastName, sizeof(p.LastName));
// check if data valid
if (p.FirstName[0]==0 || p.LastName[0]==0 || p.ID[0]==0)
{
// rejected
strcpy(p.ID,"");
cout << "Error: Data not valid. Operation cancelled.";
getch();
}
return p;
}
void OutputPatient (patient * p)
{
// this function outputs patient data to the screen
if (p == NULL || p->ID[0]==0)
{
cout << "No patient";
return;
}
else
cout << "Patient Information:"<<endl<<endl;
cout << "First name: " << p->FirstName<<endl<<endl;
cout << "Last name: " << p->LastName<<endl<<endl;
}
int ReadNumber()
{
// this function reads an integer number from the keyboard.
// it is used because input with cin >> doesn't work properly!
char buffer[20];
cin.getline(buffer, sizeof(buffer));
return atoi(buffer);
}
void DepartmentMenu (queue * q)
{
// this function defines the user interface with menu for one department
int choice = 0, success; patient p;
while (choice != 6)
{
// print menu
system("CLS");
cout << "<< || Welcome || >> "<<endl << q->DepartmentName<<endl;
cout << "Please enter your choice:"<<endl<<endl;
cout << "1: Register patient"<<endl;
cout << "2: Serve patient "<<endl;
cout << "3: Cancel all patients from queue"<<endl;
cout << "4: Show all patient"<<endl;
cout << "5: Exit"<<endl<<endl<<endl<<endl<<endl<<endl<<endl<<endl;
choice = ReadNumber();
switch (choice)
{
case 1: // Add new patient
p = InputPatient();
if (p.ID[0])
{
success = q->RegisterPatien(p);
system("CLS");
if (success)
{
cout << "Patient added:"<<endl<<endl;
}
else
{
// error
cout << "Sorry: The queue is full. We Cannot add any patient:";
}
OutputPatient(&p);
cout << "Press any key";
getch();
}
break;
case 2: // Call patient for operation /First Come First Surve
p = q->GetNextPatient();
system("CLS");
if (p.ID[0])
{
cout << "Patient to operate:";
OutputPatient(&p);
}
else
{
cout << "Currently there is no patient to operate.";
}
cout << "Press any key to contiune";
getch();
break;
case 3: // Cancel all from queue
p = InputPatient();
if (p.ID[0])
{
success = q->CancelAll(&p);
system("CLS");
if (success)
{
cout << "Patient removed:";
}
else
{
// error
cout << "Sort: We cannot find patient:";
}
OutputPatient(&p);
cout << "Press any key to contiune";
getch();
}
break;
case 4: // Show all patient -> queues
system("CLS");
q->OutputList();
cout << "Press any key";
getch(); break;
}
}
}
// the main function defining queues and main menu
void main ()
{
int i, MenuChoice = 0;
// define queue
queue department[1];
// set department name
strcpy_s (department[0].DepartmentName, "To Emergency Department");
while (MenuChoice != 2)
{
system("CLS");
// Cout menu
cout<<"\n------------------------------------\n";
cout << "Welcome to Waiting Room Management System"<<endl;
cout<<"---------------------------------------\n";
cout << "Please Select a Number from the following menu:"<<endl<<endl;
for (i = 0; i < 1; i++)
{
// write menu item for department i
cout<< "" << (i+1) << ": "<< department[i].DepartmentName;
cout<<endl;
}
cout << "2: Exit"<<endl;
// get user choice
MenuChoice = ReadNumber();
// is it a department name?
if (MenuChoice >= 1 && MenuChoice <= 1)
{
// call submenu for department
// (using pointer arithmetics here:)
DepartmentMenu (department + (MenuChoice-1));
}
}
}
Okay, it's Vc++ for waiting room. You can see the code is working well, but I have problem with generating ID! I need to generate ID for each patient (auto-generate by system). How I can generate ID for my queue?
Thanks alot !
Usually, you'd do it by putting a static variable in that class, and each time you get a new patient, you assign its current value to the current patient, then increment it.
class patient {
// ...
int id;
static int current_id; // added
patient() : id(current_id++) {} // added
};
int patient::current_id; // added
The other answers are great, but the currently accepted one isn't actually thread safe as pointed out by another user.
To make a thread safe ID generation function, we can use atomics! Here is a modification of the currently accepted answer that makes it thread safe.
#include <atomic> //std::atomic_uint32_t
class patient
{
// ...
uint32_t id;
static std::atomic_uint32_t current_id; // added
patient() : id(current_id++) {} // added
};
uint32_t patient::current_id; // added
std::atomic_uint32_t is a 32 bit unsigned integer, that (because its atomic) wont have any data races if written to at the same time by two different threads.
I also changed the integer to be unsigned. This is because the ID will never be negative, so it makes sense to make it unsigned.
https://en.cppreference.com/w/cpp/atomic/atomic
If you want unique ids, you can generate a GUIDs. For VC++, you can use:
extern C
{
#include <Rpc.h>
}
//...
UUID id;
UuidCreate ( &id );
You can use a static variable when you create a patient (if you want it there) that increment itself in the constructor, or a variable in you're queue that increment when you add a patient (if you want to only assign an ID when in the queue).
But in your case, I think you want the first solution (static variable in the constructor).
I had the same need for an SQL-database and ended up with this...
Warning: Mostly an example of bad programming with mixing C and C++ (still need to convert old code), but it was meant to convey the idea. I am sure better solutions exist...
It generates (unfortunately) large character based ID's, based on current date and time. This means that every next auto-generated ID needs to be bigger: if it is equal or smaller, a millisecond timer is appended and the code will wait until the ID has become unique. It works without the milliseconds as well, but this will lead to long delays if you need to generate many ID's at once (a second for each pause). If desired, it will also add an optional postfix (can help omit the milliseconds).
My experience with simple counters is, that they might duplicate under certain circumstances, which had me look for an alternative.
Careful: Another user on another computer might generate equal ID's... (same second or millisecond)
TUID::TUID()
{
*LastID = 0; // char [80] - Global within object
}
void TUID::GetToday (int *d, int *m, int *y)
{
time_t now;
struct tm *ltm;
time (&now);
ltm = localtime (&now);
*y = ltm->tm_year + 1900;
*m = ltm->tm_mon + 1;
*d = ltm->tm_mday;
}
void GetTime (int *h, int *m, int *s)
{
time_t t = time(0); // get time now
struct tm * now = localtime( & t );
*h = now->tm_hour;
*m = now->tm_min;
*s = now->tm_sec;
}
const char *TUID::NewUID (bool bPreviousAttemptFailed, const char *_postfix)
{
int d, m, y,
_h, _m, _s;
bool bSameAsLastUID;
char _uid [80];
GetToday (&d, &m, &y);
do
{
GetTime (&_h, &_m, &_s);
sprintf (_uid, "%04d%02d%02d_%02d%02d%02d%s", y, m, d, _h, _m, _s, _postfix);
bSameAsLastUID = (strcmp (_uid, LastUID) <= 0);
if (bPreviousAttemptFailed || bSameAsLastUID)
sprintf (_uid + strlen (_uid), "_%d",
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count());
}
while (strcmp (_uid, LastUID) <= 0);
strcpy (LastUID, _uid);
return LastUID;
}
This results in ID's like this:
20170816_115904 (no post-fix)
20170816_115904i (with post-fix keeping id unique, preventing milliseconds)
20170816_115904i_6427264 (auto-added milliseconds to keep ID unique)
20170816_115904i_6427265
20170816_115904i_6427266
I'm trying to make a program that looks for syntax and everytime it goes from state to state
It needs to indicate that state. I'm getting different output that I shouldn't have got.
using namespace cppfsm;
#include <vector>
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
using std::vector;
int cppfsm::updateState(int& state, char c) {
const int state1 = 1;
const int state2 = 2;
switch (state) {
case state1:
if (c == '/')
cout << "1" << endl;
// do stuff; update state
else if (c == '"')
cout << "1" << endl;
// do something else; update state
case state2:
if (c == '/')
cout << "1" << endl;
// do stuff; update state
else if (c == '"')
cout << "1" << endl;
// do something else; update state
}
return 0;
}
void testFSM(string s) {
vector<int> stlist; // list of states.
int cstate = start;
for (unsigned long i = 0; i < s.length(); i++) {
stlist.push_back(updateState(cstate,s[i]));
}
// push the last state:
stlist.push_back(cstate);
cout << s << endl;
for (unsigned long i = 0; i < stlist.size(); i++) {
cout << stlist[i];
}
cout << endl;
}
int main() {
// the finite state machine:
string input;
while(getline(cin,input)) {
cout << " ";
testFSM(input);
}
return 0;
}
the output should be looking like this.
the numbers are the states when going from 1 to another
$ echo "int x; // holds stuff" | ./fsm
int x; // holds stuff
0111010042222222222222
$ echo 'cout << "some string";' | ./fsm
cout << "some string";
01111000033333333333300
$ echo 'cout << "\"escape\" chars are fun";' | ./fsm
cout << "\"escape\" chars are fun";
011110000353333333533333333333333300
But my output comes out to be all 0000......s. How do I fix this problem?
If you're wondering why stlist is all 0's, take a look at the return statement for updateState:
return 0;
}
Compare this with your code for populating stlist:
stlist.push_back(updateState(cstate,s[i]));
As far as I can tell, all 0's is the correct behavior of this code. Obviously, this is not the expected or logical behavior, so I suggest changing updateState:
int cppfsm::updateState(int& state, char c) {
// ...
return state;
}
Now when you run the code stlist should contain each state change as intended.
It looks like you aleays call updateState with the same value, start. That value ist not handled in the switch, so the function returns zero. This means tha you just keep appending zeros to the stlist vector.
Try handling the start state in the switch, and the return value of the updateState function should be assigned to the cstate variable.
Your code never checks you start state:
switch (state) {
case state1: /* ... */
case state2: /* ... */
case start : /* ... */
}
Sorry scratch my last post, it's way to late =S
But basically I'm having problems sending out the buffer I created. Just need to know where I'm going wrong =( or if theres a better way.
------ Client Sending Username -------
int bufferSize = 32;
char messageBuffer[bufferSize];
char* message;
if (userName.size() > 8)
{
cout << "Invalid username : Greater than 8 characters" << endl;
}
else
{
switch(regType)
{
case(REGISTER):
{
cout << "Registered name: " << userName << endl;
messageBuffer[0] = 1;
messageBuffer[1] = 0;
for (int i = 2; i < (userName.size() + 2); i++)
{
messageBuffer[i] = userName[(i - 2)];
}
for (int i = 0; i < (userName.size() + 2); i++)
{
cout << messageBuffer[i];
}
cout << "<<<< messageBuffer " << endl;
message = &messageBuffer[0];
cout << messageBuffer << endl;
//message[(userName.size() + 2)] = '\0';
int messageLen = userName.size() + 2;
if (send(sock, messageBuffer, messageLen, 0) != messageLen)
DieWithError("send() send an invalid name");
}break;
case(UNREGISTER):
{
}break;
}
}
----------- Server (Receiver)------------
char msgRcvBuffer[RCVBUFSIZE];
int recvMsgSize;
if ((recvMsgSize = recv(clntSocket, msgRcvBuffer, RCVBUFSIZE, 0)) < 0)
DieWithError("recv() failed");
msgRcvBuffer[recvMsgSize] = '\0';
string msgType( msgRcvBuffer );
cout << "Message Type " << msgType << endl; <<<<<< Nothing appears when printed
void handleReg(string message, int socket, string ipAddr)
{
// Remove the Prefix
int startIndex = 2;
// Get the username from the message
string userName = message.substr(startIndex, message.size() - startIndex);
cout << "Username " << userName << endl;
For some reason my message string is just 1... =S What i'm trying to do is just get the message from what was sent from client. I'm just tryin to remove the '1' and '0' from the beginning of the buffer. 1 and 0 aren't characters.
Thanks so much for everyones help =)
The conversion from char* to string treats the string as null-terminated. This doesn’t seem to be the case here – in particular, your char array appears to contain 0 characters, so the string will be cut off at this position.
To circumvent this, you need to pass the valid range of characters to the string constructor, in place of only the start pointer:
msgType = string(msgRcvBuffer, msgRcvBuffer + recvMsgSize);
If msgRcvBuffer is of size RCVBUFSIZE then msgRcvBuffer[recvMsgSize] = '\0'; is going to be writing beyond the end of the buffer I think.
Use the std::string constructor that takes a buffer and buffer size parameter:
msgType = std::string(msgRcvBuffer, recvMsgSize);