I have a Scanner class to tokenize streams of characters coming from files, cin, etc.
class Scanner {
public:
Scanner(std::istream&& c) : input{std::move(c)} {}
private:
std::istream input;
};
This code does not compile because the move constructor of std::istream is protected. I could use a reference to the stream instead, but then, I have no guarantee that someone is not playing with my stream outside of the Scanner class (ss below).
std::string code = "this is code";
std::stringstream ss{code};
Scanner scanner{ss};
Is there a neat way to address this issue, or do people just use std::istream& and hope for the best?
From what I can remember about my work with stream objects.
You need to think about this a bit differently.
class Scanner {
public:
Scanner() {}
virtual void scan(std::istream & p_stream);
};
Your Scanner class could just be focused on scanning the input for whatever, not actually containing it.
Now it can be focused purely on a single task.
Then you need to work out what is the best way for you to keep the stream object alive. I suggest a separate class for that task.
I would have written this as a comment, but there's too much here.
Related
For our application we have the following scenario:
Firstly, we get a large amount of data (on cases, this can be more than 100MB) through a 3rd party API into our class via a constructor, like:
class DataInputer
{
public:
DataInputer(int id, const std::string& data) : m_id(id), m_data(data) {}
int handle() { /* Do some stuff */ }
private:
std::string m_id;
std::string m_data;
};
The chain of invocation going into our class DataInputer looks like:
int dataInputHandler()
{
std::string inputStringFromThirdParty = GiveMeStringFrom3rdPartyMagic(); // <- 1.
int inputIntFromThirdParty = GiveMeIntFrom3rdPartyMagic();
return DataInputer(inputIntFromThirdParty, inputDataFromThirdParty).handle();
}
We have some control over how the dataInputHandler handles its string (Line marked with 1. is the place where the string is created as an actual object), but no control for what GiveMeStringFrom3rdPartyMagic actually uses to provide it (if it's important for anyone, this data is coming from somewhere via a network connection) so we need. As a consolation we have full control over the DataInputer class.
Now, what the application is supposedly doing is to hold on to the string and the associated integer ID till a later point when it can send to another component (via a different network connection) provided the component provides a valid ID (this is the short description). The problem is that we can't (don't want to) do it in the handle method of the DataInputer class, it would block it for an unknown amount of time.
As a rudimentary solution, we were thinking on creating an "in-memory" string store for all the various strings that will come in from all the various network clients, where the bottom line consists of a:
std::map<int, std::string> idStringStore;
where the int identifies the id of the string, the string is actually the data and DataInputer::handle does something like idStringStore.emplace(m_id, m_data);:
The problem is that unnecessarily copying a string which is on the size of 100s of megabytes can be a very time consuming process, so I would like to ask the community if they have any recommendations or best practices for scenarios like this.
An important mention: we are bound to C++11 for now :(
Use move-semantics to pass the 3rd-party data into your DataInputer constructor. The std::move here is redundant but makes the intention clear to the reader:
class DataInputer
{
public:
DataInputer(int id, std::string&& data) : m_id(id), m_data(std::move(data)) {}
int handle() { /* Do some stuff */ }
private:
std::string m_id;
std::string m_data;
};
And pass GiveMeStringFrom3rdPartyMagic() directly as an argument to the constructor without first copying into inputStringFromThirdParty.
int dataInputHandler()
{
int inputIntFromThirdParty = GiveMeIntFrom3rdPartyMagic();
return DataInputer(inputIntFromThirdParty, GiveMeStringFrom3rdPartyMagic()).handle();
}
Of course, you can use a std::map or any other STL container that supports move-semantics. The point is that move-semantics, generally, is what you're looking to use to avoid needless copies.
I have a function that sends stuff to std::ostream.
Something like this
void f(std::ostream& oss) {
oss << "Hello world!";
}
Now, I would like to create my own class that derives from std::ostream that would be parsing outgoing text, and changing it, so it would print "Ola world!", for example.
class StreamConverted : public std::ostream {
...
};
I believe (I am not too experienced with stream manipulation) that I would have to 'play' with the underlying rdbuf() of the stream, so I would have to substitute rdbuf of std::ostream with mine.
MyStreamBuf m_my_streambuf;
std::ostream& m_original_stream
std::streambuf* m_original_streambuf;
public:
StreamConverted(std::ostream& os)
: m_original_stream(os)
, m_original_streambuf(os.rdbuf(&m_my_streambuf))
{}
(Please, forgive me any obvious mistakes or typos. I am writing this all on the flight. I would also add destructor to restore original streambuf.)
This leads me to the need to write my MyStreamBuf that would derive from std::streambuf
class MyStreamBuf : public std::streambuf {
};
And here comes the moment when I need an advice.
Should I create my own buffer by calling std::streambuf::setp(begin, end) and then overwrite the overflow() method to parse the contents of the buffer when it is called, and then send the data out to the original streambuf after 'in some reasonable way' transforming the buffer?
I am not sure if I am going to far with modifying the buffer instead of doing something with ostream...
Any advice?
1 - Does it sound like a correct approach to use extraction operators to extract some data not from a file but from a big and complex class hierarchy ?
class ComplexInterface
{
//...
};
struct EXTRACTED_DATA
{
//...
friend ComplexInterface& operator>>(const ComplexInterface& interface, EXTRACTED_DATA& extracted)
{
extracted.m_member = interface.someAccessor().getData();
}
};
Would it be better to simply make it a member method (void extractFromComplexInterface(const ComplxInterface&)) ?
Is there a better solution ?
2 - Would it be possible to handle this extraction as a regular stream operation ? The ComplexInterface would become an istream, implementing some magic function in which it would say "I will send this data, then this and that...". Then, the EXTRACTED_DATA could use its regular extraction operator (friend istream& operator>>(istream&, EXTRACTED_DATA&)) which is already defined to read from a file stream.
If this is possible, how ?
I have the following User.h that holds several attributes (strings). User.cpp has all the definitions.
//User.h
#ifndef USER_H
#define USER_H
#include<iostream>
#include <cstring>
using namespace std;
class User{
string username;
public:
User();
string getUsername() const;
void setUsername(string);
};
#endif
I'm using a another class, "File" to insert new users/view users from a randomly accessed .dat file
//File.h
#ifndef FILE_H
#define FILE_H
#include "User.h"
class File{
public:
void loadUser();
bool addUser(User&);
};
#endif
File class definitions
//File.cpp
#include<cstring>
#include<iostream>
#include<iomanip>
#include<fstream>
#include "User.h"
#include "File.h"
using namespace std;
User tempUser;
fstream usersFile;
void File::loadUser(){
usersFile.open("users.dat", ios::in | ios::binary);
usersFile.seekg(0);
// commenting the following lines prevented the issue
usersFile.read(reinterpret_cast<char *>(&tempUser), sizeof(tempUser));
cout<<tempUser.getUsername().c_str();
usersFile.close();
}
bool File::addUser(User& user){
usersFile.open("users.dat", ios::out | ios::ate | ios::binary);
// no issue when writing to file
usersFile.write( reinterpret_cast<const char *>(&user), sizeof(user));
usersFile.close();
cout<<"User added";
}
I'm getting the above mentioned issue when running. No compilation issue though.
Is there any issue when dealing with objects that has "string attributes" inside, in handling?
Please help
I think the issue is that you are mixing C++ code with a C mindset.
What you should really be doing here is use the extraction operator, opeartor>>(), along with the C++ IO streams. This, as opposed to using the C standard IO along with the read() function. For writing an object, use the insertion operator operator<<() instead of the C write() function.
For working with strings use std::string. This class provides operator<<() and operator>>(). So, you can say std::string s and then io << s and io >> s where io is some C++ IO stream object. This will do The Right Thing(tm). The philosophy here is that the std::string class knows better than you, a user, how to serialize a std::string object. So let it do it, with the << and >> operators.
Going on with the idea, you, as the author of User, know better than anyone else how to serialize a User object. So provide the << and >> operators for users of your class, as a service. "Users of your class" might well be you one week from now, when you have completely forgot how to properly serialize a User object. (Or, you think you remember but in practice you forgot a detail, causing a bug in your code). Example:
// in User.h
#include <string>
#include <iosfwd> // forward declarations of standard IO streams
namespace mine {
class User {
User(const std::string& name) : username(name) { }
friend std::ostream& operator<<(std::ostream&, const User&);
friend std::istream& operator>>(std::istream&, User&);
private:
std::string username;
};
std::ostream& operator<<(std::ostream& out, const User& u)
{
return out << u.username;
}
std::istream& operator>>(std::istream& in, User& u)
{
return in >> u.username;
}
} // namespace mine
From here on, to save a user to a file you say
std::ofstream f("filename");
User u("John");
f << u;
That's it. To read a user:
std::ifstream f2("filename");
f2 >> u;
It's a good practice to wrap your code in a namespace. IDEs give a good visualization of this issue, by showing how many symbols are visible with the auto-complete feature. You get to see how much of a mess there is in the global scope. By wrapping your code in a namespace you group it under a scope name, saving some more mess in the global name scope. It's just about being tidy. If you put your code in your own namespace then you can choose any name you want for a function, a class or a variable, so long as you haven't chosen it before. If you don't put it in a namespace then you need to share names with others. It's like a skunk declaring his own territory, only without the bed smell.
On that note I suggest you take off that using namespace std from your header. This brings all the symbols in the std namespace into scope of all files that #include the header. It's a bad practice. Only say using namespace std in implementation files, if you wish, but not in header files.
Granted, some will say even this is a bad idea. I personally think it's fine if you're aware of the fact you might have name clashes in that particular implementation file. But at least you know where that using statement is: it's in your implementation file, and it only causes clashes in that implementation file. It's sort of a gun, (a plastic water gun, but a gun nonetheless), only you can only shoot (wet) your own feet and no one else's. Which in my opinion is perfectly fine.
You can't read non-POD types like that. A string is not a POD type.
What are POD types in C++?
There are a few ways to read in strings properly, depending upon how they were stored.
For text files:
If the string is just a single word, separated on both sides by whitespace, you can use the plain old >> operator. If it's more than one word, you can store it on it's own line, and use getline.
For binary files:
Store the string in null terminated form. Read it one character at a time, checking for the null character. Or, prepend the string with an integer storing it's size. When you read it, first read in the integer, then read in that many characters.
string is an object, which means you're not writing it's contents.
Try writing an User and examine the file to see what I mean. What you're then reading are some pointers, that point to invalid memory locations.
Yes, the std::string class is not plain old data, in other words it contains pointers.
If you save/load the string class in this manner, the data pointed to won't be loaded/saved, only the value of the pointer.
Additionally sizeof(tempUser) won't include the size of the text pointed to by the strings.
Your solution is to change how you're reading/writing data.
One way is to use boost::serialization which handles datatypes like std::string.
Another way would be to write each string to a separate line in the text document yourself (not in binary mode) and then use readline to read them back in.
Rather than construct your own custom serialization code, something like Google Protocol Buffers might do what you want with less effort. It's ideal for passing around simple structured data from place to place.
I have the following User.h that holds several attributes (strings). User.cpp has all the definitions.
//User.h
#ifndef USER_H
#define USER_H
#include<iostream>
#include <cstring>
using namespace std;
class User{
string username;
public:
User();
string getUsername() const;
void setUsername(string);
};
#endif
I'm using a another class, "File" to insert new users/view users from a randomly accessed .dat file
//File.h
#ifndef FILE_H
#define FILE_H
#include "User.h"
class File{
public:
void loadUser();
bool addUser(User&);
};
#endif
File class definitions
//File.cpp
#include<cstring>
#include<iostream>
#include<iomanip>
#include<fstream>
#include "User.h"
#include "File.h"
using namespace std;
User tempUser;
fstream usersFile;
void File::loadUser(){
usersFile.open("users.dat", ios::in | ios::binary);
usersFile.seekg(0);
// commenting the following lines prevented the issue
usersFile.read(reinterpret_cast<char *>(&tempUser), sizeof(tempUser));
cout<<tempUser.getUsername().c_str();
usersFile.close();
}
bool File::addUser(User& user){
usersFile.open("users.dat", ios::out | ios::ate | ios::binary);
// no issue when writing to file
usersFile.write( reinterpret_cast<const char *>(&user), sizeof(user));
usersFile.close();
cout<<"User added";
}
I'm getting the above mentioned issue when running. No compilation issue though.
Is there any issue when dealing with objects that has "string attributes" inside, in handling?
Please help
I think the issue is that you are mixing C++ code with a C mindset.
What you should really be doing here is use the extraction operator, opeartor>>(), along with the C++ IO streams. This, as opposed to using the C standard IO along with the read() function. For writing an object, use the insertion operator operator<<() instead of the C write() function.
For working with strings use std::string. This class provides operator<<() and operator>>(). So, you can say std::string s and then io << s and io >> s where io is some C++ IO stream object. This will do The Right Thing(tm). The philosophy here is that the std::string class knows better than you, a user, how to serialize a std::string object. So let it do it, with the << and >> operators.
Going on with the idea, you, as the author of User, know better than anyone else how to serialize a User object. So provide the << and >> operators for users of your class, as a service. "Users of your class" might well be you one week from now, when you have completely forgot how to properly serialize a User object. (Or, you think you remember but in practice you forgot a detail, causing a bug in your code). Example:
// in User.h
#include <string>
#include <iosfwd> // forward declarations of standard IO streams
namespace mine {
class User {
User(const std::string& name) : username(name) { }
friend std::ostream& operator<<(std::ostream&, const User&);
friend std::istream& operator>>(std::istream&, User&);
private:
std::string username;
};
std::ostream& operator<<(std::ostream& out, const User& u)
{
return out << u.username;
}
std::istream& operator>>(std::istream& in, User& u)
{
return in >> u.username;
}
} // namespace mine
From here on, to save a user to a file you say
std::ofstream f("filename");
User u("John");
f << u;
That's it. To read a user:
std::ifstream f2("filename");
f2 >> u;
It's a good practice to wrap your code in a namespace. IDEs give a good visualization of this issue, by showing how many symbols are visible with the auto-complete feature. You get to see how much of a mess there is in the global scope. By wrapping your code in a namespace you group it under a scope name, saving some more mess in the global name scope. It's just about being tidy. If you put your code in your own namespace then you can choose any name you want for a function, a class or a variable, so long as you haven't chosen it before. If you don't put it in a namespace then you need to share names with others. It's like a skunk declaring his own territory, only without the bed smell.
On that note I suggest you take off that using namespace std from your header. This brings all the symbols in the std namespace into scope of all files that #include the header. It's a bad practice. Only say using namespace std in implementation files, if you wish, but not in header files.
Granted, some will say even this is a bad idea. I personally think it's fine if you're aware of the fact you might have name clashes in that particular implementation file. But at least you know where that using statement is: it's in your implementation file, and it only causes clashes in that implementation file. It's sort of a gun, (a plastic water gun, but a gun nonetheless), only you can only shoot (wet) your own feet and no one else's. Which in my opinion is perfectly fine.
You can't read non-POD types like that. A string is not a POD type.
What are POD types in C++?
There are a few ways to read in strings properly, depending upon how they were stored.
For text files:
If the string is just a single word, separated on both sides by whitespace, you can use the plain old >> operator. If it's more than one word, you can store it on it's own line, and use getline.
For binary files:
Store the string in null terminated form. Read it one character at a time, checking for the null character. Or, prepend the string with an integer storing it's size. When you read it, first read in the integer, then read in that many characters.
string is an object, which means you're not writing it's contents.
Try writing an User and examine the file to see what I mean. What you're then reading are some pointers, that point to invalid memory locations.
Yes, the std::string class is not plain old data, in other words it contains pointers.
If you save/load the string class in this manner, the data pointed to won't be loaded/saved, only the value of the pointer.
Additionally sizeof(tempUser) won't include the size of the text pointed to by the strings.
Your solution is to change how you're reading/writing data.
One way is to use boost::serialization which handles datatypes like std::string.
Another way would be to write each string to a separate line in the text document yourself (not in binary mode) and then use readline to read them back in.
Rather than construct your own custom serialization code, something like Google Protocol Buffers might do what you want with less effort. It's ideal for passing around simple structured data from place to place.