C++: Member functions that read and write from txt file - c++

I apologize in advance for how poorly this question is asked, I'm really struggling here.
I am writing a class named Point in C++ with private members x and y, and member functions getX, getY, setX, setY, read and write. I have been able to do everything except read and write, as I am awful with input/output files. I have the following declaration for read and write:
void read(istream& ins);
void write(ostream& outs);
The RME is as follows for read:
* Requires: ins is in good state.
* Modifies: ins, x, y.
* Effects: Reads point in form (x,y)
and for write:
* Requires: outs is in good state.
* Modifies: outs.
* Effects: Writes point in form (x,y).
'read' takes ordered points like (1, 5), (2, 7), etc. from a given file "data1.txt" and extracts the x and y components (at least, I believe this is what it should do). I was provided with a test suite for read:
void test_point() {
Point pt1;
pt1.setX(15);
cout << "pt1 is: " << pt1 << endl;
ifstream input_file;
input_file.open("data1.txt");
pt1.read(input_file);
cout << "pt1 is: " << pt1 << endl;
return;}
I really have no idea how to write the read function. I have tried defining characters a, b, c, and integers u, v, and executing:
ins >> a >> u >> b >> v >> c;
but that didn't work. Could someone please help me see how to implement this?

Quite a few things missing from your question, that you will need such that the use of this class to be viable. For one, reading a file of ordered points should not be implemented as a member function. If anything, you could use a loop as such:
while(input_file) {
// set a point to have members x, y that were read from file
// store this point in a vector of points
}
Otherwise there is no reasonable way to store a bunch of points that you have read from a file.
Your idea for this solution within the read function should definitely work (assuming chars a, b, c, and ints u, v):
input_stream >> a >> u >> b >> v >> c;
In fact, with the format that you have given us (taking an ifstream as an argument), there really is no need for be much else in the read function, as it mutates the object and doesn't need to return anything. Your implementation is the simplest way to parse such "records" in a structured file.
After you read all these, set the x coordinate of the caller object to u, and the y to v. This should be a void function, does not need to return anything but should alter the Point object that it is being called for. This member function should be called on a temporary point object in the loop that I mentioned, then add that object to a vector of points as such:
vector<Point> points;
while(...) {
// declare Point
// initialize with values from read
points.push_back(//the point you just created);
}
If in fact you need to be able to read multiple points.
In summary, your read function needs:
To check that the ifstream is actually good first of all, which I didn't mention yet (but this is one of your requirements):
if (!input_file) { //however you want to handle this error }
The temporary char and int variables (serving as buffers) to read into (by the way, you can even read straight into x and y instead of reading into u and v then copying, just saying).
If you choose to not read straight into x and y, you must assign the u and v values to x and y. Now your object is complete.
As for the write function, instead of std::cout you will use the name of the ofstream that was passed as an argument to the write function, and write the records with the format that you have shown. This is (in essence) no different from printing to a console, except the output is on a text file.
Note: Make sure you understand the difference between iostream objects (istream, ostream) and fstream (ifstream, ofstream, fstream) objects, which are preferable in this case.

Related

How can one store multiple variables in one variable using c++? For example, doubles x,y,z are contained within a variable point p

I am teaching myself c++ and while on the topic of pointers, I came across an exercise which wants me to define a function length() that receives the coordinates of a point P passed as a pointer, and computes the distance from the origin to the point P:
double length(Coord3D *p);
int main()
{
Coord3D pointP = {10, 20, 30};
cout << length(&pointP) << endl; // would print 37.4166
}
class Coord3D // Given this class which had variables x,y,z.
{
public:
double x;
double y;
double z;
};
I am confused as to what my next steps are for example I am thinking I should use pointers to create a variable p and set *p to the class.
Also I think the function at the very top should contain the formula which would be sqrt(pow(x,2)+pow(y,2)+pow(z,3)). If I am right then how can I implement the pointer properly and if I am wrong what I am I doing wrong and how can I fix it.
Please keep in mind this is my first time learning so I am trying my best.
The code that I wrote so far is :
#include<iostream>
#include<string>
#include<cmath>
using namespace std;
// Class
class Coord3D
{
public:
double x;
double y;
double z;
};
int p = Coord3D;
p*= Coord3D
// Function
double length(Coord3D *p)
{
double length = sqrt(pow(x,2)+pow(y,2)+pow(z,3));
cin>>x;
cin>>y;
cin>>z;
cout << length << endl;
}
// Main
int main()
{
Coord3D pointP = {10, 20, 30};
cout << length(&pointP) << endl; // would print 37.4166
}
Assuming the code works the output should a double that shows the distance from origin to point p.
Trouble writing a function is often a sign that more time needs to be spent designing the function. Let's start by moving your description of the function into the function's documentation:
// Receives the coordinates of a point P passed as a pointer, and
// computes the distance from the origin to the point P.
double length(Coord3D *p)
It's a start, but it combines three aspects of the function into one statement. Let's try breaking out 1) the function's purpose, 2) the function's input, 3) the function's output.
// Computes the distance from the origin to a point.
// Input: the point P passed as a pointer
// Output: the distance
double length(Coord3D *p)
You've already got an idea of how to accomplish the purpose (the call to the sqrt function), but you're not sure how to get values to that function. (Terminology note: "values", not "variables". The number 10 is a value, but it is not a variable. Variables have names, like "pointP". A single variable may hold one, many, or even no values, depending on its type.) So we should take a closer look at your function's input requirement.
// Input: the point P passed as a pointer
Well, that's not exactly right, is it? The inputs to a function come via parameters, but this function has no parameter named "P" (remember that case matters). In fact, the name "P" seems to refer to the variable in the main function. One important rule for writing good functions: keep it self-contained. In particular, we don't care what the caller calls things. So toss that name. We are only interested in "the point". (Which point? The one mentioned in the statement of the function's purpose. More complex cases would need to be more specific, but we're starting simple. You can work on more complex once you've got the simple down.)
Also, if the input parameter is a pointer, then it cannot be a point. This is where one of your programming skills comes in: translate the natural language description into more precise terms. A parameter cannot be "as a pointer"; either it is a pointer or it is not. Get rid of the wishy-washy and take a stand. We want a pointer!**
// Computes the distance from the origin to a point.
// Input: a pointer to the point
// Output: the distance (via a return statement)
double length(Coord3D *p)
Now hopefully things are becoming a bit clearer. This function needs to calculate the distance to the thing to which its parameter points (i.e. *p). It's not some "variable x" that you need to square, but the x-coordinate of *p. More precisely, you need to square the x field of the object to which p points, a.k.a. p->x. Similarly for y and z.
[Get rid of the statements between your class definition and your function definition -- they serve no purpose other than generating confusion. Get rid of the streaming operations (>> and <<) in your function -- that is not how this function is supposed to do its input/output. Input is via its parameter, and output is via its (currently missing) return statement.]
**Actually, we probably want a reference instead of a pointer, but that might be a future lesson.
Fixed version:
#include <iostream>
#include <cmath>
class Coord3D
{
public:
double x;
double y;
double z;
};
double length(const Coord3D& p)
{
return sqrt(pow(p.x, 2) + pow(p.y, 2) + pow(p.z, 2));
}
std::istream& operator >> (std::istream& is, Coord3D& p)
{
return is >> p.x >> p.y >> p.z;
}
int main()
{
Coord3D pointP = {10, 20, 30};
std::cout << length(pointP) << std::endl; // would print 37.4166
// And now from input:
std::cin >> pointP;
std::cout << length(pointP) << std::endl;
}

saving object information into a binary file

I m trying to save all the member variables of an object in a binary file. However, the member variables are vectors that is dynamically allocated. So, is there any way to combine all the data and save it in a binary file. As of now, it just saves the pointer, which is of little help. Following is my running code.
#include <vector>
#include <iostream>
#include <fstream>
class BaseSaveFile {
protected:
std::vector<float> first_vector;
public:
void fill_vector(std::vector<float> fill) {
first_vector = fill;
}
void show_vector() {
for ( auto x: first_vector )
std::cout << x << std::endl;
}
};
class DerivedSaveFile : public BaseSaveFile {
};
int main ( int argc, char **argv) {
DerivedSaveFile derived;
std::vector<float> fill;
for ( auto i = 0; i < 10; i++) {
fill.push_back(i);
}
derived.fill_vector(fill);
derived.show_vector();
std::ofstream save_object("../save_object.bin", std::ios::out | std::ios::binary);
save_object.write((char*)&derived, sizeof(derived));
}
Currently size of the binary file is just 24 bytes. But I was execting much larger because of the vector of 10 floats.
"is there any way to combine all the data and save it in a binary file" - of course there is. You write code to iterate over all the data and convert it into a form suitable for writing to a file (that you know how to later parse when reading it back in). Then you write code to read the file, parse it into meaningful variables classes and construct new objects from the read-in data. There's no built-in facility for it, but it's not rocket science - just a bunch of work/code you need to do.
It's called serialisation/de-serialisation btw, in case you want to use your preferred search engine to look up more details.
The problem
You can write the exact binary content of an object to a file:
save_object.write((char*)&derived, sizeof(derived));
However, it is not guaranteed that you you read it back into memory with the reverse read operation. This is only possible for a small subset of objects that have a trivially copyable type and do not contain any pointer.
You can verify if your type matches this definition with std::is_trivially_copyable<BaseSaveFile>::value but I can already tell you that it's not because of the vector.
To simplify a bit the formal definition, trivially copyable types are more or less the types that are composed only of other trivially copiable elements and very elementary data types such as int, float, char, or fixed-size arrays.
The solution: introduction to serialization
The general solution, as mentionned int he other response it called serialization. But for a more tailored answer, here is how it would look like.
You would add the following public method to your type:
std::ostream& save(std::ostream& os){
size_t vsize=first_vector.size();
os.write((char*)&vsize, sizeof(vsize));
os.write((char*)first_vector.data(), vsize*sizeof(float));
return os;
}
This method has access to all the members and can write them to the disk. For the case of the vector, you'd first write down its size (so that you know how big it is when you'll read the file later on).
You would then add the reverse method:
std::istream& load(std::istream& is){
size_t vsize;
if(is.read((char*)&vsize, sizeof(vsize))) {
first_vector.resize(vsize);
is.read((char*)first_vector.data(), vsize*sizeof(float));
}
return is;
}
Here the trick is to first read the size of the vector on disk, and then resize the vector before loading it.
Note the use of istream and ostream. This allows you to store the data on a file, but you could use any other kind of stream such as in memory string stream if you want.
Here a full online example (it uses stringstream because the online service doesn't provide for files to be written).
More serialization ?
There are some serialization tricks to know. First, if you have derived types, you'd need to make load() and save() virtual and provide the derived types with their own overridden version.
If one of your data member is not trivially copyable, it would need its own load() and save() that you could then invoke recursively. Or you'd need to handle the thing yourself, which is only possible if you can access all the members you'd need to restore its state.
Finally, you don't need to reinvent the wheel. There are some libraries outside that may help, like boost serialisation or cereal

Override methods in C++

I was supposed to create a base class (P2D) that represents a point in two dimensions and a derivate class (P3D) that represents a point in three dimensions. In the first class I created a method this way:
virtual istream& set(istream&); //sets the values
istream& P2D::set(istream& in){
in >> x >> y;
return in;
}
In the second class, instead:
istream& set(istream&); //sets the values
istream& P3D::set(istream& in){
P2D::set(in);
in >> z;
return in;
}
In the main function i thought about putting all points (regardless of whether they are in two or three dimensions) in a vector, and then about setting all points in a while cycle somehow using the set method above.
Here is the piece of code in question:
vector<P2D*> points;
bool ans(true);
P2D* p;
while(ans){
cout << "Insert a point" << endl;
p->set();
points.push_back(p);
cout << "Do you want to insert another point? ";
cin >> ans;
}
I know this is wrong and i know why, but is there a chance to overcome that not using the dinamic memory? Am i forced to let the user choice if he wants to insert a two or a three dimension point?
This seems broken to me, as inheritance implies an 'is a' relation, and 3D point definitely is not a 2D point.
This reflects on your problem, as there is no elegant way to do it as you suggest. IMO, user has to decide whether to enter 2D or 3D point, but still it doesn't make sense to keep it as a collection of 2D points.
It all depends on what you intend to do later with points in the collection. Maybe you should have a Point type as parent of both 2D and 3D point, but it is hard to tell without bigger context.

C++ vectors with structs not working?

I was working with some files and trying to load them. I wanted to use a vector to store the final information, so I can keep it globally without needing to know how big it is. This is my code but the program doesn't finish launching:
std::string one = "v 100.32 12321.232 3232.6542";
struct Face {float x, y, z;};
std::vector<struct Face> obj;
char space[3];
sscanf(one.c_str(), "%s %f %f %f", space, &obj[1].x1, &obj[1].y1, &obj[1].z1);
std::cout << obj[1].x1 << std::endl;
Default constructed vectors start empty, and even though the compiler lets you use operator [], it's undefined behavior to do so.
You can allocate some space when you create the vector though:
std::vector<struct Face> obj(2); // Allow enough space to access obj[1]
If you want to write to element 1 in the vector, the vector must have size() >= 2. In your example, size() is always 0.
Consider creating a temporary Face and then push_back-ing it into the vector<Face>.
Maybe you are using sscanf for a good reason, but at least I think is good to point that you can use streams to load the info into the struct.
In this case, I recommend you to use istringstream class, wich lets you read values as values from a string, casting as needed. So, your code, I think I can change it to this:
std::string one = "v 100.32 12321.232 3232.6542";
struct Face {float x,y,z;};
std::vector<struct Face>obj;
char space[3];
// As mentioned previously, create a temporal Face variable to load the info
struct Face tmp; // The "struct" maybe can be omited, I prefer to place it.
// Create istringstream, giving it the "one" variable as buffer for read.
istringstream iss ( one );
// Replace this line...
//sscanf(one.c_str(), "%s %f %f %f",space,&obj[1].x1,&obj[1].y1,&obj[1].z1);
// With this:
iss >> space >> tmp.x >> tmp.y >> tmp.z;
// Add the temporal Face into the vector
obj.push_back ( tmp );
// As mentioned above, the first element in a vector is zero, not one
std::cout << obj[0].x1 << std::endl;
The istringstream class (you need to include "sstream") is useful in this cases, when you have values to load from a string.
I hope my answer can help you in any way.

Write/Read a kdtree into a file

I am new to C++ and data structure, I have code to approximate the nearest neighbors, and for that I implemented a Kd-tree in C++.
My question how can I write the kd-tree into a file and how to read it from that file?
Thanks for any help
See boost::serialization. You may choose between several output formats - plain text, xml, binary
If you're new to C++, you just have to understand what exactly do you need and implement it in a correct simple way. So no boost dependency is needed.
At first - your kd-tree likely stores pointers to objects and do not own them. Consider dumping\loading via structures that actually own objects (that is responsible for their life time), thus avoiding duplicates and leaks. At second - usually trees are not stored in files, instead they are constructed each time you load some geometry because they require more storage than just an array of objects and they can contain duplicates, that you need to track separately.
Thereby, if you figured out who owns your objects, your read\write procedures will look like
int main(int argc, char** argv) {
std::string filename = "geometty_dump.txt"
if (argc == 2) { // filename explicitly provided
filename = *argv[1];
}
ProblemDriver driver; // stores geometry owner\owners
bool res = driver.GetGeometry(filename);
if (res) res = driver.SolveProblem();
if (res) res = driver.DumpGeometry();
return res;
}
In the place where you access geometric data itself (like double x, y;) you must include <iostream>, try to read something about C++ i\o if your question is about it. Objects that own x, y must have friend correspondent functions
ostream& operator<< (ostream out&, const MyPoint& point) {
out << point.x() << point.y() << '\n';
}
ostream& operator>> (istream in&, MyPoint& point) {
double x, y;
in >> x >> y;
point.set(x, y);
}
Meaning you create ofstream and ifstream repectively in ProblemDriver methods (GetGeometry, DumpGeometry) that invoke these functions.