I'm writing an application in which I need serialization to store some data in files. For serialization I want to use the QDataStream class.
I can't compile my code due to this compiler error:
Error 1 error C2679: binary '<<' : no operator found which takes a
right-hand operand of type 'const CListItem' (or there is no
acceptable conversion) c:\qt\4.8.6\src\corelib\io\qdatastream.h 265
See the relevant code below. Anyone knows what's going on here?
There is a similar question which didn't help me. When I follow the steps described there, I get this (similar) error:
Error 1 error C2678: binary '<<' : no operator found which takes a
left-hand operand of type 'QDataStream' (or there is no acceptable
conversion) c:\qt\4.8.6\src\corelib\io\qdatastream.h 265
This is my problem:
What I want to serialize is a class called CMPProject.
CMPProject holds
CListModel* m_pData;
QDateTime m_dateTimeCreated;
This is basically what should be serialized.
CMPProject has operators to stream its contents into a QDataStream.
MPProject.h:
#ifndef _MPPROJECT_
#define _MPPROJECT_
#include <QtCore/QString>
#include <QtCore/QFile>
#include <QtCore/QDateTime>
#include "ListModel.h"
class CMPProject
{
public:
// ...
friend QDataStream& operator <<(QDataStream& stream, const CMPProject& project);
friend QDataStream& operator >>(QDataStream& stream, CMPProject& project);
private:
static const quint32 m_streamHeader = 0x1329453;
QFile* m_pFile;
CListModel* m_pData;
QDateTime m_dateTimeCreated;
};
#endif // _MPPROJECT_
The data stream operators in MPProject.cpp:
QDataStream& operator <<(QDataStream& stream, const CMPProject& project)
{
return stream << project.m_dateTimeCreated << *(project.m_pData);
}
QDataStream& operator >>(QDataStream& stream, CMPProject& project)
{
return stream >> project.m_dateTimeCreated >> *(project.m_pData);
}
m_pData is of type CListModel. CListModel contains the actual data stored as a QList<CListItem>.
To serialize the CListModel I added the according operators in ListModel.h:
#ifndef _LISTMODEL_
#define _LISTMODEL_
#include <QtCore/QAbstractListModel>
#include <QtCore/QList>
#include <QtCore/QStringList>
#include "ListItem.h"
typedef QMap<unsigned int, QString> TValueMap;
class CListModel : public QAbstractListModel
{
public:
// ...
template<typename T>
friend void operator <<(QVariant& data, const QList<T>& target);
template<typename T>
friend void operator >>(const QVariant& data, QList<T>& target);
friend QDataStream& operator <<(QDataStream& stream, const CListModel& listModel);
friend QDataStream& operator >>(QDataStream& stream, CListModel& listModel);
private:
QList<CListItem> m_items;
};
#endif // !_LISTMODEL_
and ListModel.cpp:
template<typename T>
void operator <<(QVariant& data, const QList<T>& target)
{
QVariantList list;
list.reserve(target.count());
for (int i = 0; i < target.count(); i++) {
QVariant item;
item << target[i];
list.append(item);
}
data = list;
}
template<typename T>
void operator >>(const QVariant& data, QList<T>& target)
{
QVariantList list = data.toList();
target.reserve(list.count());
for (int i = 0; i < list.count(); i++) {
T item;
list[i] >> item;
target.append(item);
}
}
QDataStream& operator <<(QDataStream& stream, const CListModel& listModel)
{
// ERROR C2679 does not occur when I change this line to "return stream;"
return stream << listModel.m_items;
}
QDataStream& operator >>(QDataStream& stream, CListModel& listModel)
{
return stream >> listModel.m_items;
}
In order to serialize the contents of listModel.m_items (objects of type CListItem) I implemented the according operators in ListItem.h:
#ifndef _LISTITEM_
#define _LISTITEM_
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QDateTime>
#include <QtCore/QVariantMap>
class CListItem
{
public:
// ...
friend void operator <<(QVariant& data, const CListItem& target);
friend void operator >>(const QVariant& data, CListItem& target);
private:
QString m_name;
QString m_domain;
QString m_login;
QString m_password;
QDateTime m_LastModified;
};
#endif // !_LISTITEM_
and ListItem.cpp:
template<typename T>
void operator <<(QVariant& data, const T& target)
{
data = QVariant::fromValue<T>(target);
}
template<typename T>
void operator >>(const QVariant& data, T& target)
{
target = data.value<T>();
}
void operator <<(QVariant& data, const CListItem& target)
{
QVariantMap map;
map["name"] << target.m_name;
map["domain"] << target.m_domain;
map["login"] << target.m_login;
map["password"] << target.m_password;
map["dateModified"] << target.m_LastModified;
data << map;
}
void operator >>(const QVariant& data, CListItem& target)
{
QVariantMap map;
data >> map;
map["name"] >> target.m_name;
map["domain"] >> target.m_domain;
map["login"] >> target.m_login;
map["password"] >> target.m_password;
map["dateModified"] >> target.m_LastModified;
}
I fixed it.
The problem was that I didn't have operators to put my QList into a QDataStream. But I defined wrappers to store my list in QVariant, so (of course) I just should have used those.
QDataStream& operator <<(QDataStream& stream, const CListModel& listModel)
{
QVariant var;
var << listModel.m_items;
return stream << var;
}
QDataStream& operator >>(QDataStream& stream, CListModel& listModel)
{
QVariant var;
stream >> var;
var >> listModel.m_items;
return stream;
}
It looks like here is no operator<< overloaded for arguments: QDataStream & and QList<CListItem> const&. In the reference here and here, there aren't any documented.
The second error addresses this, but there also should be QList<CListItem> argument mentioned. Was that the full error message?
Basically, you should be using this:
QDataStream& operator <<(QDataStream& stream, const CListModel& listModel)
{
for(auto const& item : listModel.m_items)
stream << item; // write elements one by one
return stream;
}
This template might serve you for multiple types (or something akin to Pretty-print C++ STL containers):
template <typename Stream, typename Containter>
Stream& operator <<(Stream& stream, const Containter& container)
{
for(auto const& item : container)
stream << item;
return stream;
}
Your code would be valid without making change to operator<< you defined. Hope that helps.
Related
I am working with Qt 5.10 and I have to subclass QDatastream and I overload the operator << with an other class, like this :
class myDataStream:public QDataStream
{
public :
myDataStream(QIODevice* device):QDataStream(device)
{}
};
class data
{
public:
data(double v):data_(v) {}
double getData() const {return data_;}
void record(myDataStream& stream) const;
private:
double data_;
};
void data::record(myDataStream &stream) const
{
stream<<getData();
}
myDataStream &operator<<(myDataStream &stream, const data &d )
{
stream<<d.getData(); //<------ Error here
return stream;
}
I have this error :
> error: use of overloaded operator '<<' is ambiguous (with operand types 'myDataStream' and 'double')
When I remove the const operator behind data like this :
myDataStream &operator<<(myDataStream &stream, data &d )
{
stream<<d.getData();
return stream;
}
I don't have error. The operator<< doesn't change the class data ... doesn' it ? the getData() method is const.
I don't understand.
Someone to help me ?
Finaly, I will follow the KubaOber advices (in comments of my questions) and make QDataStream a composition of my class instead of subclassing QDataStream.
Thanks to KubaOber for its advices !
I am trying to read and display the contents of a file named myDocument.txt, but I always get the following error with my header file.
./ReadDocument.h:22:24: error: expected parameter declarator std::ifstream myflux("myDocument.txt");
What's going on?
Here is my ReadDocument.h:
class ReadDocument{
public:
ReadDocument(); //empty constructor
ReadDocument(const std::string& fileName); //constructor
void documentContent();
friend std::ostream& operator <<(std::ostream& os, const ReadDocument& d);
std::list<std::string> myList;
std::ifstream myflux("myDocument.txt");
if(myflux){
//Getting the words from the file string by string
std::string data;
while(myflux>>data){
myList.push_back(data);
}
myflux.close();
}
else{
std::string message = "\t\tERROR ==> This file doesn't exist.";
throw FileDoesntExistException(message);
}
};
Here is my ReadDocument.cpp:
#include "ReadDocument.h"
ReadDocument::ReadDocument(){}
void ReadDocument::documentContent(){}
std::ostream& operator <<(std::ostream& os, const ReadDocument& d){
for(std::list <std::string> :: const_iterator it = ReadDocument::myList.begin(); it!=ReadDocument::myList.end(); it++)
std::cout<<*it<<std::endl;
return os;
}
And this is my UseReadDocument.cpp
#include "ReadDocument.h"
int main(){
ReadDocument rd("test.txt");
std::cout<<rd<<std::endl;
return 0;
}
I tried renamed the file and still the same problem.
I put it in the class, and doesn't change anything.
I always have the same error coming out.
and yes it is my entire code
The part of your class after myflux is not in a method.
Did you want to put the code in the function documentContent, like this?
ReadDocument.h:
class ReadDocument{
public:
ReadDocument(); //empty constructor
ReadDocument(const std::string& fileName); //constructor
void documentContent();
friend std::ostream& operator <<(std::ostream& os, const ReadDocument& d);
std::list<std::string> myList;
std::ifstream myflux("myDocument.txt");
};
ReadDocument.cpp:
#include "ReadDocument.h"
ReadDocument::ReadDocument(){}
void ReadDocument::documentContent(){
if(myflux){
//Getting the words from the file string by string
std::string data;
while(myflux>>data){
myList.push_back(data);
}
myflux.close();
}
else{
std::string message = "\t\tERROR ==> This file doesn't exist.";
throw FileDoesntExistException(message);
}
}
std::ostream& operator <<(std::ostream& os, const ReadDocument& d){
for(std::list <std::string> :: const_iterator it = ReadDocument::myList.begin(); it!=ReadDocument::myList.end(); it++)
std::cout<<*it<<std::endl;
return os;
}
By the way, you should avoid creating an empty constructor and filling it with nothing.
Since C++11 you should write ReadDocument() = default; in your header and remove ReadDocument::ReadDocument(){} from the cpp.
With your class your are declaring two public variables and trying to give each instance of your class these default characteristics. The solution in to put the default conditions in the default constructor as follows:
class ReadDocument{
private:
std::list<std::string> myList; //moved member variables to private
std::ifstream myflux; //part of class
public:
ReadDocument(); //empty constructor
ReadDocument(const std::string& fileName); //constructor
void documentContent();
friend std::ostream& operator <<(std::ostream& os, const ReadDocument& d);
};
Implementation:
#include "ReadDocument.h"
ReadDocument::ReadDocument()
{
myflux.open("myDocument.txt");
if(myflux){
//Getting the words from the file string by string
std::string data;
while(myflux>>data){
myList.push_back(data);
}
myflux.close();
}
else{
std::string message = "\t\tERROR ==> This file doesn't exist.";
throw FileDoesntExistException(message);
}
}
void ReadDocument::documentContent(){}
std::ostream& operator <<(std::ostream& os, const ReadDocument& d){
for(std::list <std::string> :: const_iterator it =
d.myList.begin(); it!=d.myList.end(); it++)
std::cout<<*it<<std::endl;
return os;
}
Code will now associate myflux with "myDocument.txt" and extract the file data into myList by default (unless instantiated with string parameter for use with const string& constructor) for each instance of the ReadDocument class. As well as a few other fixes in the ostream function.
P.S* Assuming this is your entire .cpp file associated with the ReadDocument class, you forgot to implement the constructor containing the const string& parameter, ReadDocument(const std::string& fileName).
If I take out the line where I am outputting the contents of data[], it compiles fine. I seem to be having trouble understanding accessing the array through the implementation file. Any help would be apprecciated.
implementation file function:
ostream& operator << (ostream& output, const Bag& b1)
{
for(int i = 0; i< b1.used; i++)
{
output <<Bag.data[i]<<" "; // LINE OF ERROR
}
output<<endl;
return output;
}
header file:
#ifndef BAG_H
#define BAG_H
#include <cstdlib>
#include <fstream>
namespace greg_bag{
using namespace std;
class Bag
{
public:
typedef int value_type;
typedef std:: size_t size_type;
static const size_type CAPACITY = 30;
Bag(){used = 0;}
void erase();
bool erase_one(const value_type& target);
void insert (const value_type& entry);
//void operator += (const bag& addend);
//size_type size()const {return used;}
//size_type count(const value_type& target) const;
//bag operator +(const bag& b1, const bag& b2);
friend ostream& operator << (ostream&, const Bag&);
private:
value_type data[CAPACITY];
size_type used;
};
}
#endif
error message:
error: expected primary-expression before '.' token|
data is not a static variable so that you can use it directly with class name.
data is different for each instance of a class. Use the following code :
ostream& operator << (ostream& output, const Bag& b1)
{
for(int i = 0; i< b1.used; i++)
{
output <<b1.data[i]<<" "; // LINE OF ERROR
}
output<<endl;
return output;
}
I have a very simple inline helper class called IpAddress, which has 2 overloads of operator << to either serialize an object to a set of custom binary streams (operator as template function) or to output it to a std::ostream (non-template operator function).
#include <cstdint>
#include <string>
#include <array>
#include <iostream>
#include <sstream>
typedef uint8_t byte;
class IpAddress: public std::array<byte,4>
{
// ...
template<class S>
inline friend S& operator<<(S& left, const IpAddress& right) {
left.setBytes(right.data(), 4);
return left;
}
inline friend std::ostream& operator<< (std::ostream& left, const IpAddress& right) {
// do stuff eligible for an ostream
return left;
}
inline operator std::string() {
std::stringstream stream;
stream << *this;
return stream.str();
}
}
As you can see, there is also an operator to convert the address to a string. Unfortunately, the << call inside is selecting the wrong operator<< (the template one), leading to a compiler error:
error: C2039: 'setBytes' : is not a member of 'std::basic_stringstream<char,std::char_traits<char>,std::allocator<char>>'
Why is the compiler (MSVC in this case) selecting the wrong overload with a non-working body? Why not the "more specialized" std::ostream operator? How do I change this behaviour? Thanks!
You don't post a complete, compilable example so I can't see the call site.
However, the problem is most likely because whatever you are streaming out to is not exactly an ostream. It's likely to be derived from an ostream. In which case, template T will be a better match.
EDIT:
Style comments aside (they are right and you would do well to listen), here is a fix:
#include <iostream>
#include <utility>
#include <cstdint>
#include <array>
#include <sstream>
template<class T> static constexpr bool IsAnOstream = std::is_base_of<std::decay_t<T>, std::ostream>::value;
using byte = std::uint8_t;
struct IpAddress: public std::array<byte,4>
{
// ...
template<class S, std::enable_if_t<not IsAnOstream<S>>* = nullptr>
friend S& operator<<(S& left, const IpAddress& right) {
left.setBytes(right.data(), 4);
return left;
}
friend std::ostream& operator<< (std::ostream& left, const IpAddress& right) {
// do stuff eligible for an ostream
return left;
}
/*
* this is a bad idea - it can lead to all kinds of confusion
*
inline operator std::string() {
std::stringstream stream;
stream << *this;
return stream.str();
}
*/
};
// this is better - it's less surprising.
std::string to_string(const IpAddress& r)
{
std::stringstream stream;
stream << *this;
return stream.str();
}
struct MyStream
{
void setBytes(const uint8_t* p, size_t len) {}
};
int main()
{
IpAddress a;
MyStream s;
std::cout << a;
s << a;
return 0;
}
I am trying to read/write my custom classes using QDataStream. I have overridden the << and >> operators, which seem to work fine for normal objects. However, when I try to pass a pointer to my custom object, the overridden operators don't work properly.
Here is the relevant data from card.h:
#ifndef CARD_H
#define CARD_H
#include <QDataStream>
#include <QImage>
#include <QString>
class Card
{
private:
QString name;
QImage image;
QString type;
int strength;
int movement;
int deployCost;
QString back;
public:
Card();
QDataStream& read(QDataStream &dataStream);
QDataStream& write(QDataStream &dataStream) const;
...
};
QDataStream& operator <<(QDataStream &out, const Card &c);
QDataStream& operator >>(QDataStream &in, Card &c);
QDataStream& operator <<(QDataStream &out, const Card *c);
QDataStream& operator >>(QDataStream &in, Card *c);
//QDataStream& operator <<(QDataStream &out, const Card *&c);
//QDataStream& operator >>(QDataStream &in, Card *&c);
#endif // CARD_H
And here is card.cpp:
#include "card.h"
Card::Card()
{
}
QDataStream& operator <<(QDataStream &out, const Card &c) {
return c.write(out);
}
QDataStream& operator >>(QDataStream &in, Card &c) {
return c.read(in);
}
QDataStream& operator <<(QDataStream &out, const Card *c) {
return c->write(out);
}
QDataStream& operator >>(QDataStream &in, Card *c) {
return c->read(in);
}
/*QDataStream& operator <<(QDataStream &out, const Card *&c) {
return c->write(out);
}
QDataStream& operator >>(QDataStream &in, Card *&c) {
return c->read(in);
}*/
QDataStream& Card::read(QDataStream &dataStream) {
dataStream >> name;
dataStream >> image;
dataStream >> type;
dataStream >> strength;
dataStream >> movement;
dataStream >> deployCost;
dataStream >> back;
return dataStream;
}
QDataStream& Card::write(QDataStream &dataStream) const {
dataStream << name;
dataStream << image;
dataStream << type;
dataStream << strength;
dataStream << movement;
dataStream << deployCost;
dataStream << back;
return dataStream;
}
...
As you can see, I've tried both
QDataStream& operator <<(QDataStream &out, const Card *c);
QDataStream& operator >>(QDataStream &in, Card *c);
and
//QDataStream& operator <<(QDataStream &out, const Card *&c);
//QDataStream& operator >>(QDataStream &in, Card *&c);
If I use "Card *c", the data writes fine, but I get a SEGFAULT when I try to read. If I use "Card *&c", the program doesn't even recognize that I've overridden the operator, so it doesn't get called.
What am I doing wrong?
EDIT:
The problem comes when I am reading or writing "cards" which is a QHash defined in deck.h as
QHash<QString, Card*> cards;
deck.h:
#ifndef DECK_H
#define DECK_H
#include <QDataStream>
#include <QHash>
#include "card.h"
class Deck
{
private:
QString name;
QHash<QString, Card*> cards;
public:
Deck();
QDataStream &read(QDataStream &dataStream);
QDataStream &write(QDataStream &dataStream) const;
...
};
QDataStream &operator<<(QDataStream &out, const Deck &d);
QDataStream &operator>>(QDataStream &in, Deck &d);
#endif // DECK_H
deck.cpp:
#include "deck.h"
Deck::Deck()
{
}
QDataStream &operator<<(QDataStream &out, const Deck &d) {
return d.write(out);
}
QDataStream &operator>>(QDataStream &in, Deck &d) {
return d.read(in);
}
QDataStream &Deck::read(QDataStream &dataStream) {
dataStream >> name;
// Reading the QHash - one problem spot
dataStream >> cards;
return dataStream;
}
QDataStream &Deck::write(QDataStream &dataStream) const {
dataStream << name;
// Writing the QHash - the other problem spot
dataStream << cards;
return dataStream;
}
...
Because the cards are stored as pointers in the QHash, I'm not sure how I am supposed to get around overriding the pointer operator. Is there a better way to read/write the QHash, or the *Card stored in the QHash?
EDIT:
As per Marek R's answer, I looked for a way to avoid writing a Card*. The solution was to iterate through the QHash and save each individual Card.
First problem is that you are trying to do this operator for pointer.
If my coworker would do this I would probably try to strangle him. For 99.9999% of cases never overload operators for pointers.
If you insist on doing that I guessing that you code with usage of this operator looks like this:
Card *uninitializedPointer;
someDataStream >> uninitializedPointer; // SEGFAULT
And this is wrong since uninitializedPointer is .. its name says what is the problem, you are referring to restricted or random chunk of memory.
Probably you want (this is wrong to but it will work, I'm showing this only to explain crash not as a fix):
Card *initializedPointer = new Card;
someDataStream >> initializedPointer;
Or:
Card object;
someDataStream >> &object;
Do not try fix this by do allocation in operator or you will fry in hell :)
Simply trash operators overloaded for pointers you really don't need them.
The expected signatures for stream operators are:
stream& operator<<(stream& out, const T& t);
stream& operator>>(stream& in, T& t);
See How to properly overload the << operator for an ostream? or QDatastream operator>> for QList<Class*> for instance.
This applies to QDataStream as well. The difficulty here comes the fact that T is a pointer type, but essentially you just replace T by your pointer type. So your signatures should be :
QDataStream& operator<<(QDataStream &out, Card* const& c);
QDataStream& operator>>(QDataStream &in, Card*& c);
Note the placement of const in Card* const, because it is the pointer that needs to be const, not the pointee.