I am a little stuck on this area. By using BookList.insert(book, Position::BOTTOM) I would get cquery saying this error on the BookList:
cannot use dot operator on a type
I can't think of another way to insert into booklist than this.
Could this be from the if statement? I intention it to do "if (stream works) {insert into booklist}".
Here is the code, and at the bottom there is a link to a picture of the code.
std::istream& operator>>(std::istream& stream, BookList& book_list) {
if (!book_list.containers_are_consistent()) {
throw BookList::InvalidInternalStateException(
"Container consistency error in operator>>");
}
std::string label_holder;
size_t count;
// Read in data from a stream and use it to fill in the data of a BookList
// object. Create a function that will read the output created by the
// ostream operator above into an object properly.
stream >> count;
for (int i = 0; i < count; i++) {
stream.ignore(100, ':');
}
if (stream) {
BookList.insert(book, Position::BOTTOM);
}
return stream;
}
I'm assuming that insert() is a member function of the BookList class. If this assumption is correct, when using the . notation, it must be called on an instance of BookList rather than on the class name itself. So, replace this:
BookList.insert(book, Position::BOTTOM);
with this
book_list.insert(book, Position::BOTTOM);
Related
I'm trying to take input from the user and the user have multiple choice to enter the different types of inputs(char, int float). And according to the value entered I have to take proper action.
eg. I have a function below :-
int* function(int data)
{
int a[50];
int k = 0;
a[k] = data;
k++;
// I want to make choice generalized so that it can accept both type of
// values int as well as char.
cout<<"\n Enter integer element to insert into array, otherwise press 'n' to terminate array list: ";
cin>>choice;
if(choice != 'n')
function(choice);
return a;
}
So, in the above example I want to make choice generalized. I how to use template for function and classes but, I want to do this for variable. Please help.
NOTE: the above code is just a example to illustrate my problem.
Thank's.
Templates are a compile time construct, so there is no way to let the user input have an influence on the template type.
What you can do is some template based automation of the input conversion where you decide which template instantiations should be checked. Suppose you have a class to handle your input conversion called GenericInput with template functions bool GenericInput::CanConvert<TargetType>() and TargetType GenericInput::Convert<TargetType>()
GenericInput in;
std::cin >> in;
if (in.CanConvert<int>()) {
// some action
}
else if (in.CanConvert<char>()) {
// another action
}
// ...
It would basically be a wrapper for the idea to first read the string and then check, how the string can be interpreted.
To implement it, you would need the following things:
Overload operator >>
std::istream& operator >>(std::istream& stream, GenericInput& element) {
/* TODO: read input into string member of GenericInput object */
return stream;
}
The GenericInput class
class GenericInput {
private:
std::string _inputElement; // store input as base for conversion
public:
// TODO: standard class implementation
template <typename TargetType>
bool CanConvert() {
// TODO: create std::stringstream from _inputElement and try to read into a TargetType variable
// return true if the stringstream is valid after the read
}
template <typename TargetType>
TargetType Convert() {
// TODO: create std::stringstream from _inputElement and try to read into a TargetType variable
// return variable if the stringstream is valid after the read, otherwise report error
}
}
Regarding the "valid" stringstream after read, it might be important to check two things: if the stringstream is in some kind of error state, conversion failed. If there are unread characters in the stringstream, the conversion was incomplete, which may also count as a failure.
Please don't expect this to be working out of the box, its more of an idea scratch than an actual implementation.
I have a class called Game which contains the following:
vector<shared_ptr<A>> attr; // attributes
D diff; // differences
vector<shared_ptr<C>> change; // change
My question is, how can I write these (save) to a file and read/load it up later?
I thought about using a struct with these in it, and simply saving the struct but I have no idea where to start.
This is my attempt so far, with just trying to save change. I've read up a lot on the issue and my issue (well one of them, anyway) here seems to be that I am storing pointers which after closing the program would be invalid (compounded by the fact that I also free them before exiting).
/* Saves state to file */
void Game::saveGame(string toFile) {
ofstream ofs(toFile, ios::binary);
ofs.write((char *)&this->change, sizeof(C));
/* Free memory code here */
....
exit(0);
};
/* Loads game state from file */
void Game::loadGame(string fromFile) {
ifstream ifs(fromFile, ios::binary);
ifs.read((char *)&this->change, sizeof(C));
this->change.toString(); // display load results
};
Can anyone guide me in the right direction for serializing this data? I'd like to use only standard packages, so no boost.
Thanks.
I have no idea how is implemented classes A, C or D, but that is the first question: how to serialize an object of that class. For the C case, you need to implement something like this:
std::ostream& operator <<(std::ostream& os, const C& c) {
// ... code to serialize c to an output stream
return os;
}
std::istream& operator >>(std::istream& is, C& c) {
// ... code to populate c contents from the input stream
return is;
}
or, if you prefer, create a write() and read() function for that class.
Well, if you want to serialize a vector<shared_ptr<C>> looks obvious you don't want to serialize the pointer, but the contents. So you need to dereference each of those pointers and serialize. If the size of the vector is not known before loading it (i.e., is not always the same), you'll need to store that information. Then, you can create a pair of functions to serialize the complete vector:
std::ostream& operator <<(std::ostream& os, const std::vector<std::shared_ptr<C>>& vc) {
// serialize the size of the vector using << operator
// for each element of the vector, let it be called 'pc'
os << *pc << std::endl; // store the element pointed by the pointer, not the pointer.
return os;
}
std::istream& operator >>(std::istream& is, std::vector<std::shared_ptr<C>>& c) {
// read the size of the vector using >> operator
// set the size of the vector
// for each i < sizeo of the vector, let 'auto &pc = vc[i]' be a reference to the i-th element of the vector
C c; // temporary object
is >> c; // read the object stored in the stream
pc = std::make_shared<C>(c); // construct the shared pointer, assuming the class C has copy constructor
return is;
}
And then,
/* Saves state to file */
void Game::saveGame(string toFile) {
ofstream ofs(toFile);
ofs << change;
....
};
/* Loads game state from file */
void Game::loadGame(string fromFile) {
ifstream ifs(fromFile);
ifs >> change;
};
I know there are a lot of things you still need to resolve. I suggest you to investigate to resolve them so you understand well how to solve your problem.
Not only are you saving pointers, you're trying to save a shared_ptr but using the wrong size.
You need to write serialization functions for all your classes, taking care to never just write the raw bits of a non-POD type. It's safest to always implement member-by-member serialization for everything, because you never know what the future will bring.
Then handling collections of them is just a matter of also storing how many there are.
Example for the Cs:
void Game::save(ofstream& stream, const C& data)
{
// Save data as appropriate...
}
void Game::saveGame(string toFile) {
ofstream ofs(toFile, ios::binary);
ofs.write((char *)change.size(), sizeof(change.size());
for (vector<shared_ptr<C>>::const_iterator c = change.begin(); c != change.end(); ++c)
{
save(ofs, **c);
}
};
shared_ptr<C> Game::loadC(ofstream& stream)
{
shared_ptr<C> data(new C);
// load the object...
return data;
}
void Game::loadGame(string fromFile) {
change.clear();
size_t count = 0;
ifstream ifs(fromFile, ios::binary);
ifs.read((char *)&count, sizeof(count));
change.reserve(count);
for (int i = 0; i < count; ++i)
{
change.push_back(loadC(ifs));
}
};
All the error handling is missing of course - you would need to add that.
It's actually a good idea to at least start with text storage (using << and >>) instead of binary. It's easier to find bugs, or mess around with the saved state, when you can just edit it in a text editor.
Writing your own serialization is quite a challenge. Even if you do not use boost serializatoin I would recommend you learn how to use it and comprehend how it works rather than discovering it yourself.
When serializing you finally end up with a buffer of data of which content you have very vague idea. You have to save everything you need to be able to restore it. You read it chunk by chunk. Example (not compiled, not tested and not stylish ):
void save(ostream& out, const string& s)
{
out << s.size();
out.write(s.c_str(), s.size());
}
void load(istream& in, string& s)
{
unsigned len;
in >> len;
s.resize(len);
in.read((char*)s, len);
}
struct Game
{
void save(ostream& out)
{
player.save(out);
};
void load(istream& in)
{
player.load(in);
}
};
struct Player
{
void save(ostream& out)
{
// save in the same order as loading, serializing everything you need to read it back
save(out, name);
save(out, experience);
}
void load(istream& in)
{
load(in, name);
load(in, experience); //
}
};
I do not know why you would do it to yourself instead of using boost but those are some of the cases you should consider:
- type - you must figure out a way to know what "type of change" you actually have there.
- a string (vector, whatever) - size + data (then the first thing you read back from the string is the length, you resize it and copy the "length" number of characters)
- a pointer - save the data pointed by pointer, then upon deserialization you have to allocate it, construct it (usually default construct) and read back the data and reset the members to their respective values. Note: you have to avoid memory leakage.
- polymorphic pointer - ouch you have to know what type the pointer actually points to, you have to construct the derived type, save the values of the derived type... so you have to save type information
- null pointer... you have to distinguish null pointer so you know that you do not need to further read data from the stream.
- versioning - you have to be able to read a data after you added/removed a field
There is too much of it for you to get a complete answer.
Can some one explain me this code
there is a class StringStream . What i don't get is StringStream& write(char*).
and if in cpp file there is
StringStream& StringStream::write(char* text)
{
//what values can i return??
//can i return address of character text is currently pointing to?
}
You'd return *this - i.e. a reference to the current object. (Well, you can return any non-local StringStream, but I guess the purpose is the one I stated)
This technique is usually used for method chaining - i.e. doing something like:
StringStream ss;
ss.write("Hello ").write("world!");
This is a method that most likely modifies a StringStream instance, and returns a reference to a StringStream. So you should return a reference to the instance itself
StringStream& StringStream::write(char* text)
{
// do stuff
return *this;
}
This allows you to perform chaining:
StringStream s;
s.write("foo").write("bar");
That said, I would have expected the write method to take a const char*:
StringStream& write(const char* text);
since the method will presumably not modify the data passed to it, and is required in order to be able to correctly pass string literals such as the "foo" and "bar" in the example.
you can simply return a reference to stringStream class. As you are writing a member function of the same class you can simply return pointer to this. For more info about the StringStream class : click here
I need to implement a mechanism where I can initialize a vector of my custom class using a text source, where each line of the source is representing one instance of my class. To achieve this, I implemented the operator >> for my class and stringstream. When I read the source, I go line-by-line and get a substream of my original source, then parse the substream each time. This has three benefits for me. First, this way I can make sure that one line of the text source would represent exactly one instance of my class. Second, as the rest of the line after parsing is ignored, I can safely add any comment in any line of my text source, which would surely get ignored by the parser. And third, I don't need to mention the length of the vector in my original source, since the first time I get a parsing error (I check the fail and bad bits of the stream to confirm this) I know that the vector declaration is over.
To parse line-by-line, I'm using the following code:
std::stringstream fullStream;
std::stringstream lineStream;
std::string str;
bool isValid;
myClass newInstance;
std::vector < myClass > result;
// Fill fullStream from external source (codepart omitted)
isValid = true;
while ( isValid && ! fullStream.eof ( ) ) {
std::getline ( fullStream, str );
lineStream.clear ( );
lineStream.str ( str );
lineStream >> newInstance;
isValid = ! lineStream.fail ( );
if ( isValid ) {
result.push_back ( newInstance );
}
}
Although this code works fine, I'm wondering if there was a better way to achieve the same result. Specially, if there was a more efficient way to extract a line from fullStream to lineStream.
Thanks,
Ádám
First, if the code works, it is really only by chance. The idiomatic
way of handling this is:
std::string line;
while ( std::getline( fullStream, line ) ) {
std::istringstream lineStream( line );
lineStream >> newInstance;
if ( lineStream ) {
result.push_back( newInstance );
} else {
fullStream.setstate( std::ios_base::failbit );
}
}
Checking eof() before a read is rarely useful, and not checking the
results of your getline before using it is almost certainly an error.
Trying to reuse a stringstream is more complex and error prone than
simply creating a new one; there is all sorts of state which may or may
not have to be reset. Streams have a mechanism for memorizing error
state, so you probably want to use this. (If you want to continue using
fullStream for other things after the error, the problem is more
complex, because you've already extracted the line which failed, and you
can't put it back.) And if you're only reading, you should use
std::istringstream, and not std::stringstream (which has a lot of
extra baggage); in general, it's very, very rare to use a bi-directional
stream.
One obvious alternative would be to have your operator>> do line-by-line reading itself, so you don't have to do that externally:
class MyClass {
// some sort of data to demonstrate the idea:
int x;
std::string y;
friend std::istream &operator>>(std::istream &is, MyClass &m) {
std::string temp;
std::getline(is, temp);
std::istringstream buffer(temp);
buffer >> m.x >> m.y;
return is;
}
};
With that, code to read data from a file becomes a little more straightforward:
std::copy(std::istream_iterator<MyClass>(fullStream),
std::istream_iterator<MyClass>(),
std::back_inserter(result));
Edit: if you don't want to build the line-oriented reading directly into the operator>> for MyClass, another possibility is to use a proxy class:
class LineReader {
MyClass object;
public:
operator MyClass() { return object; }
friend std::istream &operator>>(std::istream &is, LineReader &d) {
std::string line;
std::getline(is, line);
std::istringstream buffer(line);
buffer >> d; // delegate to the object's own stream-oriented reader.
}
};
Then when you want to do line-oriented reading, you read objects of the proxy class, but store objects of the original class:
std::vector<MyClass>((std::istream_iterator<LineReader>(some_stream)),
std::istream_iterator<LineReader>());
But, when/if you want to read a stream of objects instead of lines of objects, you use the object's own operator>> directly:
std::vector<MyClass>((std::istream_iterator<MyClass>(stream),
std::istream_iterator<MyClass>());
My problem is how to use correctly the function infile.open().
I have a class that, among the others, has the following public properties:
class myclass {
public:
int rows
int columns
const char* array_file
}
All values are given at run-time.
When I call the function that uses a member of the class I have (pt is a pointer to a member of the class)
#include <vector>
#include <fstream>
#include <iostream>
typedef std::vector< std::vector<int> > Matrixint;
void function(myclass* pt) {
Matrixint array_name(pt->rows, std::vector<int>(pt->columns));
std::ifstream infile;
infile.open("%s", pt->array_file); // my problem: is this correct?
for (int a = 0; a < pt->rows; a++) {
for (int b = 0; b < pt->columns; b++) {
infile >> array_name[a][b] ;
}
}
infile.close();
}
Is this way of opening/reading the file correct?
The data in the file will be formatted as in this question (please note: only the array will be present in the file, no other data)
infile.open gets as its first parameter the filename:
void open ( const char * filename, ios_base::openmode mode = ios_base::in );
(source)
I don't know what your filename is but maybe something like this (just a guess based on variable types) could do:
infile.open(pt->array_file);
of course you have to ensure that the filename you pass in is correct at the time of calling that function.
Assuming that my psychic abilities for fixing your question's code were right, I'd write it like this:
struct mine {
int rows
int columns
std::string array_file
}
void function(const mine& m) {
Matrixint array_name(pt->rows, std::vector<int>(pt->columns));
std::ifstream infile(m.array_file.c_str());
for (int a = 0; a < ls->rows && infile.good(); ++a) {
for (int b = 0; b < ls->columns && infile.good(); ++b) {
infile >> array_name[a][b] ;
}
}
if(!infile)
throw "Uh oh!"; // assume real error handling here
}
Why have I changed all these things?
A class with all public data isn't a class, but an aggregation of data. I'd use a struct for that, to not to confuse those who later need to maintain my code. (That might include me a few years down the road, which is a strong incentive to be very helpful.)
Unless you know exactly what you're doing (which doesn't seem to be the case), you should be using std::string rather than C-style strings.
Why pass the function parameter by pointer, when you can use a const reference?
std::ifstream has a constructor with which you can open a file immediately. I rarely ever use (or see used) its open() member function.
You need to test whether the file was opened and whether the input operations succeeded. (In this case, I merged the test whether it could be opened with the input operation succeed test, since the for loop is a pre-testing loop.) After the operations, I check whether an error occurred. Alternatively you could break out of the loop with an exception when reading fails.
Usually there is no need to close a stream, since its destructor already does this. If you make your variables as local as possible (which is good programming praxis), the file will be closed right away nevertheless.