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.
Related
sorry for such a stupid question but I couldn't find any obvious answer.
I need to read from stdin first an int n with the size of an array, and then integer values from a string in the format "1 2 3 4 5 6" with n elements.
If I knew the number of parameters at compile time I could use something like a scanf (or the safe alternatives) with a format string like "%d %d %d %d %d %d", but here I will only know that value at run time.
What would be the best way to do this in C++? Performance is important but more than that safety.
How should I read a format string of variable length in C++ from stdin?
You should not attempt to do such thing. Only ever use constant format strings.
I need to read from stdin first an int n with the size of an array, and then integer values
What would be the best way to do this in C++?
Read one value at a time. Repeat using a loop.
Here's a function that does what errorika describes:
const int SIZE = //as much of your memory as you'd like the user to have access to
***caller function must include this:
//allocate a string to hold some data;
char* buffer = NULL;
buffer = malloc (SIZE * sizeof(char));
if (buffer == NULL) {
printf("malloc error terminating\n");
return;
}
***
void getEntry(char* buffer) {
int count = 0;
int maxlen = SIZE - 1;
char a = '0';
for (int i = 0; i < SIZE; i++) {
buffer[i] = '0';
}
while (a != '\n' && count < maxlen) {
a = fgetc(stdin);
buffer[count] = a;
count++;
}
if (a == '\n') {
buffer[count - 1] = '\0';
}
else {
buffer[count] = '\0';
do {
a = fgetc(stdin);
} while (a != '\n');
}
}
This is all basic C code but user entry is evil. Here is what I've come up with for more C++ idiomatic user input functions (query is just the message string you pass in):
template<typename T>
void getInput(const std::string query, T& entry) {
std::string input;
std::cout << query << std::endl;
getline(std::cin, input);
std::stringstream buffer{input};
buffer >> entry;
}
OR
template<typename T>
void getInput2(std::string query, T& entry) {
bool validInput = false;
while (validInput == false)
{
validInput = true;
std::cout << query << std::endl;
std::cin >> entry;
if (std::cin.fail()) {
validInput = false;
std::cout << "Unacceptable entry\n" << std::endl;
}
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
}
I admittedly am an extreme C++ novice, so please forgive me for my probably very naive question.
I am writing code that should parse an assembly language file in its fundamental parts, to be translated into machine language at a second stage.
I have built a parser class, but I am not having success in opening the external assembly .asm textfile, and in feeding it to the various functions that compose my parser class.
More in particular, there are problems with the constructor.
I attach the full code I wrote below:
// parses .asm assembly files
#include <iostream>
#include <fstream>
#include <varargs.h>
#include <string>
using namespace std;
class parser
{
private:
istream inputfile;
char inputname[30];
string line;
bool endfile;
bool a_command, l_command, c_command;
string parsedLine, destParsedLine, compParsedLine, jumpParsedLine;
public:
// default parser constructor
parser()
{
}
//parser(char* argv[])
//{
// reader(argv[]);
//}
// opens input file
string reader(char* argv[])
{
strcpy(inputname,argv[1]);
strcat(inputname,".asm");
// opens input .asm file
ifstream inputfile(inputname);
// reads first line
getline(inputfile,line);
if (line[0] == '/' || line.empty())
inputfile.ignore(line.length(),'\n');
return line;
}
// checks if at end file
bool hasMoreCommands()
{
a_command = false;
l_command = false;
c_command = false;
endfile = false;
if (inputfile.eof())
endfile = true;
return endfile;
}
// advances read of inputfile
void advance()
{
if (line[0] == '/' || line.length() == 0)
inputfile.ignore(line.length(),'\n');
getline(inputfile,line);
}
/* function for labelling the type of command (address,computation,label) */
bool commandType()
{
if (line[0] == '#')
a_command = true;
else if (line[0] == '(')
l_command = true;
else
c_command = true;
return a_command, l_command, c_command;
}
// function to select parsing function
string selector()
{
if (a_command || l_command)
symbol();
else if (c_command)
{
dest();
comp();
jump();
string parsedLine = destParsedLine + compParsedLine + jumpParsedLine;
}
return parsedLine;
}
// function returning address or label symbol
string symbol()
{
if (a_command)
string parsedLine = line.substr(1);
else if (l_command)
string parsedLine = line.substr(1,line.length()-1);
return parsedLine;
}
// functions returning computation destination
string dest()
{
size_t equal = line.find('='); //no '=' found = returns 'npos'
string destParsedLine = line.substr(0,equal);
return destParsedLine;
}
string comp()
{
size_t equal = line.find('=');
size_t semicolon = line.find(';');
string compParsedLine = line.substr(equal,semicolon);
return compParsedLine;
}
string jump()
{
size_t semicolon = line.find(';');
string jumpParsedLine = line.substr(semicolon);
return jumpParsedLine;
}
};
// main program
int main (int argc, char *argv[])
{
bool endfile = false;
string parsedLine;
int count = 0;
if ((argc != 2) || (strchr(argv[1],'.') != NULL))
{
cout << argv[0] << ": assembly .asm file argument should be supplied, without .asm extension\n";
return 1;
}
parser attempt1 = parser();
attempt1.reader(argv[]);
while (!endfile)
{
attempt1.hasMoreCommands();
if (endfile)
return 0;
if (count > 0)
attempt1.advance();
attempt1.commandType();
attempt1.selector();
cout << parsedLine << endl; //debugging purposes
count++;
}
}
I provide the name of the .asm textfile to be opened, from the command line (.asm file located in the same folder of this cpp file).
Hence I need to use varargs.h which I suppose may be part of the problem.
When I try to build this, visual studio 2008 gives me the following 2 errors:
1 error C2512: 'std::basic_istream<_Elem,_Traits>' : no appropriate default constructor available line 21
2 error C2059: syntax error : ']' line 137
Help appreciated, and insults tolerated, thanks :)
Your class uses std::istream for the inputfile member, but does not initialize it. That will not work.
In this situation, your class would need to use std::ifstream instead for its inputfile member, and then call its open() method before trying to read from it.
Also, your reader() method is ignoring the inputfile member, instead creating a local variable of the same name to read from. You need to get rid of that local variable, and instead call open() on your class member.
Following #Remy Lebeau suggestions, the modified code below at least compiles correctly (still does not do what it is supposed to do though)
// parses .asm assembly files
#include <iostream>
#include <fstream>
#include <varargs.h>
#include <string>
using namespace std;
class parser
{
private:
istream inputfile;
char inputname[30];
string line;
bool endfile;
bool a_command, l_command, c_command;
string parsedLine, destParsedLine, compParsedLine, jumpParsedLine;
public:
// default parser constructor
parser()
{
}
// ignores inputfile line if comment or empty
void ignoreline()
{
if (line[0] == '/' || line.empty())
inputfile.ignore(line.length(),'\n');
}
// composes inputfile name and opens input file
void reader(char* argv[])
{
strcpy(inputname,argv[1]);
strcat(inputname,".asm");
// opens input .asm file
inputfile.open(inputname, fstream::in);
// reads first line
getline(inputfile,line);
ignoreline();
}
// checks if at end file
bool hasMoreCommands()
{
a_command = false;
l_command = false;
c_command = false;
endfile = false;
if (inputfile.eof())
endfile = true;
return endfile;
}
// advances read of inputfile
void advance()
{
ignoreline();
getline(inputfile,line);
}
/* function for labelling the type of command (address,computation,label) */
bool commandType()
{
if (line[0] == '#')
a_command = true;
else if (line[0] == '(')
l_command = true;
else
c_command = true;
return a_command, l_command, c_command;
}
// function to select parsing function
string selector()
{
if (a_command || l_command)
symbol();
else if (c_command)
{
dest();
comp();
jump();
string parsedLine = destParsedLine + compParsedLine + jumpParsedLine;
}
return parsedLine;
}
// function returning address or label symbol
string symbol()
{
if (a_command)
string parsedLine = line.substr(1);
else if (l_command)
string parsedLine = line.substr(1,line.length()-1);
return parsedLine;
}
// functions returning computation destination
string dest()
{
size_t equal = line.find('='); //no '=' found = returns 'npos'
string destParsedLine = line.substr(0,equal);
return destParsedLine;
}
string comp()
{
size_t equal = line.find('=');
size_t semicolon = line.find(';');
string compParsedLine = line.substr(equal,semicolon);
return compParsedLine;
}
string jump()
{
size_t semicolon = line.find(';');
string jumpParsedLine = line.substr(semicolon);
return jumpParsedLine;
}
};
// main program
int main (int argc, char *argv[])
{
bool endfile = false;
string parsedLine;
int count = 0;
if ((argc != 2) || (strchr(argv[1],'.') != NULL))
{
cout << argv[0] << ": assembly .asm file argument should be supplied, without .asm extension\n";
return 1;
}
parser attempt1 = parser();
attempt1.reader(argv);
while (!endfile)
{
attempt1.hasMoreCommands();
if (endfile)
return 0;
if (count > 0)
attempt1.advance();
attempt1.commandType();
attempt1.selector();
cout << parsedLine << endl;
count++;
}
return 0;
}
Here is the code:
void Reader::read(short& in) {
char* str = new char[6];
char* strbeg = str;
cin.getline(str, 6);
in = 0;
int value = 0;
short sign = 1;
if (*str == '+' || *str == '-') {
if (*str == '-' ) sign = -1;
str++;
}
while (isdigit(*str)) {
value *= 10;
value += (int) (*str - '0');
str++;
if (value > 32767) {
cout.write("Error, value can't fit short. Try again.\n", 41);
delete[] strbeg;
read(in);
return;
}
}
if (sign == -1) { value *= -1; }
in = (short) value;
delete[] strbeg;
return;
}
What happens is that if I type 999999999, it calls itself but on fourth line it's not gonna ask for input again. Debugger couldn't give much info as it is more language-specific question. Thank you in advance. Have a nice day!
Yes, the goal is to parse input as short. I know about losing 1 from min negative, wip :)
=== edit ===
I've tried goto... No, same thing. So it's not about visible variables or addresses, I guess.
=== edit ===
I can't use operator >> as it is forbidden by the task.
999999999 will cause an overflow, thus failbit is set for cin. Then your program reach read(in), then the cin.getline(). Here, beacause of failbit, cin will not ask any input again.
If you tried to figure out why in my code cin do ask for more input, you might find out all this by yourself.
I write you an example.
#include <iostream>
#include <climits>
using namespace std;
int main() {
char str[6];
short x = 0;
bool flag = false;
while (flag == false) {
cin.getline(str, 6);
flag = cin.good();
if (flag) { // if read successfully
char *p = str;
if (*p=='-') // special case for the first character
++p;
while (*p && *p>='0' && *p<='9')
++p;
if (*p) // there is a non digit non '\0' character
flag = false;
}
if (flag == false) {
cout << "An error occurred, try try again." << endl;
if (!cin.eof()) {
cin.unget(); // put back the possibly read '\n'
cin.ignore(INT_MAX, '\n');
}
cin.clear();
} else {
// str is now ready for parsing
// TODO: do your parsing work here
// for exemple x = atoi(str);
}
}
std::cout << x << std::endl;
return 0;
}
As we have discussed, you don't need new.
Check whether the string read is clean before parsing. If you mix checking and parsing, things will be complicated.
And you don't need recursion.
Read characters from stream by istream::getline seems to be the only option we have here. And when an error occurred, this function really doesn't tell us much, we have to deal with overflow and other problem separately.
I'm looking for a way to write floats/ints/strings to a file and read them as floats/ints/strings. (basically read/write as ios::binary).
I ended up writing it myself. Just wanted to share it with others.
It might not be optimized, but I had some difficulties finding C++ code that mimics C#'s BinaryReader & BinaryWriter classes. So I created one class that handles both read and write.
Quick things to note:
1) "BM" is just a prefix for my classes.
2) BMLogging is a helper class that simply does:
cout << "bla bla bla" << endl;
So you can ignore the calls to BMLogging, I kept them to highlight the cases where we could warn the user.
Here's the code:
#include <iostream>
#include <fstream>
using namespace std;
// Create the macro so we don't repeat the code over and over again.
#define BMBINARY_READ(reader,value) reader.read((char *)&value, sizeof(value))
enum BMBinaryIOMode
{
None = 0,
Read,
Write
};
class BMBinaryIO
{
// the output file stream to write onto a file
ofstream writer;
// the input file stream to read from a file
ifstream reader;
// the filepath of the file we're working with
string filePath;
// the current active mode.
BMBinaryIOMode currentMode;
public:
BMBinaryIO()
{
currentMode = BMBinaryIOMode::None;
}
// the destructor will be responsible for checking if we forgot to close
// the file
~BMBinaryIO()
{
if(writer.is_open())
{
BMLogging::error(BMLoggingClass::BinaryIO, "You forgot to call close() after finishing with the file! Closing it...");
writer.close();
}
if(reader.is_open())
{
BMLogging::error(BMLoggingClass::BinaryIO, "You forgot to call close() after finishing with the file! Closing it...");
reader.close();
}
}
// opens a file with either read or write mode. Returns whether
// the open operation was successful
bool open(string fileFullPath, BMBinaryIOMode mode)
{
filePath = fileFullPath;
BMLogging::info(BMLoggingClass::BinaryIO, "Opening file: " + filePath);
// Write mode
if(mode == BMBinaryIOMode::Write)
{
currentMode = mode;
// check if we had a previously opened file to close it
if(writer.is_open())
writer.close();
writer.open(filePath, ios::binary);
if(!writer.is_open())
{
BMLogging::error(BMLoggingClass::BinaryIO, "Could not open file for write: " + filePath);
currentMode = BMBinaryIOMode::None;
}
}
// Read mode
else if(mode == BMBinaryIOMode::Read)
{
currentMode = mode;
// check if we had a previously opened file to close it
if(reader.is_open())
reader.close();
reader.open(filePath, ios::binary);
if(!reader.is_open())
{
BMLogging::error(BMLoggingClass::BinaryIO, "Could not open file for read: " + filePath);
currentMode = BMBinaryIOMode::None;
}
}
// if the mode is still the NONE/initial one -> we failed
return currentMode == BMBinaryIOMode::None ? false : true;
}
// closes the file
void close()
{
if(currentMode == BMBinaryIOMode::Write)
{
writer.close();
}
else if(currentMode == BMBinaryIOMode::Read)
{
reader.close();
}
}
bool checkWritabilityStatus()
{
if(currentMode != BMBinaryIOMode::Write)
{
BMLogging::error(BMLoggingClass::BinaryIO, "Trying to write with a non Writable mode!");
return false;
}
return true;
}
// Generic write method that will write any value to a file (except a string,
// for strings use writeString instead).
void write(void *value, size_t size)
{
if(!checkWritabilityStatus())
return;
// write the value to the file.
writer.write((const char *)value, size);
}
// Writes a string to the file
void writeString(string str)
{
if(!checkWritabilityStatus())
return;
// first add a \0 at the end of the string so we can detect
// the end of string when reading it
str += '\0';
// create char pointer from string.
char* text = (char *)(str.c_str());
// find the length of the string.
unsigned long size = str.size();
// write the whole string including the null.
writer.write((const char *)text, size);
}
// helper to check if we're allowed to read
bool checkReadabilityStatus()
{
if(currentMode != BMBinaryIOMode::Read)
{
BMLogging::error(BMLoggingClass::BinaryIO, "Trying to read with a non Readable mode!");
return false;
}
// check if we hit the end of the file.
if(reader.eof())
{
BMLogging::error(BMLoggingClass::BinaryIO, "Trying to read but reached the end of file!");
reader.close();
currentMode = BMBinaryIOMode::None;
return false;
}
return true;
}
// reads a boolean value
bool readBoolean()
{
if(checkReadabilityStatus())
{
bool value = false;
BMBINARY_READ(reader, value);
return value;
}
return false;
}
// reads a character value
char readChar()
{
if(checkReadabilityStatus())
{
char value = 0;
BMBINARY_READ(reader, value);
return value;
}
return 0;
}
// read an integer value
int readInt()
{
if(checkReadabilityStatus())
{
int value = 0;
BMBINARY_READ(reader, value);
return value;
}
return 0;
}
// read a float value
float readFloat()
{
if(checkReadabilityStatus())
{
float value = 0;
BMBINARY_READ(reader, value);
return value;
}
return 0;
}
// read a double value
double readDouble()
{
if(checkReadabilityStatus())
{
double value = 0;
BMBINARY_READ(reader, value);
return value;
}
return 0;
}
// read a string value
string readString()
{
if(checkReadabilityStatus())
{
char c;
string result = "";
while((c = readChar()) != '\0')
{
result += c;
}
return result;
}
return "";
}
};
EDIT: I replaced all the read/write methods above with these: (updated the usage code as well)
// Generic write method that will write any value to a file (except a string,
// for strings use writeString instead)
template<typename T>
void write(T &value)
{
if(!checkWritabilityStatus())
return;
// write the value to the file.
writer.write((const char *)&value, sizeof(value));
}
// Writes a string to the file
void writeString(string str)
{
if(!checkWritabilityStatus())
return;
// first add a \0 at the end of the string so we can detect
// the end of string when reading it
str += '\0';
// create char pointer from string.
char* text = (char *)(str.c_str());
// find the length of the string.
unsigned long size = str.size();
// write the whole string including the null.
writer.write((const char *)text, size);
}
// reads any type of value except strings.
template<typename T>
T read()
{
checkReadabilityStatus();
T value;
reader.read((char *)&value, sizeof(value));
return value;
}
// reads any type of value except strings.
template<typename T>
void read(T &value)
{
if(checkReadabilityStatus())
{
reader.read((char *)&value, sizeof(value));
}
}
// read a string value
string readString()
{
if(checkReadabilityStatus())
{
char c;
string result = "";
while((c = read<char>()) != '\0')
{
result += c;
}
return result;
}
return "";
}
// read a string value
void readString(string &result)
{
if(checkReadabilityStatus())
{
char c;
result = "";
while((c = read<char>()) != '\0')
{
result += c;
}
}
}
This is how you would use it to WRITE:
string myPath = "somepath to the file";
BMBinaryIO binaryIO;
if(binaryIO.open(myPath, BMBinaryIOMode::Write))
{
float value = 165;
binaryIO.write(value);
char valueC = 'K';
binaryIO.write(valueC);
double valueD = 1231.99;
binaryIO.write(valueD);
string valueStr = "spawnAt(100,200)";
binaryIO.writeString(valueStr);
valueStr = "helpAt(32,3)";
binaryIO.writeString(valueStr);
binaryIO.close();
}
Here's how you would use it to READ:
string myPath = "some path to the same file";
if(binaryIO.open(myPath, BMBinaryIOMode::Read))
{
cout << binaryIO.read<float>() << endl;
cout << binaryIO.read<char>() << endl;
double valueD = 0;
binaryIO.read(valueD); // or you could use read<double()
cout << valueD << endl;
cout << binaryIO.readString() << endl;
cout << binaryIO.readString() << endl;
binaryIO.close();
}
EDIT 2: You could even write/read a whole structure in 1 line:
struct Vertex {
float x, y;
};
Vertex vtx; vtx.x = 2.5f; vtx.y = 10.0f;
// to write it
binaryIO.write(vtx);
// to read it
Vertex vtxRead;
binaryIO.read(vtxRead); // option 1
vtxRead = binaryIO.read<Vertex>(); // option 2
Hope my code is clear enough.
I subclassed ifstream and ofstream: ibfstream and obfstream. I made a little helper class that would detect the endianness of the machine I was compiling/running on. Then I added a flag for ibfstream and obfstream that indicated whether bytes in primitive types should be flipped. These classes also had methods to read/write primitive types and arrays of such types flipping the byte order as necessary. Finally, I set ios::binary for these classes by default.
I was often working on a little-endian machine and wanting to write big-endian files or vice versa. This was used in a program that did a lot of I/O with 3D graphics files of various formats.
I subclassed ifstream and ofstream: ibfstream and obfstream. I made a class that would detect the endianness of the machine I was compiling/running on. Then I added a flag for ibfstream and obfstream that indicated whether bytes in primitive types should be flipped. These classes also had methods to read/write primitive types and arrays of such types flipping the byte order as necessary.
I was often working on a little-endian machine and wanting to write big-endian files or vice versa. This was used in a program tht did a lot of I/O with 3D graphics files of various formats.
i'm new in c++ world, i just use it for litle app that help me in my work, now, i need to read the content of a folder, list the folder content, i've made a function that return a pointer with the name of every obj in the folder, but now, i don't know how to read the content of the pointer to just print it in a console, my function look like this
string* listdir (const char *path)
{
string* result = new string[50]; // limit to 50 obj
DIR *pdir = NULL;
pdir = opendir (path);
struct dirent *pent = NULL;
if (pdir == NULL)
{
printf ("\nERROR! pdir could not be initialised correctly");
return NULL;
}
int i = 0;
while (pent = readdir (pdir))
{
if (pent == NULL)
{
printf ("\nERROR! pent could not be initialised correctly");
return NULL;
}
//printf ("%s\n", pent->d_name);
result[i++]= pent->d_name;
}
closedir (pdir);
return result;
}
i've been trying to print the result of teh function
int main()
{
string *dirs;
dirs = listdir("c:\\");
int i = 0;
//while(dirs[i])
//{
//cout<<dirs[i]<<'\n';
//++i;
//}
}
but i really don't know what i'm doing, lol, some help would be perfect
thanks
Examine your while loop condition : dirs[i] is a std::string. You are using a string object in a boolean context : would you expect std::string to convert to bool ?
My recommendation : ditch the fixed sized array and go for std::vector.
void listdir(const char *path, std::vector<std::string> &dirs)
{
/* ... */
while (pent = readdir (pdir))
{
/* ... */
dirs.push_back(pent->d-name);
}
closedir(pdir);
}
int main()
{
std::vector<std::string> dirs;
listdir("c:\\", dirs);
for (std::vector<std::string>::const_iterator it = dirs.begin(), end = dirs.end(); it != end; ++it)
std::cout << *it << std::endl;
}
int main()
{
string *dirs;
dirs = listdir("c:\\");
for (int i = 0; i < 50 && dirs[i].size() > 0; ++i)
{
cout << dirs[i] << '\n';
}
}
dirs is a pointer to an array, so you can index it like an array. You created an array of 50, so you need to limit yourself to 50 here too. Since you might not have populated the whole array, the .size() check allows the printing loop to stop early.
There is some major confusion in your code, especially between arrays of characters, strings and arrays of strings. Also, there is a memory leak.
Here are my questions / concerns:
Issues / Concerns
The opendir function may be called
with a null parameter. You should
check for a null path before calling
opendir.
Returns NULL after declaring some
variables. IMHO, one should check
parameters before declaring
variables.
How does one know how many valid
entries are in the returned array?
If the size of the array (known only
to the listdir function) changes,
the users of the function are
doomed.
Is the type of pent->d_name the
same as string *?
The address of the directory name,
pent->d_name, is copied into the
results array, but not the content
of the directory name. The OS may
reuse this location without telling
you; so copying the address of the
text is not a good idea.
The main function does not delete
the memory allocated for the
results. This is known as a memory
leak.
Suggestions / Fixes
Use std::string within the
function. This takes care of
allocating memory for text.
Use std::vector<string> for the
results. This takes care of knowing
the quantity of directories and no
need to dynamically allocate or
deallocate memory.
Create a std::string from the
pent->d_name and use push_back
to append the string to the results.
In C++, dereferencing a pointer is achieved using the * operator, just like in 'C'.
However, there are a number of problems with your code, which I have addressed here because I was bored...
#include <string>
#include <iostream>
#include <list>
#include <dirent.h>
typedef std::list<std::string> dir_list;
bool listdir(const std::string& path, dir_list& result)
{
bool retval = true;
DIR* pdir = opendir(path.c_str());
if (pdir == NULL)
{
std::cerr << "ERROR! pdir could not be initialised correctly" << std::endl;;
retval = false;
}
else
{
for (dirent* pent = readdir(pdir); pent != NULL; pent = readdir(pdir))
{
if (pent == NULL && result.empty())
{
std::cerr << "ERROR! pent could not be initialised correctly" << std::endl;
retval = false;
}
if (result.size() < 50)
{// *really* limit to 50!
result.push_back(pent->d_name);
}
}
closedir(pdir);
}
return retval;
}
int main()
{
dir_list dirs;
if (listdir("C:/", dirs))
{
for (dir_list::const_iterator iter(dirs.begin()), end(dirs.end()); iter != end; ++iter)
{
std::cout << *iter << std::endl;
}
}
}
Since you're using C++, STL, its string and container classes will save you a World of pointer pain!
your while statement looks strange, it looks like it's expecting dirs[i] to be a pointer, but I believe dirs[i] will be a non-pointer type. Maybe change it to (assuming string is std::string):
while(i < 50 && dirs[i].length() > 0)
{
cout<<dirs[i]<<'\n';
++i;
}