One of my weaknesses is effectively using chars in C++ which is what I am trying to do right now. I have a player class in my game and within the player class, I create a playerCard object which displays various information. This works fine for a single instance of the player object (i.e. Player player) but when I attempt to push_back a player object in to a vector it all goes wrong.
Basically, the program continues to run but the player doesn't render to the screen. When I quit the program, I then get a breakpoint error when main tries to return MSG. The comment about the breakpoint reads:
/*
* If this ASSERT fails, a bad pointer has been passed in. It may be
* totally bogus, or it may have been allocated from another heap.
* The pointer MUST come from the 'local' heap.
*/
_ASSERTE(_CrtIsValidHeapPointer(pUserData));
I have located the error to here
strcat(nameCard, nameChar);
strcat(nameCard, genderChar);
strcat(nameCard, ageChar);
strcat(nameCard, cashHeldChar);
strcat(nameCard, productWantedChar);
within the playerCard class because when I comment this out, I do not get the error. Here is the full playerCard class (Again, it is messy and probably the wrong way for going about things but I am trying to get my head round using chars/strings etc)
#include "Headers.h";
class Playercard{
private:
RECT textbox;
LPD3DXFONT font;
std::string nameStr;
std::string genderStr;
std::string ageStr;
std::string cashHeldStr;
std::string prodWantedStr;
char nameCard[1000];
public:
Playercard()
{
}
void load(char* name, bool male, int age, double cash, char* prod)
{
if(male)
{
genderStr = "Gender: Male\n";
}
else
{
genderStr = "Gender: Female\n";
}
nameStr = "Name: " + static_cast<std::ostringstream*>( &(std::ostringstream() << name))->str() + "\n";
ageStr = "Age: " + static_cast<std::ostringstream*>( &(std::ostringstream() << age))->str() + "\n";
cashHeldStr = "Cash Held: " + static_cast<std::ostringstream*>( &(std::ostringstream() << cash))->str() + "\n";
prodWantedStr = "Product Wanted: " + static_cast<std::ostringstream*>( &(std::ostringstream() << prod))->str() + "\n";
char * nameChar = new char [nameStr.length()+1];
char * genderChar = new char [genderStr.length()+1];
char * ageChar = new char [ageStr.length()+1];
char * cashHeldChar = new char [cashHeldStr.length()+1];
char * productWantedChar = new char [prodWantedStr.length()+1];
strcpy(nameChar, nameStr.c_str());
strcpy(genderChar, genderStr.c_str());
strcpy(ageChar, ageStr.c_str());
strcpy(cashHeldChar, cashHeldStr.c_str());
strcpy(productWantedChar, prodWantedStr.c_str());
strcat(nameCard, nameChar);
strcat(nameCard, genderChar);
strcat(nameCard, ageChar);
strcat(nameCard, cashHeldChar);
strcat(nameCard, productWantedChar);
diagFile.open("Diag.txt");
diagFile.write("Test", 100);
diagFile.close();
}
void setUp(int L, int T, int R, int B)
{
SetRect(&textbox, L,T,R,B);
}
void draw()
{
font->DrawTextA(d3dSprite, nameCard, -1, &textbox, DT_LEFT, D3DCOLOR_XRGB(255, 255, 255));
}
LPCSTR plCard()
{
return nameCard;
}
};
Any help would be greatly appreciated. Thank you.
Your nameCard is uninitialized. Replace the first strcat with strcpy, or initialize it with a zero string.
Now, how about using std::string exclusively?
Your main problem is that nameCard is uninitialized. strcat requires a null-terminated string to do its magic, and there's no guarantee that the first, or any, character in nameCard is a null.
However, C strings are unnecessary. Just use std::string all the time. After changing nameCard to a string, I'd change load to (file writing excluded):
void load(const std::string &name, bool male, int age, double cash, const std::string &prod)
{
nameStr = "Name: " + name + "\n";
genderStr = "Gender: " + (male ? "Male" : "Female") + "\n";
ageStr = "Age: " + std::to_string(age) + "\n";
cashHeldStr = "Cash Held: " + std::to_string(cash) + "\n";
prodWantedStr = "Product Wanted: " + prod + "\n";
nameCard = nameStr + genderStr + ageStr + cashHeldStr + prodWantedStr;
}
I would actually just make nameCard a data member, removing the others, and use this:
nameCard.clear();
nameCard += "Name: " + name + "\n";
//add on other parts
Other than that, make plCard() return a std::string and in draw(), use nameCard.c_str(). I hope that clears up what you can do with strings a bit more.
Do note, however, that std::to_string is C++11. C++03 has two common solutions:
std::string str = boost::lexical_cast<std::string>(someNumber);
Or
std::ostringstream oss;
oss << someNumber;
std::string str = oss.str();
I find the three-liner much more readable than a one-liner or two-liner.
Related
I have a vector of unsigned char where I copy bytes in C++. I convert all primitive types to bytes and copy to this vector of char (which is interpreted as bytes in C++). Now I am copying also strings. But I am not sure if I am converting strings to bytes. If you take a look at my output when I am printing the vector of unsigned char I am printing bytes from double int float but I am printing the real string of my variable testString. So I suppose that I am not inserting bytes of this testString on my vector of unsigned char. How should I do that?
Thanks
const std::string lat = "lat->", alt = "alt->", lon = "lon->", testString = "TEST-STRING";
double latitude = 10.123456;
double longitude = 50.123456;
double altitude = 1.123456;
std::vector<unsigned char> result(
sizeof(latitude) + sizeof(longitude) + sizeof(altitude) + testString.length());
std::cout << "copying to the vector" << std::endl;
memcpy(result.data(), &longitude, sizeof(longitude));
memcpy(result.data() + sizeof(longitude), &latitude, sizeof(latitude));
memcpy(result.data() + sizeof(longitude) + sizeof(latitude), &altitude, sizeof(altitude));
memcpy(result.data() + sizeof(longitude) + sizeof(latitude) + sizeof(altitude), testString.c_str(),
testString.length() + 1);
std::cout << "copied to the vector\n" << std::endl;
std::cout << "printing the vector" << std::endl;
for (unsigned int j = 0; j < result.size(); j++) {
std::cout << result[j];
}
std::cout << std::endl;
std::cout << "printed the vector\n" << std::endl;
// testing converting back ...................
std::cout << "printing back the original value" << std::endl;
double dLat, dLon, dAlt;
std::string value;
memcpy(&dLon, result.data(), sizeof(longitude));
memcpy(&dLat, result.data() + sizeof(longitude), sizeof(latitude));
memcpy(&dAlt, result.data() + sizeof(longitude) + sizeof(latitude), sizeof(altitude));
value.resize(testString.length());
memcpy(&value[0], result.data() + sizeof(longitude) + sizeof(latitude) + sizeof(altitude),
sizeof(value.data()) + testString.size());
std::cout << alt << dAlt;
std::cout << lat << dLat;
std::cout << lon << dLon;
std::cout << " " << value << std::endl;
std::cout << "printed back the original value\n" << std::endl;
output:
copying to the vector
copied to the vector
printing the vector
[?�gI#m���5?$#l������?TEST-STRING
printed the vector
printing back the original value
alt->1.12346lat->10.1235lon->50.1235 TEST-STRING
printed back the original value
There's no problem with your code! You're printing the actual bytes of your variables. The bytes in a double can't really be interpreted as a text string (at least, it doesn't make sense if you do) but the bytes in a text string can, producing what you see.
Let's say you've got the following code (which is really just disguised C):
#include <cstdio>
int main(int argc, char *argv[]) {
struct {
double latitude;
double longitude;
char name[30];
} structure = {
53.6344,
126.5223167,
"Keyboard Mash"
};
printf("%f %f %s\n", structure.latitude, structure.longitude, structure.name);
for (size_t i = 0; i < sizeof(structure); i += 1) {
printf("%c", ((char*)&structure)[i]);
}
printf("\n");
}
This code would (probably) print:
53.6344 126.5223167 Keyboard Mash
����������������Keyboard Mash�����������������
The first 16 bytes are from the doubles, and the next 30 are from the char[]. That's just how char[]s are stored! Your code is doing what you'd expect it to.
Of course, you can't rely on it doing this in exactly this way; that's undefined behaviour.
I feel like you were expecting something like: 128565TESTSTRING where 12, 85 and 65 are values of longitude, latitude and altitude. Well, that's not going to happen be cause you wrote 12 in the data, not "12"; therefore, it will return you the character whose ASCII code is 12. Maybe you could use something like sprintf() instead.
I just discovered reinterpret_cast in C++ and I am trying to learn more about it. I wrote this code:
struct Human{
string name;
char gender;
int age;
Human(string n, char g, int a) : name(n), gender(g), age(a) {}
};
int main()
{
Human h("John", 'M', 26);
char* s = reinterpret_cast<char*>(&h);
Human *hh = reinterpret_cast<Human*>(s);
cout << hh->name << " " << hh->gender << " " << hh->age << endl;
}
It works pretty well, exactly as expected. Now I want convert the char * to an std::string and then from this string get back the Human object:
int main()
{
Human h("John", 'M', 26);
char* s = reinterpret_cast<char*>(&h);
string str = s;
Human *hh = reinterpret_cast<Human*>(&str);
cout << hh->name << " " << hh->gender << " " << hh->age << endl; // prints wrong values
}
Does anyone have an idea to overcome this ?
Thank you.
In your second program when you do
string str = s;
you create a completely new object that is totally unrelated to the pointer s. Getting the address from str will give you a pointer to str, and not the "string" it contains.
Also, using reinterpret_cast is a way to tell the compiler "I know what I am doing", and if you don't actually know what's happening then you will undoubtedly march into the territory of undefined behavior which is what will happen when you try to initialize str with the "string" pointed to by s, since it's not really a string.
I realize the way I am approaching this is wrong, but I don't know why.
I am very new to C++/programming in general.
I want my program to do the following.
If I cin "setwanted joe" I want it to store joe into a char array. I have it so I can succesfully seperate and cout joe, but I am can't return the char array from the function.
Here's my example code.
char * GETNAME (char *searchinput, char*searchtext)
{
char returnname[64];
int starter=0;
for(;;)
{
if (*searchinput == *searchtext)
{
searchinput++;
searchtext++;
}
if (*searchinput == ' ')
{
searchinput++;
searchtext++;
}
if (*searchinput!='\0' && *searchinput!= *searchtext && *searchinput != ' ')
{
returnname[starter] = *searchinput;
starter++;
searchinput++;
searchtext++;
}
if (*searchinput=='\0')
{
returnname[starter]='\0';
cout << "Char Array to Return: " << returnname << endl;
return returnname;
}
}
}
Above is the function I made to try to return the name from the char array.
Here is the code I was using to call the function.
char Recv[256];
cin >> Recv;
char * wantedname = new char[64];
wantedname = GETNAME(Recv,"setwanted");
cout << "Returned Name: " << wantedname << endl;
Thanks for reading/your patience I know this is messy.
Use std::array, std::vector or std::string (in case of array of characters) to easily return an array of items. You're code invokes undefined behavior due to returning a pointer to a local temporary object. I prefer this
std::string GETNAME(const std::string &searchinput,const std::string &searchtext)
{
std::string returnname;
...
return returnname;
}
I want to truncate a string in a cout,
string word = "Very long word";
int i = 1;
cout << word << " " << i;
I want to have as an output of the string a maximum of 8 letters
so in my case, I want to have
Very lon 1
instead of :
Very long word 1
I don't want to use the wget(8) function, since it will not truncate my word to the size I want unfortunately. I also don't want the 'word' string to change its value ( I just want to show to the user a part of the word, but keep it full in my variable)
I know you already have a solution, but I thought this was worth mentioning: Yes, you can simply use string::substr, but it's a common practice to use an ellipsis to indicate that a string has been truncated.
If that's something you wanted to incorporate, you could just make a simple truncate function.
#include <iostream>
#include <string>
std::string truncate(std::string str, size_t width, bool show_ellipsis=true)
{
if (str.length() > width)
if (show_ellipsis)
return str.substr(0, width) + "...";
else
return str.substr(0, width);
return str;
}
int main()
{
std::string str = "Very long string";
int i = 1;
std::cout << truncate(str, 8) << "\t" << i << std::endl;
std::cout << truncate(str, 8, false) << "\t" << i << std::endl;
return 0;
}
The output would be:
Very lon... 1
Very lon 1
As Chris Olden mentioned above, using string::substr is a way to truncate a string. However, if you need another way to do that you could simply use string::resize and then add the ellipsis if the string has been truncated.
You may wonder what does string::resize? In fact it just resizes the used memory (not the reserved one) by your string and deletes any character beyond the new size, only keeping the first nth character of your string, with n being the new size. Moreover, if the new size is greater, it will expand the used memory of your string, but this aspect of expansion is straightforward I think.
Of course, I don't want to suggest a 'new best way' to do it, it's just another way to truncate a std::string.
If you adapt the Chris Olden truncate function, you get something like this:
#include <iostream>
#include <string>
std::string& truncate(std::string& str, size_t width, bool show_ellipsis=true) {
if (str.length() > width) {
if (show_ellipsis) {
str.resize(width);
return str.append("...");
}
else {
str.resize(width);
return str;
}
}
return str;
}
int main() {
std::string str = "Very long string";
int i = 1;
std::cout << truncate(str, 8) << "\t" << i << std::endl;
std::cout << truncate(str, 8, false) << "\t" << i << std::endl;
return 0;
}
Even though this method does basically the same, note that this method takes and returns a reference to the modified string, so be careful with it since this string could be destroyed because of an external event in your code. Thus if you don't want to take that risk, just remove the references and the function becomes:
std::string truncate(std::string str, size_t width, bool show_ellipsis=true) {
if (str.length() > width) {
if (show_ellipsis) {
str.resize(width);
return str + "...";
}
else {
str.resize(width);
return str;
}
}
return str;
}
I know it's a little bit late to post this answer. However it might come in handy for future visitors.
I have two base structures like following :
struct stuSectionProperties
{
int Field1; // Row | BoxNo | SplitterNo
int Field2; // Col | Adapter | -
double Latitude;
bool IsEast;
int Band;
int CableNo;
SDP::Global::enuSections::Type Section;
stuSectionProperties()
{
this->Field1 = -1;
this->Field2 = -1;
this->Latitude = -1;
this->Band = -1;
this->Section = SDP::Global::enuSections::None;
this->CableNo = -1;
}
const char* toStr()
{
return ((QString) (QString::number(this->Field1) + " , " + QString::number(this->Field2) + " , " + QString::number(Latitude) + " , " + QString::number(IsEast) + " , " + QString::number(Band) + " , "
+ QString::number((int) Section) + QString::number((int) CableNo))).toStdString().c_str();
}
};
and
struct stuSearchResult
{
stuSectionProperties MyData;
QList<stuSectionProperties> Connections;
stuSearchResult()
{
this->MyData.Field1 = -1;
this->MyData.Field2 = -1;
this->MyData.Latitude = -1;
this->MyData.Band = -1;
this->MyData.Section = SDP::Global::enuSections::None;
this->MyData.CableNo = -1;
stuSectionProperties stuDummy;
stuDummy.Band=-1;
stuDummy.CableNo=-1;
stuDummy.Field1=-1;
stuDummy.Field2=-1;
stuDummy.IsEast=-1;
stuDummy.Latitude=-1;
stuDummy.Section= SDP::Global::enuSections::None;
this->Connections.append(stuDummy);
}
const char * toStr()
{
return ((QString) (QString::number(this->MyData.Field1) + " , " + QString::number(this->MyData.Field2) + " , " + QString::number(this->MyData.Latitude) + " , " + QString::number(this->MyData.IsEast) + " , " + QString::number(this->MyData.Band) + " , "
+ QString::number((int) this->MyData.Section) + QString::number((int) this->MyData.CableNo)) + " , " + QString::number(this->Connections[0].Field1) + " , " + QString::number(this->Connections[0].Field2) ).toStdString().c_str();
}
};
whenever I try to create an instance out of second the structure and then try to call its toStr() member I'll get an erro which is saying that these lines have some issues :
+ QString::number(this->Connections[0].Field1) + " , " + QString::number(this->Connections[0].Field2)
can you please tell me whats my problem exactly?
regards.
the assert error says Connections doesn't have the element you referenced. if you print out Connections.size() how many elements does it say the list contains?
returning string.c_str() could be a potential memory issue, as it's returning a pointer to memory that has been freed.
in c++, custom printing is often done by overloading the stream operator in your class:
friend std::ostream &operator<<(std::ostream &os, const myClass &c)
{
return os << c.some << c.val;
}
...
cout << myObj << endl;
...
strstream ss;
ss << "cool: " << myObj << " " << 55;
string s = ss.str();
you could also just pass a reference to a string object in your toStr function:
makeString(string &s)
{
...
s = qstr.toStdString();
}
string s;
makeString(s);
Yes the code has many issues
Normally people don't write "this->x"; they just write "x"; the "this->" is implicit inside methods
As the other answer pointed out, c_str() points to invalid memory here
However, the Connections list should contain at least one element because the constructor appends it there. I think it would help if the poster would also post the code to allocate the structure. It looks like the constructor doesn't get called OR there is other code that clears the Connections list before toStr() is actually called.