Assign different values to different struct variables using loop - c++

I have a struct of Examinee like below (each has 1 id and their scores of different subjects):
struct Examinee
{
string id;
float math, literature, physic, chemistry, biology, history, geography, civic_education, natural_science,
social_science, foreign_language;
};
Now i want to write a function that reads from a string different values and assign them to an Examinee. The string looks like this (each info is separated by a comma):
BD1200001,9,4.0,5.0,10,3.5,7.5,4.25,7.0,7.75,9.25,2.0
This is what i have done so far:
Examinee readExaminee(string line_info) {
//turn line_info to char*
int Line_info_length = line_info.length();
char* info = new char[Line_info_length + 1];
strcpy(info, line_info.c_str());
//create examinee
Examinee examinee;
//read id into examinee by token
char* token = strtok(info, ",");
examinee.id = token;
//read score and assign to subjects
while (token != NULL)
{
float score = strtof(token, NULL);
//assign score to appropriate subject
token = strtok(NULL, ",");
}
delete[] info;
return examinee;
}
The question is: Can i assign each score to each subject in while loop like above? How can i do that? If not, is assigning each score manually the only way?

I'd change the design of Examinee. Something along these lines:
struct Examinee
{
enum Subject {kSubjMath, kSubjLiterature, ..., kSubjForeignLanguage, kSubjCount};
string id;
float scores[kSubjCount];
};
This way you can access scores in a loop, e.g.
for (int subj = 0; subj < Examinee::kSubjCount; ++subj) {
examinee.scores[subj] = some_score;
}
Or access specific score as examinee.scores[Examinee::kSubjLiterature]
If you are unable or unwilling to change Examinee, you can sort of simulate this locally:
Examinee examinee;
float* scores[] = {&examinee.math, &examinee.literature, ..., &examinee.foreign_language};
for (int subj = 0; subj < std::extent_v<scores>; ++subj) {
*scores[subj] = some_value;
}

Related

