I am using Embarcadero C++ Builder.
I have a function that declares a TStringList, uses it throughout the function, then deletes the object at the end of the function.
I have been using this code happily as a 32 bit application and have converted it to a 64 bit application, and now I get an "Invalid Pointer Operation" exception when trying to delete the TStringList. Any ideas?
The odd thing is I had the same trouble with another function that uses a character pointer (using new to create heap memory space) and a delete operation. I ended up creating a local buffer with stack space for that function, but I'm stuck with this one since I would like to use the TStringList object.
Here is the code:
String ReadUserConfig(String ConfigString) {
String UserConfigPath = AppDrive + "\\DC\\userconfig.csv";
TStringList *List = new TStringList;
if (FileExists(UserConfigPath)) { // file present, parse it
try {
List->LoadFromFile(UserConfigPath);
delete List;
}
catch(...) {
ShowMessage("Exception in ReadUserConfig()");
return ReturnString;
}
for (int i = 0; i < List->Count; ++i) {
String thisLine = List->Strings[i];
/* search for ConfigString in this line */
if ((thisLine.Pos(ConfigString) != 0) &&
(thisLine.Pos("USER_CONFIG") != 0)) {
/* grab everything right of ConfigString */
thisLine = thisLine.SubString
(thisLine.Pos(ConfigString) + ConfigString.Length() + 1,
thisLine.Length());
ReturnString = thisLine.Trim();
i = List->Count;
}
}
}
delete List; /* CAUSES INVALID POINTER EXCEPTION */
return ReturnString;
}
As stated in comments, your code has logic bugs in it, causing you to delete the List twice, or leak it completely.
Try something more like this instead:
String ReadUserConfig(String ConfigString) {
String UserConfigPath = AppDrive + "\\DC\\userconfig.csv";
try {
TStringList *List = new TStringList;
try {
if (FileExists(UserConfigPath)) { // file present, parse it
List->LoadFromFile(UserConfigPath);
for (int i = 0; i < List->Count; ++i) {
String thisLine = List->Strings[i];
/* search for ConfigString in this line */
if ((thisLine.Pos(ConfigString) != 0) &&
(thisLine.Pos("USER_CONFIG") != 0)) {
/* grab everything right of ConfigString */
thisLine = thisLine.SubString(thisLine.Pos(ConfigString) + ConfigString.Length() + 1, thisLine.Length());
ReturnString = thisLine.Trim();
break;
}
}
}
}
__finally {
delete List;
}
}
catch(const Exception &e) {
ShowMessage("Exception in ReadUserConfig()\n" + e.Message);
}
catch(...) {
ShowMessage("Exception in ReadUserConfig()");
}
return ReturnString;
}
Or, use a std::auto_ptr (pre C++11) or std::unique_ptr (C++11 and later) instead of a try/finally block:
#include <memory>
String ReadUserConfig(String ConfigString) {
String UserConfigPath = AppDrive + "\\DC\\userconfig.csv";
try {
//std::auto_ptr<TStringList> List(new TStringList);
std::unique_ptr<TStringList> List(new TStringList);
if (FileExists(UserConfigPath)) { // file present, parse it
List->LoadFromFile(UserConfigPath);
for (int i = 0; i < List->Count; ++i) {
String thisLine = List->Strings[i];
/* search for ConfigString in this line */
if ((thisLine.Pos(ConfigString) != 0) &&
(thisLine.Pos("USER_CONFIG") != 0)) {
/* grab everything right of ConfigString */
thisLine = thisLine.SubString(thisLine.Pos(ConfigString) + ConfigString.Length() + 1, thisLine.Length());
ReturnString = thisLine.Trim();
break;
}
}
}
}
catch(const Exception &e) {
ShowMessage("Exception in ReadUserConfig()\n" + e.Message);
}
catch(...) {
ShowMessage("Exception in ReadUserConfig()");
}
return ReturnString;
}
Related
This error occurred
FATAL ERROR: test case CRASHED: SIGABRT - Abort (abnormal termination) signal
::error::Error: Exit with code: 134 and signal: null
when I was testing a string constructor "String::String(const char* other)"
String::String(const char* other) // the constructor I'm talking about
{
if (other == nullptr)
{
String();
}
else if (other != nullptr)
{
int count{0};
for (int i = 0; other[i] != '\0'; ++i)
{
count = count + 1;
}
slength = count;
// delete [] ifmt; // ?
ifmt = new char [slength + 1];
for (int i = 0; i < slength; ++i)
{
ifmt[i] = other[i];
}
ifmt[slength] = '\0';
}
}
}
String::String()
{
slength = 0;
ifmt = new char[slength + 1];
ifmt[0] = '\0';
}
in my custom String class.
private:
int slength;
char* ifmt;
One of the suggestions I received was to create a new option branch to handle "negative length" and construct an empty string in this case, but I have no idea what "negative length" is.
I have also searched for some case on the Internet but I found I have done the things they suggested but it still doesn't work.
https://www.geeksforgeeks.org/how-to-create-a-custom-string-class-in-c-with-basic-functionalities/
design a string class constructor c++
custom string exercise in c++
I will be thankful if anyone could give some guidance in this case.
This code snippet
if (other == nullptr)
{
String();
}
does not make sense.
In this statement
String();
there is created a temporary object that at once is destroyed.
As a result when a null pointer is passed then the object of the class has uninitialized data members.
Pay attention to that there is a redundant closing brace
//...
}
}
String::String()
I am trying to read the elements in an XML and store in a array of
struct and need to pass the pointer of this array to other functions.
However I have issue compiling in gnu, error message:
error: cannot
convert myRec to uint32_t {aka unsigned int}' in return
return *recs;
Tried to set myRec recs[count] without malloc, get an error of invalid pointer.
struct myRec
{
std::string one;
std::string two;
std::string three;
std::string four;
std::string five;
std::string six;
};
uint32_t count = 0;
XMLDocument doc;
doc.LoadFile(pFilename);
XMLElement* parent = doc.FirstChildElement("a");
XMLElement* child = parent->FirstChildElement("b");
XMLElement* e = child->FirstChildElement("c");
for (e = child->FirstChildElement("c"); e; e = e->NextSiblingElement("c"))
{
count++;
}
std::cout << "\n""Count = " << count << std::endl;
recs = (myRec *)malloc(6 *count * sizeof(myRec));
XMLElement *row = child->FirstChildElement();
if (count > 0)
{
--count;
count = (count < 0) ? 0 : count;
for (uint32_t i = 0; i <= count; i++)
{
while (row != NULL)
{
std::string six;
six = row->Attribute("ID");
recs[i].six = six;
XMLElement *col = row->FirstChildElement();
while (col != NULL)
{
std::string sKey;
std::string sVal;
char *sTemp1 = (char *)col->Value();
if (sTemp1 != NULL) {
sKey = static_cast<std::string>(sTemp1);
}
else {
sKey = "";
}
char *sTemp2 = (char *)col->GetText();
if (sTemp2 != NULL) {
sVal = static_cast<std::string>(sTemp2);
}
else {
sVal = "";
}
if (sKey == "one") {
recs[i].one = sVal;
}
if (sKey == "two") {
recs[i].two = sVal;
}
if (sKey == "three") {
recs[i].three = sVal;
}
if (sKey == "four") {
recs[i].four = sVal;
}
if (sKey == "five") {
recs[i].five = sVal;
}
col = col->NextSiblingElement();
}// end while col
std::cout << "\n""one = " << recs[i].one << "\n"" two= " << recs[i].two << "\n""three = " << recs[i].three << "\n""four = " << recs[i].four << "\n""five = " << recs[i].five << "\n""six = " << recs[i].six << std::endl;
row = row->NextSiblingElement();
}// end while row
}
}
else
{
std::cout << "Failed to find value, please check XML! \n" << std::endl;
}
return *recs;
expect to return a pointer to the array
I declared it as:
std::string getxmlcontent(const char* pFilename);
myRec*recs= NULL;
Function:
std::string readxml::getxmlcontent(const char* pFilename)
{
}
Not sure if it is the right way as I am quite new to c++
You're making a few errors, you probably should get a good C++ book and do some studying
In C++ use new
recs = new myRec[6*count];
instead of
recs = (myRec *)malloc(6 *count * sizeof(myRec));
The problem with malloc in a C++ program is that it won't call constructors, so all the strings you have in your struct are invalid, and (most likely) your program will crash when you run it.
It's not clear to me why you need 6*count, that seems to be because you have six strings in your struct. If so then that's confused thinking, really you just need
recs = new myRec[count];
and you'll get 6*count strings because that's how you declared your struct.
sKey = sTemp1;
instead of
sKey = static_cast<std::string>(sTemp1);
No need for the cast, it's perfectly legal to assign a char* to a std::string.
Finally if you want to return a pointer, then just return the pointer
return recs;
not
return *recs;
However you haven't included the function signature in the code you posted. I suspect there's another error, but I can't tell unless you post how you declare this function.
So I am trying to open a file and parse a file in C++ (using initializeSimulation(). I can open the file successfully, but when I try to parse the file and store specific values from the file into my variables, I get the THREAD 1 error. The DataParser.cpp file is one I am required to use and cannot modify, I am to simply call it. I think the issue is with my function call to getData() (using pointers) but I cannot seem to figure out why. The way my variables are set up now is working weirdly for some reason, until i try to store m_sMaxVal. When I set them all up as pointers, I was getting a different error. I'm new to C++ but please let me know if you need any more information, and thanks in advance.
Simulation.cpp
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include "Simulation.hpp"
#include "dDataParser.h"
Simulation::Simulation() {}
Simulation::~Simulation(){}
void Simulation::initializeSimulation() {
char *fileName;
char *m_sType;
char m_sMaterial;
int m_iID;
char m_sUnits;
double m_sMinVal;
double *m_sMaxVal;
cout << "Enter the name of the data file:" << endl;
cin >> fileName ;
//cout << fileName << "\n" ;
parser = new EnviroSimDataParser(fileName);
parser ->getSensorData(m_sType, &m_sMaterial, &m_iID, &m_sUnits, &m_sMinVal, m_sMaxVal);
}
DataParser.cpp
EnviroSimDataParser::EnviroSimDataParser(char *fileName)
{
char line[128];
strcpy(m_sFileName, fileName);
inFile = new ifstream();
inFile->open(m_sFileName, fstream::in); // Open the data file
m_iNumSensors = 0; // Number of sensors in the file
m_iNumDisplays = 0; // Number of display devices in the file
m_iNextSensor = 0; // Next sensor number to read
m_iNextDisplay = 0; // Next display number to read
if(inFile->is_open())
{
cout << "file opened successfully1 \n";
// Count the number of sensors
while(getNextLine(line, 127))
{
if(strcmp(line, "<SENSOR>") == 0)
m_iNumSensors++;
if(strcmp(line, "<DISPLAY_DEVICE>") == 0)
m_iNumDisplays++;
}
inFile->close();
cout << "file closed successfully1 \n";
}
else
{
cout << "Failed to open file. Application terminated...\n";
exit(0);
}
}
//-----
bool DataParser::getData(char *type, char *material, int *ID,
char *units, double *minVal, double *maxVal)
{
int sNum = 0;
char line[128];
// See if we have read all sensors
if(m_iNextSensor >= m_iNumSensors) return false;
// Reopen the file
inFile = new ifstream();
inFile->open(m_sFileName, fstream::in);
if(inFile->is_open())
{
// Read to the the current sensor count
while(getNextLine(line, 127))
{
if(strcmp(line, "<SENSOR>") == 0) // Got one
{
if(sNum == m_iNextSensor)
{
// Get data on this one
while(getNextLine(line, 127))
{
// Get the type
if(strcmp(line, "<TYPE>") == 0)
{
if(getNextLine(line, 127))
{
strcpy(type, line); // Set the type
}
else
return false; // Oops!
}
else if(strcmp(line, "<MATERIAL>") == 0)
{
if(getNextLine(line, 127))
{
strcpy(material, line); // Set the material
}
else
return false; // Oops!
}
else if(strcmp(line, "<ID>") == 0)
{
if(getNextLine(line, 127))
{
*ID = atoi(line); // Set the ID
}
else
return false; // Oops!
}
else if(strcmp(line, "<UNITS>") == 0)
{
if(getNextLine(line, 127))
{
strcpy(units, line); // Set the units
}
else
return false; // Oops!
}
else if(strcmp(line, "<MINIMUM_VALUE>") == 0)
{
if(getNextLine(line, 127))
{
*minVal = atof(line); // Set the minimum value
}
else
return false; // Oops!
}
else if(strcmp(line, "<MAXIMUM_VALUE>") == 0)
{
if(getNextLine(line, 127))
{
*maxVal = atof(line); // Set the minimum value
}
else
return false; // Oops!
}
else if(strcmp(line, "</SENSOR>") == 0)
{
m_iNextSensor++; // Increment for next sensor
return true; // Got it
}
} // end while
} // end if(sNum == m_iNextSensor)
else
{
sNum++; // Check the next one
}
}
}
inFile->close();
} // end if file open
return false; // If we get here we have got all the sensors
}
edit: the debugger stops at the line of
*maxVal = atof(line); //Set the minimum value
When a function or method has a parameter that is a pointer to a variable, you need to allocate memory for that variable. There are a few ways to do that, but the easiest one is to declare an automatic variable like this:
double someVar = 0.0;
and call the function that requires a pointer like this:
SomeFunctionThatTakesADoublePointer(&someVar);
You could allocate the memory on the heap by doing:
double* someVar = new double;
and call the function like this:
SomeFunctionThatTakesADoublePointer(someVar);
This comes with a couple of requirements, though. First, you need to check that the allocation succeeded by wrapping the function call in a conditional, like this:
if (someVar != nullptr)
{
SomeFunctionThatTakesADoublePointer(someVar);
}
And you have to free the memory when you're done using it, or it will leak:
if (someVar != nullptr)
{
SomeFunctionThatTakesADoublePointer(someVar);
// ... do some calculating with *someVar
delete someVar;
}
Things get trickier with character strings. In C++ you should avoid character strings and use std::string from the standard library. (You'll need to #include <string> to use it.) If you must work with character strings because you're calling a function which you don't control and it requires a string, you need to allocate a string that has enough memory for all the characters and the terminating nul character. You can do that on the stack if you know the length of the string is limited by doing something like:
char someString [ MAX_ALLOWED_STRING_LENGTH + 1 ];
where MAX_ALLOWED_STRING_LENGTH is a constant defined somewhere in your program. Then you can call a function or method which takes a character string like this:
SomeFunctionThatTakeACharacterString(someString);
Again, you could allocate the memory using new [], but you have to ensure it's not nullptr before using it and delete [] it when you're done.
In my game I keep track of unlocked levels with a vector std::vector<bool> lvlUnlocked_;.
The simple function to save the progress is this:
void save() {
std::stringstream ss;
std::string stringToSave = "";
std::ofstream ofile("./progress.txt");
if (ofile.good()) {
ofile.clear();
for (std::size_t i = 0; i < levelUnlocked_.size(); ++i) {
ss << "lvl" << i << "=" << (lvlUnlocked_.at(i) ? "1" : "0") << std::endl;
}
stringToSave = ss.str();
ofile << stringToSave;
ofile.close();
}
}
This works and is nice since I can just use a loop to dump the info.
Now to the part where I am stuck, the lower part of my load function (see comment in code below):
void load() {
std::ifstream ifile("./progress.txt");
if (ifile.good()) {
int begin;
int end;
std::string line;
std::string stringKey = "";
std::string stringValue = "";
unsigned int result;
while (std::getline(ifile, line)) {
stringKey = "";
stringValue = "";
for (unsigned int i = 0; i < line.length(); i++) {
if (line.at(i) == '=') {
begin = i + 1;
end = line.length();
break;
}
}
for (int i = 0; i < begin - 1; i++) {
stringKey += line.at(i);
}
for (int i = begin; i < end; i++) {
stringValue += line.at(i);
}
result = static_cast<unsigned int>(std::stoi(stringValue));
// usually I now compare the value and act accordingly, like so:
if (std::strcmp(stringKey.c_str(), "lvl0") == 0) {
lvlUnlocked_.at(0) = true;
} else if (std::strcmp(stringKey.c_str(), "lvl1") == 0) {
lvlUnlocked_.at(1) = true;
} else if (std::strcmp(stringKey.c_str(), "lvl2") == 0) {
lvlUnlocked_.at(2) = true;
}
// etc....
}
}
}
This works fine, but...
the problem is that I have 100+ levels and I want it to be dynamic based on the size of my lvlUnlocked_ vector instead of having to type it all like in the code above.
Is there a way to somehow make use of a loop like in my save function to check all levels?
If you parse your key to extract a suitable integer value, you can just index into the bit-vector with that:
while (std::getline(ifile, line)) {
const size_t eq = line.find('=');
if (eq == std::string::npos)
// no equals sign
continue;
auto stringKey = line.substr(0, eq);
auto stringValue = line.substr(eq+1);
if (stringKey.substr(0,3) != "lvl")
// doesn't begin with lvl
continue;
// strip off "lvl"
stringKey = stringKey.substr(3);
size_t end;
std::vector<bool>::size_type index = std::stoi(stringKey, &end);
if (end == 0 || end != stringKey.length())
// not a valid level number
continue;
if (index >= lvlUnlocked_.size())
// out of range
continue;
// Set it :-)
lvlUnlocked_[index] = stringValue=="1";
}
(I've also updated your parsing for "key=value" strings to more idiomatic C++.)
I have a delete function that is supposed to delete a string in an array by writing over it with the previous strings.
The look function see's that Overide matches and should be deleted. But the code i wrote for the loop in Delete is not removing that first spot in the array that Overide has taken up, and the output remains unchanged.
Also each phrase after + is being added into the array so four spots are taken in the array, and sorry i could not make that part look better the formatting screwed it up.
int AR::Look(const std::string & word)
{
int result = -1;
for(int i=0; i<counter; ++i)
{
if( con[i].find(word) != std::string::npos)
result = i;
}
return result;
}
void AR::Delete(const string & word)
{
int loc = Look(word);
if (loc == -1)
{
cout<<"word not found\n";
}
else
{
for(int i=0; i<counter-1,i++;)
{
con[i]= con[i+1];
}
}
}
AR their
Ar(1);
theirAr + "Overload the +" + " operator as a member function " + "with chaining to add a string " + "to an Arrary object.";
cout<<theirAr<<endl<<endl;
cout<<"testing Delete and Look. <<endl;
theirAr.Delete("XXXXXX");
theirAr.Delete("Overload");
cout<<"Output after Delete and Look called\n";
cout<<theirArray<<endl<<endl;
You are locating the String but only use the value to write an error if it does not appear; if you find the string at pos N you will delete the first string anyway:
void AR::Delete(const string & word)
{
int loc = Look(word);
if (loc == -1)
{
cout<<"word not found\n";
}
else
{
for(int i=0;i<counter-1,i++;) <--- Why don't you use loc here???
{
con[i]= con[i+1];
}
}
}
Also, your Look method would be better returning after the first match:
for ... {
if( con[i].find(word) != std::string::npos)
return i;
}
return -1;
Not sure if this is your problem, but shouldn't this be like so?
void AR::Delete(const string & word)
{
int loc = Look(word);
if (loc == -1)
{
cout<<"word not found\n";
}
else
{
for(int i=loc;i<counter-1,i++;) // changes in this line
{
con[i]= con[i+1];
}
}
}
Start at where you found the string and start shuffling them backwards. Also, what shortens the array? i.e. drops the last element off. Looks like that is missing too.
Try this instead:
int AR::Look(const std::string & word)
{
for (int i = 0; i < counter; ++i)
{
if (con[i].find(word) != std::string::npos)
return i;
}
return -1;
}
void AR::Delete(const string & word)
{
int loc = Look(word);
if (loc == -1)
{
cout << "word not found" << endl;
}
else
{
for (int i = loc+1; i < counter; ++i)
{
con[i-1] = con[i];
}
--counter;
}
}