C++ Need to make functions more "general" (and re-usable) - c++

I'm working on a simple program with a friend. I wrote a few I/O functions to plug into the program (they just read from/write to txt files). I'm planning to add them in a header file to be included in the main program.
The program manages collections of items by doing a few CRUD operations. For now, it only stores into memory. There are 3 different structs (to represent the different kind of objects) and 3 vectors (one for each type of struct) to represent the 3 collections.
For each vector of a different struct type (which I've called t_item in this example) I made three I/O functions:
void serialize_item(const string &file_name, t_item &an_item)
Declares a ofstream variable and appends (ios::app) each item field to the txt file. Then calls file.close()
void serialize_all_items(const string &file_name, vector<t_item> &items)
Declares a ofstream variable and overwrites (ios::trunc) file contents. Iterates through vector, writing each struct field to the txt file. Then calls file.close()
void deserialize_items(const string &file_name, vector<t_item> &items)
Declares a local t_item variable and a ifstream variable. Iterates while reading the file contents and saving each item field to the t_item variable. When an item is complete, it calls push_back to save it into the vector and goes back to reading the next item from file. Then calls file.close()
The thing is: me and my friend are working separately on different parts of the code. Since he declared all the structs, he got to decide what name to use for each struct and what are the names of the fields (although we both agreed there would be a certain amount of fields in each struct and they would be of certain type, since that's the design we need to implement).
My question now is: is there a way for me to implement the I/O functions without knowing the name of the structs and fields he used? For example, this function:
void serialize_person(const string &file_name, type_person &a_person)
{
ofstream personsFile(file_name, ios::app);
personsFile<< a_person.name << endl;
personsFile<< a_person.age << endl;
personsFile<< a_person.gender << endl;
personsFile.close();
}
My friend could have chosen to name the struct "onePerson" instead of "type_person" and the fields could be "fullName", "age", "sex". But the structure is pretty much the same.
How can I tweak my I/O functions to fit the same structure despite the struct/field names the other programmer chose?

Could you use an union? You can avoid reference each variable instead of... an array?
For example:
#include <stdio.h>
union myStrings
{
struct fields
{
char *name;
char *age;
char *gender;
} myFields;
char *arrayPointers[3];
};
int main (void)
{
char name[] = "David";
char age[] = "38";
char gender[] = "Male";
myStrings st;
st.myFields.name = name;
st.myFields.age = age;
st.myFields.gender = gender;
for( int i = 0 ; i < 3 ; i++ )
printf( "%s\n", st.arrayPointers[i] );
return 0;
}
Result (without use any name):
David
38
Male
Live example

Related

Getting errors trying to pass a vector to a class's function C++

Im trying to read a text file "dictionary.txt" that contains some words with their definition and type. Each word is meant to be loaded into a Word class object with the definition and type, this object is then meant to be pushed to a vector array of other Word objects.
However I'm getting the errors:
E0147 declaration is incompatible with "void Dictionary::loadDictionary(std::vector<<error-type> std::allocator<<error-type>>> &vect)" (declared at line 27)
and
E0020 identifier "loadDictionary" is undefined.
I'm pretty new to C++ and OOP in general so would love some help with these errors.
Thanks heaps!
Code:
#include "stdafx.h"
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Dictionary
{
public:
void loadDictionary(vector<Word>& vect);
private:
Word w1;
string word;
string def;
string type;
};
void Dictionary::loadDictionary(vector<Word>& vect)
{
ifstream dicFile;
dicFile.open("dictionary.txt");
if (!dicFile)
{
cout << "File not found!" << endl;
exit(1);
}
int count1 = 0;
while (!dicFile.eof())
{
w1 = new Word;
dicFile >> word;
dicFile >> def;
dicFile >> type;
w1.word->word;
w1.def->def;
w1.type->type;
vect.push_back(w1);
}
}
class Word
{
public:
private:
string word;
string definition;
string type;
};
Word::Word() {
word = "";
definition = "";
type = "";
}
int main()
{
Dictionary d;
vector<Word> word;
d.loadDictionary(word);
return 0;
}
Here is a set of suggestions to make everything work and start thinking
to the problem in a more OOP way (but this may be subjective).
As others pointed out, the main problem is that w1 is a Word and you try to do
w1 = new Word;
This makes no sense, since new Word creates a pointer to a Word (a Word*),
which is not what you want. C++ is not Java, in which everything is an implicit
pointer to something. Here you can have automatic objects (Word) and pointers
to objects (Word*).
From a class design point of view you create a Word which is supposed to keep together the three
strings word, definition, and type. Ok. What is a Dictionary? The name suggests
it is a container for words, so the vector should be an attribute of the Dictionary,
rather than a parameter which gets filled. Otherwise the name should have been
DictionaryLoader or something in those lines.
So I'd start by fixing the Word class. To make things simpler I suggest that you have
everything public, so I'll use struct instad of class. Following
Google C++ Style Guide I
added an underscore after the member variable names. Since the initialization is not needed,
I'd avoid it. Instead you will load words from a stream, so it may be a nice idea
to have a method for loading a word. An operator would be even better, but let's
leave it for the future.
The way you were reading didn't allow for definitions including spaces. So I took the liberty of using getline to use quoted strings (no quotes inside!).
This is an example dictionary.txt (which you should have included in your question! Remember Minimal, Complete, and Verifiable example):
sovereign "a king or queen" noun
desk "a type of table that you can work at, often one with drawers" noun
build "to make something by putting bricks or other materials together" verb
nice "pleasant, enjoyable, or satisfactory" adjective
And here goes the code.
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
struct Word {
std::string word_;
std::string definition_;
std::string type_;
std::istream& read(std::istream& is) {
is >> word_;
std::string skip;
std::getline(is, skip, '"');
std::getline(is, definition_, '"');
is >> type_;
return is;
}
};
Now the Dictionary. A dictionary is a container for words, so our dictionary should
have a vector of words inside it. All the variables which were in your dictionary
were not really in the right place. You were using them as temporaries, so they should
have been placed inside your function.
struct Dictionary {
std::vector<Word> vect_;
bool load(const std::string& filename) {
std::ifstream is("dictionary.txt");
if (!is)
return false;
while (true) {
// Read
Word w;
w.read(is);
// Check
if (!is)
break;
// Use
vect_.push_back(w);
}
/* Alternative
Word w;
while (w.read(is)) { // Read & Check
// Use
vect_.push_back(w);
}*/
/* Another alternative
for (Word w; w.read(is);) { // Read & Check
// Use
vect_.push_back(w);
}*/
return true;
}
};
int main()
{
Dictionary d;
if (d.load("dictionary.txt"))
return EXIT_SUCCESS;
else
return EXIT_FAILURE;
}
Check the Dictionary::load function. The rule is simple: Read, Check, Use. My suggestion is to always start with an infinite loop with the three comments. Then add the relevant code to make the read, then that for checking, and finally use what you just read. Then look for more compact alternatives, if you really need them.
Ah, I just remembered: since you are using VisualStudio, do yourself a favor: don't use precompiled headers. You don't know what they are and, believe me, you won't need them for a very long time. So, create your projects with "Windows Desktop Wizard", don't create a directory for the solution, and in the following dialog select "Empty Project".
If you push all of code to single file, then just make an object of Dictionary and call to function loadDictionary()
Dictionary d;
d.loadDictionary(word);
The code needs to be revised completely. But some quick fixes that are pretty obvious:
The "new" operator returns pointer to the object. In your code you are storing the pointer into a non-pointer variable of the type "Word".
Inside the "while" loop you are trying to access a "Word" object inside another "Word" and then accessing private variables of the class inside "loadDictionary" function.
The data between two classes are duplicated.
Instead of a class for "Word" you can use "struct" which is simpler, however it doesn't really matter if you use classes correctly.

Problems while opening a .dat file in c++

so basically I was trying to save a class inside a .dat file but in my code but it says this error No matching member function for call to 'open' but I put fstream header. I don't know if I'm writing something wrong. I use Xcode 10.
class memberinformation
{
string name; //name
long int phonenumber; // phone number
int memberid; // member id
public :
memberinformation()
{ name="not assigned" ;
phonenumber=0;
memberid=0;
}
int option3();
int option2();
int option1();
int option4();
};
void wrt_file() //file function
{
memberinformation k;
fstream f;
f.open ("information.dat",ios::app,ios::binary) //this is where I get the error.
f.write((char*)&k,sizeof(k));
}
You are lucky to have been stopped by a simple error. #Alex44 has already shown how to get rid of the error:
f.open ("information.dat",ios::app|ios::binary); //this is where I get the error.
But the following line is even worse:
f.write((char*)&k,sizeof(k));
because the compiler will not show any error, while the content of the string will not be saved in the file. std::string is not trivially copiable and because of that, the memberinformation class is not either. So you should not try to write it to a file as raw bytes.
You should instead write a serialization function that writes to a binary stream (just a possible serialization way):
phonenumber as a long int (no problem there)
memberid as an int (no problem there)
name.size as a size_t
name.data as name.size bytes
The other two answers have answered:
Why its not compiling.
Why its a bad idea to write binary objects.
I would suggest that you serialize the object via the standard C++ technique of using the stream operators. This makes writting/reading the objects trivial and usually makes debugging problems easy.
Using the format suggested by #serge-ballesta in his post:
class memberinformation
{
string name; //name
long int phonenumber; // phone number
int memberid; // member id
public :
// OLD STUFF GOES HERE
void swap(memberinformation& other) noexcept
{
using std::swap;
swap(name, other.name);
swap(phonenumber, other.phonenumber);
swap(memberid, other.memberid);
}
friend std::ostream& operator<<(std::ostream& str, memberinformation const& data)
{
return str << data.phonenumber << " "
<< data.memberid << " "
<< data.name.size() << " "
<< data.name << " ";
}
friend std::istream& operator<<(std::istream& str, memberinformation& data)
{
memberinformation tmp;
std::size_t nameSize
if (str >> tmp.phonenumber >> tmp.memberid >> nameSize) {
// All sizes were read correctly.
tmp.name.resize(nameSize);
if (str.ignore(1).read(&tmp.name[0], nameSize)) {
// ignored the space and read the name correctly.
// So now we update the "data" object
tmp.swap(data);
}
}
return str;
}
};
Now in your code:
int main()
{
memberinformation object;
std::cout << object;
std::cin >> object;
std::ofstream file("Data.dat");
file << object;
}
You miss a semicolon and you need to "bitwise or" your flags:
void wrt_file() //file function
{
memberinformation k;
fstream f;
f.open ("information.dat",ios::app|ios::binary); //this is where I get the error.
...
}
The answers above address your initial problem. I'm going to talk about two more.
First, you probably should f.close() at the end of your method. It may be perfectly fine to let it drop out of scope and clean up from there, but I personally think that's ugly, and I wouldn't count on it.
Second, I wouldn't store the data in binary unless there's a really good reason to do it. It won't be portable. Serge above suggests a serialization method. I'd consider an alternate approach. I'd write to the file in a human readable form such as JSON. Yes, it's a little more work, but...
-If you change your class, your old files will still be readable
-They are portable across environments
-You can actually look at them and readily understand what they contain
So Serge's suggestions above aren't horrible, but I'd pick a more modern serialization / deserialization style.
Note that your f.write won't work because your object contains other objects, you don't know how they work under the hood. That string, for instance, almost certainly can't be dumped the way you're trying to do it. Plus you aren't only dumping your data.
Also, you should printf the sizeof(k). You might find it interesting information. Try to account for every byte. You could printf the sizeof(k.name) to help you work some of it out.
I'm almost positive the information doing so would surprise you, but I haven't actually done it myself, because I would never try to raw memory copy C++ objects, and that's in effect what you're trying to do.

Reading/Writing to a file in c++

I am trying to reading and write objects to a file in C++, writing the object works fine, reading gives segmentation core dump. I have commented the code for writing objects to file, while writing we can uncomment that part and comment the reading part.
#include<iostream>
#include<fstream>
#include<string>
using namespace std;
class RelianceMart{
string name;
double trolley_number;
public:
RelianceMart(){
name = "NA";
trolley_number = 0;
}
RelianceMart(string name, double trolley_number){
this->name = name;
this->trolley_number = trolley_number;
}
void setname(string name){
this->name = name;
}
string getname(){
return name;
}
void settrolleynumber(double trolley_number){
this->trolley_number = trolley_number;
}
double gettrolleynumber(){
return trolley_number;
}
};
int main(){
string name;
double trl_num;
RelianceMart mart[3];
RelianceMart obj;
// ofstream fout("PersistentStorage.txt");
/*
for(int i=0;i<3;i++){
cin>>name;
cin>>trl_num;
mart[i] = RelianceMart(name, trl_num);
fout.write((char *) & mart[i], sizeof(mart[i]));
}
fout.close();
*/
ifstream fin("PersistentStorage.txt");
while(!fin.eof()){
fin.read((char *) & obj,sizeof(obj));
cout<< obj.getname();
}
fin.close();
return 0;
}
The members of std::string is really nothing more than a member variable for the length, and a member variable being a pointer to the actual string contents.
Pointers are private and unique to a specific process in all modern protected multi-tasking operating systems, no other process (not even one started from the same program) can reuse the same pointer.
When you write the RelianceMart objects, you write the pointer of the name string object to the file. As mentioned above no other process can use this pointer, and therefore can't read the file.
Furthermore when you attempt to read the raw objects, you read raw data overwriting the existing data in the constructed object, and the object won't be properly constructed anymore.
You also don't open the file in binary mode, which is wrong since you write and read raw binary data, not text.
The common solution is to use serialization, and the most common way to do it is simply to overload the "output" and "input" operators << and >>.
In the overloaded functions you simply write and read each object as text, again using the formatted << and >> operators.
Lastly, please read Why is iostream::eof inside a loop condition considered wrong?
I would use a serialization framework, you could use Google's Protocol Buffers(https://developers.google.com/protocol-buffers/).
If you consider a fullblown framework overkill, you can always write your own serialization framework, I've done that, I did use the JSON-format to encode the object.

How can I access an array stored in an object?

I'm quite new to C++. I've been trying to figure this out for days - there'll be an easy solution no doubt but I haven't been able to find it (after much googling)! My problem is this:
I'm trying to create a class with a member function that reads in characters from a file and stores them in an array. I want to be able to create multiple objects (not sure how many - decided by the user), each with their own arrays filled with characters taken from different files. I think I've managed to do that. How would I then go about accessing the object's array in main?
The code I'm working on is long and messy but something along these lines (char.txt contains simply '12345' in this case):
#include <iostream>
#include <fstream>
using namespace std;
class Something{
public:
void fill_array(char array_to_fill[]){
char next;
ifstream input;
input.open("chars.txt");
input.get(next);
while(!input.eof())
{
for(int i = 0; i < 6; i++)
{
array_to_fill[i] = next;
input.get(next);
}
}
}
};
int main()
{
Something* something = new Something[1];
char array_to_fill[5];
something->fill_array(array_to_fill);
//I'd like to be able to access the array here; for example - to cout the array.
return 0;
}
Apologies if a) my terminology is wrong b) my code is rubbish or c) my question is stupid/doesn't make sense. Also I should add I haven't learnt vectors yet and I'm not supposed to use them for the program I'm making. Any help would be much appreciated. Cheers!
Your class does not store the array at all. It is simply a holder for a method. You probably want something like this, where each instance of the class holds the array. (I changed it to std::string since they are nicer to work with.)
class Something
{
private:
std::string data;
public:
void fill_data( const std::string& filename )
{
ifstream file( filename );
file >> data;
file.close();
}
std::string get_data() const
{
return data;
}
}
int main()
{
std::vector<Something> my_things;
my_things.push_back( Something() );
my_things[0].fill_data( "chars.txt" );
cout << my_things[0].get_data() << std::endl;
my_things.push_back( Something() );
my_things[1].fill_data( "another_file.txt" );
cout << my_things[1].get_data() << std::endl;
}
Since you are using C++, not C, get used to writing C++ code instead of C. (std::vector instead of C arrays (for unknown length arrays), std::string instead of char*, etc).
I think your question is too general for the format of stack overflow, but what you want in this case is to either create a public member, or create a private member with setters and getters.
class Something
{
public:
std::string m_string;
}
int main()
{
Something A;
A.m_string = "toto";
cout << A.m_string;
return 0;
}
Put a string for convenience (you could use a const char* but you will have to understand what is the scope to know when it will not be accessible anymore and you are not quite there yet) and there may be typos since I typed this from a phone.
If you really want to access the chars themselves, pass a char* with a size_t for the length of the array or use std::array if possible.
Right now the method fill_array is creating a local copy of array_to_fill, so any changes that you make to array_to_fill only happen in the local method. To change this, pass by pointer. This way the pointer gets copied instead of the whole array object. I didn't test this but it should look more like this:
void fill_array(char* array_to_fill){
...
}
You don't need to change anything in the main method.
To actually access the elements you can use [] notation. I.e. cout << array_to_fill[0] in the main method.
Edit: I think that change should work.