Passing a member(which is a class) of an array(of class') to a function

I am trying to pass 5th element of an array(Products[]) of class product to another function. The goal is to update the information of the element Product[5]. Everything seems to work fine except information of Product[5] variable is not updating.
Update: Problem solved by removing while(!EOF), thanks to Remy Lebeau.
The relevant part of Class:
class product
{
private:
float Wholesale_Price = 0;
float Retail_Price = 0;
string Supplier = "N/A";
string Note = "N/A";
int Stock_Amount = 0;
public:
string Name="N/A";
void UpdateRetailPrice(float New_Retail_Price)
{
Retail_Price = New_Retail_Price;
}
void UpdateProductAmount(int New_Stock_Amount)
{
Stock_Amount = New_Stock_Amount;
}
void UpdateWholesale_price(float New_Wholesale_Price)
{
Wholesale_Price = New_Wholesale_Price;
}
};
The relevant part of function:
void ReadProductFromFile(product* Products)
{
string Name, Supplier, Note, Wholesale_Price, Retail_Price, Stock_Amount;//wholesale price, stock amount,price are in string so that
//it becomes easy to use getline(). Use stoi() later for turning string to int.
ifstream ReadProductFromFile;
ReadProductFromFile.open("product.txt");
if (!ReadProductFromFile)
{
perror("Product File failed to open");
return;
}
while(!EOF)
{
/*read product info txt file------>*/
getline(ReadProductFromFile, Name);
getline(ReadProductFromFile, Wholesale_Price);
getline(ReadProductFromFile, Retail_Price);
getline(ReadProductFromFile, Stock_Amount);
/*update product info---------->*/
Products->Name = Name;
Products->UpdateWholesale_price(stoi(Wholesale_Price));
Products->UpdateProductAmount(stoi(Stock_Amount));
Products->UpdateRetailPrice(stoi(Retail_Price));
}
}
Relevant part of Main function:
int main(int argc, char const *argv[])
{
product Products[10];
ReadProductFromFile(Products+5);//is this the right way to send Products[5]? tried &(product[5]) but error
return 0;
}
Input:
Bananas
210
270
310

How to read in data from a json file until there is no more data to read in QT

Basically I am using an API to retrieve stock data to me in the form of a ytd so it will return data of the closing price of the stock everyday from january until now. At first I was simply using a for loop and reading until i < json.size() but after figuring out the .size() does not properly return what i need for it to work i am again stuck on this. My code is below
//Retrieves json format of data
Json::Value chartData = IEX::stocks::chartYtd(symbol_std);
//Stores x and y values
QVector<double> time(365), closePrice(365);
//Intialize first vector to first values
closePrice[0] = chartData[0]["close"].asDouble();
time[0] = startYearTime;
//Finds max and min for range
float maxAvg = closePrice[0];
float minAvg = closePrice[0];
//Reads in data from json(historical data 1 day delayed)
for(int i = 1; ; i++)
{
time[i] = startYearTime + 86400*i;
closePrice[i] = (chartData[i]["close"].asDouble());
if((closePrice[i] == 0) && (time[i] != chartData.size() - 1))
{
closePrice[i] = closePrice[i-1];
}
if(closePrice[i] > maxAvg)
{
maxAvg = closePrice[i];
}
else if(closePrice[i] < minAvg)
{
minAvg = closePrice[i];
}
}
The json file looks like this
what can i do to have my code store the "close" value in the json file until there is no more "close" value to read in and then in which it stops, thank you in advance as im a new developer!

Appens to array object

I was playing around with d and i stuck in CaStore class, it accepts the user1 but not the user2 data, i get core.exception.RangeError#main.d(60): Range violation, for example to add db.ccuser[0] = user1; without the [0] and next the db.ccuser[0] = user2; without the [0]
import std.stdio;
class CAdata{ string username;}
class Users{
int age;
CAdata[] info;
this(){
setNull();
}
void setNull(){
age = 0;
info ~= new CAdata();
}
}
class CaStore{
Users[] ccuser;
this(){
ccuser ~= new Users();
}
}
void main()
{
Users user1 = new Users();
user1.age = 24;
user1.info[0].username = "bob";
Users user2 = new Users();
user2.age = 24;
user2.info[0].username = "alice";
CaStore db = new CaStore();
db.ccuser[0] = user1;
db.ccuser[1] = user2;
}
You are writing to a position in the array that is out of bounds.
When you declare your array
Users[] ccuser;
its length is initially 0, there is no room for any elements. Then you append one element, yielding a length of 1:
ccuser ~= new Users();
This is why the first line
db.ccuser[0] = user1;
works but the second one gives you an error:
db.ccuser[1] = user2;
You are writing to index 1, but that is past the end of the array.
You can either:
Append to the array instead:
db.ccuser ~= user2;
Or increase the length of the array to make room:
db.ccuser.length = 2;
db.ccuser[1] = user2; // now there is room for two elements, no error

How to get JSON objects value using casablanca in C++

I am new to Json. And i m using codeplex (casablanca) for reading the json values.
Below is the sample json string:
[{ "rollno": 2000,
"name": "suhani","marks":{"grade":"C"} }, {"rollno": 3000,"name": "ishu", "marks":{ "grade":"A"} }]
The code to access name & rollno, i am writing below code:
json::value jobj = json::value::parse(utility::conversions::to_string_t(resultbody));
for (unsigned int i = 0; i < jobj.size(); i++) {
auto getval = jobj[i];
if (getval.at(U("name")).is_string()) {
auto xstr = getval.at(U("name")).as_string();
std::string wide = utility::conversions::to_utf8string(xstr);
std::string str(wide.begin(), wide.end());
string name = str;
}
if (getval.at(U("rollno")).is_integer()) {
auto xstr = getval.at(U("rollno")).as_integer();
int rollno = xstr;
} }
HOW TO GET VALUE AT GRADE ?? When i access marks it is of type object, i am not getting how to access grade from there. Please respond.
Marks is still a json object. You'll need to access the grade property. From your code snippet add the following:
for (unsigned int i = 0; i < jobj.size(); i++) {
auto getval = jobj[i];
auto marks_object = getval.at(U("marks"));
auto grade_value = marks_object.at(U("grade")).as_string();

Creating object in C++ , what if already constructed?

I am still new to c++. I want to read in messages from several sources. Each source will begin data messages with a 4 char ID. Each will also have several data messages. No one message has all of the info I want from the device. So if I create an object with the ID as the object name, the next time a message is received, will the object be updated or completely reconstructed? Is there a way to check if the object is already constructed before calling it in the code?
class Channels{
public:
INT8U systemID; //0x01 Glonass, 0x02 GPS
INT8U satID;
INT8U GlonassNumber;
INT8U SNR; //signal to noise ratio
FP64 carrierPhase; //cylces
FP64 psuedoRange; //milliseconds
FP64 doppler; //HZ cycles
float tropDelay; //meters
float ionoDelay; //meters
};
class BaseStation{
public:
Channels channel[32]; //each channel object has all channel class variables in it
int numberSatelitesTracked;
FP64 timeUTC;
INT16U week;
FP64 GPStoUTCoffset;
FP64 GLOtoUTCoffset;
INT8S recieverTimeOffset;
FP64 posX; //geocentric coordinates in meters
FP64 posY;
FP64 posZ;
FP64 rmsX; //expected root mean square error of coordinates
FP64 rmsY;
FP64 rmsZ;
};
if( check == SOCKET_ERROR){
if( WSAGetLastError() != WSAEWOULDBLOCK){
printf("base station client recieve failed with error %d \n", WSAGetLastError());
FreeSocketInformation(i); //shuts down client socket if no data
}
continue;
}
else{
//recieve bytes into array
memcpy(recvArray, SocketInfo->DataBuf.buf, SocketInfo->RecvBytes +1);
//print recieved bytes on screen
printf("%s \n", SocketInfo->DataBuf.buf);
//first 4 bytes in message are base ID
cBuffer[0] = recvArray[0];
cBuffer[1] = recvArray[1];
cBuffer[2] = recvArray[2];
cBuffer[3] = recvArray[3];
baseID = cBuffer;
//create object with 4 char name
BaseStation baseID;
//test message identity and sort data
if(recvArray[4] == 0x10 && recvArray[5] == 0xF5){
baseID.timeUTC = combine64(recvArray[6]);
baseID.week = combine16u(recvArray[14]);
baseID.GPStoUTCoffset = combine64(recvArray[16]);
baseID.GLOtoUTCoffset = combine64(recvArray[24]);
baseID.recieverTimeOffset = recvArray[32];
int noChannels = (check-30) /30 ;
if (noChannels >= 32){
noChannels = 32;
}
int x = 33;
for(int m = 0; m < noChannels; m++){ //advance reading for channel m
baseID.channel[m].systemID = recvArray[x];
x++;
baseID.channel[m].satID = recvArray[x];
x++;
baseID.channel[m].GlonassNumber = recvArray[x];
x++;
baseID.channel[m].SNR = recvArray[x];
x++;
baseID.channel[m].carrierPhase = combine64(recvArray[x]);
x = x+8;
baseID.channel[m].psuedoRange = combine64(recvArray[x]);
x = x+8;
baseID.channel[m].doppler = combine64(recvArray[x]);
x = x+10;
} //end of for loop to gather F5 sat data
} //end F5 message data
if(recvArray[4] == 0x10 && recvArray[5] == 0xF6){
baseID.posX = combine64(recvArray[6]);
baseID.posY = combine64(recvArray[14]);
baseID.posZ = combine64(recvArray[22]);
baseID.rmsX = combine64(recvArray[30]);
baseID.rmsY = combine64(recvArray[38]);
baseID.rmsZ = combine64(recvArray[46]);
} //end F6 message data
OK so it seems an Array may be the best for me to use. So if I setup 100 base objects and then track the active array elements with a second boolean array, does this look like it should work? (baseID added to the base object)
BaseStation base[100];
boolean baseActive[100];
int baseNumber;
//begin message processing------------------------------------------------------------
//first 4 bytes in message are base ID
cBuffer[0] = recvArray[0];
cBuffer[1] = recvArray[1];
cBuffer[2] = recvArray[2];
cBuffer[3] = recvArray[3];
string name = cBuffer;
//check for existing baseID------------------------------------------------------------
// 100 array positions
//find if base is already in use, create new if not in use
for(baseNumber = 0; base[baseNumber].baseID != name; baseNumber++){
//for statement increases untill it finds baseID == name
if( baseNumber >= 100){ //baseID not currently in use
for(int n=0; baseActive[n] == true; n++){
//for statement increases untill finds a false baseActive
baseNumber = n; //assign baseNumber to the array position
base[baseNumber].baseID = name; //create new baseID
continue;
}
}
}
//check and process message data--------------------------------------------------------
if( base[baseNumber].baseID == name){
baseActive[baseNumber] = true;
//test message identity and sort data
}//end of for loop
//test connection, if no bytes recieved then connection is closed.----------------------
if( SocketInfo->RecvBytes == 0){
FreeSocketInformation(i); //shuts down client socket if no data
continue;
}
}
} //end of read data from socket
}
//need to add a timer to remove non sending bases from the baseActive[] array
C++ is a statically typed language, You need to provide the object name at compile time.
You cannot create an object name at run-time and create object with that name.
As already answered, you can't do so in C++.
However you can solve your problem in other way.
First, you need to bind some ID to some concrete object of structure BaseStation. You can provide this link in two ways - by holding BaseStation objects in associative containter, where keys are ID, or by holding array of BaseStation objects(as far as I can guess you are writing some sort of microcontroller code so std containers can be not available for you).
First approach code example:
//id is 4 char so it can be thought as int on most systems
std::map<int, BaseStation *> baseStations;
int * id = (int*)recvArray; //this hack is for showing how you can convert 4 char to int
//may be in your code (int id = combine32(recvArray[0])) is equvivalent
if(baseStations.find(*id) != baseStations.end()) //checking existance of object with such id
{
//ok, exists, do nothing
}
else
baseStations[*id] = new BaseStation(); //create new
baseStations[*id].timeUTC = combine64(recvArray[6]); //starting copying values
//other values copying
In second situation if you can't use associative containers or can't afford their libs\code because of microcontroller memory lack, you can use just arrays but it's not flexible at all and consumes more operations. Example:
//BaseConnection also holds field names id;
BaseConnection baseConnections[N];
int FindId(int id); //return index of element in baseConnections array with this id
BaseConnection * workingConnection = &baseConnections[FindId(combine32(recvArray[0]))];
workingConnection->timeUTC = combine64(recvArray[6]); //starting copying values
//other values copying