Cpp some basic problems

My task was as follows :
Create class Person with char*name and int age. Implement contructor using dynamic allocation of memory for variables, destructor, function init and friend function show. Then transform this class to header and cpp file and implement in other program. Ok so here's my Person class :
#include <iostream>
using namespace std;
class Person {
char* name;
int age;
public:
Person(){
int size=0;
cout << "Give length of char*" << endl;
cin >> size;
name = new char[size];
age = 0;
}
Person::~Person(){
cout << "Destroying resources" << endl;
delete [] name;
delete take_age();
}
friend void show(Person &p);
int* take_age(){
return &age;
}
char* take_name(){
return name;
}
void init(char* n, int a) {
name = n;
age = a;
}
};
void show(Person *p){
cout << "Name: " << p->take_name() << "," << "age: " << p->take_age() << endl;
}
int main(void) {
Person *p = new Person;
p->init("Mary", 25);
show(p);
system("PAUSE");
return 0;
}
And now with header/implementation part :
- do I need to introduce constructor in header/implementation files ? If yes - how?
- my show() function is a friendly function. Should I take it into account somehow ?
I already failed to return this task on my exam, but still I'd like to know how to implement it.
Solve many of your issues, by switching from char * to std::string. You'll be glad you did.
The std::string class takes care of memory allocation, and deallocation as well as copying.
If this is homework, convince your professor to use std::string for beginners and save char * for the section on pointers. Also remind your professor that the C++ langauge is different than the C language. This is one of those areas.
You don't need a * when using delete or delete[]. Just supply a pointer variable to it eg.
delete[] name;
Also, your take_age member claims to return a int* but you actually return the int member itself. You need to take the address of the member using & if you want to do that. As #Jerry has commented this is not what you want to do here.
Although some on this site apparently think it is completely acceptable, good practice (see Can a constructor return a NULL value?), you should really refrain from doing things like stream operations within the constructor of your object. Do that stream reading outside and then call the function with the results.
That is, IMHO, the first step you should take.
In a typical case, managing a pointer and block of dynamically allocated memory (such as the name in this case) is enough responsibility for one class. As such, Thomas Matthews is right: you should really use string in this case. If you're going to handle it yourself, you should still split that responsibility off into a class of its own, and embed an object of that class into your Person object. If anything, std::string already tries to do too much; you'd be better off with something that does less, not more.
Your deletes should exact match with your allocations. In this case, the only allocation is:
name = new char[size];
so the only deletion should be:
delete [] name;
As far as friend functions go, you normally want the friend declaration inside the class definition, but the function definition outside the class definition:
class Person {
// ...
friend void show(Person const &);
// ...
};
void show(Person const &p) {
// ...
}
There are other possibilities, but that's the general idea. In particular, a friend is never a member function. What you had was a declaration of one (global) function named show and a definition of a completely separate member function -- that happened to have the same name, but wasn't really the same function at all.
That shows one other point: const-correctness. You were passing the parameter as a reference to Person. Unless it's going to modify the Person object (in which case, show() seems like a poor choice of name), it should probably take a reference to a const object. The same general idea applies to take_age() -- since it only retrieves a value, it should be a const function:
int take_age() const { return age; }
I've probably already tried to cover too much, so I'll shut up for the moment...
I think you should investigate the following pieces of your code (like, what's beneath them, what happens here, etc...)
int * take_age(); // You should return plain `int` here, I assume
~Person(){
cout << "Destroying resources" << endl;
delete *[] name; // Do you understand why did you put `*` here?
delete * take_age(); // Do you understand why did you write this? What behaviour you were trying to achieve?
And, actually, so on. Only when you're done with the basic stuff, I think, you can move on to header designing questions and friend functions.
First off, kudos on trying to find the right way to implement your class, particularly after having missed the answer already.
From your description at the top, I think you may have misunderstood some of what was being asked for this assignment. First, my interpretation would be that setting the value of the name and age should take place in the init() function rather than in the constructor. As mentioned by several other posters, your constructor should simply initialize your class to a known-good state. For example,
Person() {
name = NULL;
age = 0;
}
Then in your initialization function, you can assign the values. Looking at your original init() function, it should probably be mentioned that simply assigning a pointer value (char *) to another pointer (char *) only copies the value of the pointer, not the data that it represents. Thus, for the assignment of the name value you need to calculate the size of the buffer you need, allocate the buffer, and copy the data yourself. A basic init() function would probably look like
init(const char *n, int a) {
// Calculate the required name length plus a NULL-terminator
size_t nameLen = strlen(n) + 1;
// Free any previous value.
if (name != NULL) {
delete[] name;
}
// Allocate the name buffer and copy the name into it.
name = new char[nameLen];
memcpy(name, n, nameLen);
// Store the age.
age = a;
}
Finally, in your destructor you free any resources allocated by your class, in this case the name buffer.
~Person() {
if (name != NULL) {
delete[] name;
}
}
If you have a book or something associated with your class, you may want to review the information on pointers. They can be a bit tricky but are important to learn. I suspect that is why the problem specified using char * for strings rather than the STL string class.
To your question about placing information in header and source files, it is often considered good practice to create a header file that contains the class declaration and member function prototypes and then provide the implementation of your methods in a separate source file. For some simple functions, you can provide an implementation directly in your header file.
The key when providing class member definitions in a separate source file is to provide the class name to properly scope the function (i.e., Person::). So your header file may contain a class definition like
// Header file (e.g., person.h)
class Person {
private:
char *name;
int age;
public:
Person() { name = NULL; age = 0 };
~Person() { if (name != NULL) delete[] name; }
void init(const char *n, int a);
// Other method declarations and/or definitions
};
And then in your source file
// Source file (e.g., person.cpp)
void Person::init(const char *n, int a) {
// ...
}
// Rest of method definitions
Source files that use your person class need only include the header file with your class definition.
I think your problem is with this line:
friend void(Person &p);
What is it needed for.
do I need to introduce constructor in header/implementation files ?
The constructor can be in the .h or the .cpp file. It doesn't matter. Generally if the function is short it is ok to include it in the .h file. Anything longer should go in the .cpp.
my show() function is a friendly function.
Not sure what you mean by this. friend functions exist outside the class definition. Your show function is defined inside the class so does not need to be a friend.
in addition to the previously posted answers, i've got two points of advice for you:
don't use 'friend'. some here may disagree with me, but 'friend' should really not be part of C++ anymore as it goes against what OOP stands for.
naming your methods: avoid naming your methods like 'take_name' or 'take_age'. conventionally, since those are getters, consider naming them 'getName' and 'getAge'. you end up with much more respect from developers this way.