I tried to send vector with class from client to server. Data is sent between sockets, but when I want to write them to the console, the server crashes
#include <QCoreApplication>
#include <QTcpSocket>
#include <QDataStream>
#include "data.h"
//client
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
std::vector< data > wektor;
data asd;
asd.setName("asd");
wektor.push_back(asd);
wektor.push_back(asd);
for (data w : wektor){
qDebug()<<w.getName();
}
quint16 rozmiarSampla = sizeof( wektor[0] );
quint16 ileSampli = wektor.size();
QTcpSocket socket;
socket.connectToHost("127.0.0.1",9999);
if( !socket.waitForConnected() )
return 1;
QDataStream stream(&socket);
QString typ("Paczka z buforem");
stream << typ << rozmiarSampla << ileSampli;
stream.writeRawData( reinterpret_cast<const char*>( wektor.data() ) , rozmiarSampla * ileSampli );
socket.flush();
socket.waitForBytesWritten();
return 0;
}
SERVER
#include <QCoreApplication>
#include <QTcpServer>
#include <QTcpSocket>
#include <QDataStream>
#include <QScopedPointer>
#include "data.h"
#include <iostream>
#include <string.h>
//serwer
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
std::vector< data > wektor;
QString typ;
quint16 rozmiarSampla;
quint16 ileSampli;
QTcpServer serwer;
serwer.listen(QHostAddress::AnyIPv4,9999);
if( !serwer.waitForNewConnection(30000) )
return 1;
QScopedPointer<QTcpSocket> socket{ serwer.nextPendingConnection() };
if(! socket->waitForReadyRead() )
return 1;
QDataStream stream(socket.data());
stream >> typ >> rozmiarSampla >> ileSampli;
if( rozmiarSampla == sizeof( wektor[0] ) ) {
wektor.resize(ileSampli);
stream.readRawData( reinterpret_cast<char*>( wektor.data() ) , rozmiarSampla * ileSampli );
} else {
stream.skipRawData(rozmiarSampla * ileSampli);
}
qDebug() << "Typ: " << typ;
qDebug() << "RozmiarSampla: " << rozmiarSampla;
qDebug() << "IleSampli: " << ileSampli;
qDebug()<<wektor.size();
qDebug()<< wektor[0].getName();
return a.exec();
}
DATA.H
#ifndef DATA_H
#define DATA_H
#include <QtCore>
class data
{
public:
data();
void setName(QString name);
QString getName();
private:
QString Name;
};
#endif // DATA_H
DATA.CPP
#include "data.h"
#include <QtCore>
data::data()
{
this->Name = "null";
}
void data::setName(QString name)
{
this->Name = name;
}
QString data::getName()
{
return this->Name;
}
The server crashes when trying to print the name from the data class.
I don't understand why this is happening, can someone explain to me what the error is? When I tried to send a vector that consisted of data, e.g. int, everything worked fine.
As suggested by a comment, the problem here is that QString is not a trivially copyable type.
What does that mean? Well, here is a simple model of what QString looks like:
class QString {
public:
// all of the methods
private:
// QString doesn't store the data directly, it stores a pointer to the string data
Data* data;
};
reference: https://codebrowser.dev/qt5/qtbase/src/corelib/text/qstring.h.html#979
So when you do stream.writeRawData( reinterpret_cast<const char*>( wektor.data() ) , rozmiarSampla * ileSampli ), you are copying the data contained in the QString... but what it contains is just a pointer! You are not copying the string data itself, just the pointer to the data. Of course, that pointer is nonsense on the other side of the socket.
So how can you fix this? There are lots of ways, but I would suggest specializing the QDataStream stream operator for your type:
// In your data class header file
QDataStream& operator<<(QDataStream& stream, const data& d);
QDataStream& operator>>(QDataStream& stream, data& d);
// and in the cpp file
QDataStream& operator<<(QDataStream& stream, const data& d) {
stream << d.getName();
return stream;
}
QDataStream& operator>>(QDataStream& stream, data& d) {
QString name;
stream >> name;
d.setName(name);
return stream;
}
QDataStream knows how to serialize QString when given a QString like this. What these specializations do is show QDataStream how to serialize data. Now with this, you can serialize your std::vector<data> to the data stream with something like:
stream << wektor.size(); // serialize the number of instances
for (const auto& d : wektor)
stream << d; // serialize each data instance
And you can deserialize them with something like:
size_t wektorSize;
stream >> wektorSize;
std::vector<data> wektor{wektorSize};
for (auto& d : wektor)
stream >> d;
Related
First of all I'm not very experienced with C++, so maybe I'm overseeing something here.
I'm trying to dynamically generate protobuf Messages from .proto files with the following code:
int init_msg(const std::string & filename, protobuf::Arena* arena, protobuf::Message** new_msg){
using namespace google::protobuf;
using namespace google::protobuf::compiler;
DiskSourceTree source_tree;
source_tree.MapPath("file", filename);
MuFiErCo error_mist;
Importer imp(&source_tree, &error_mist);
printf("Lade Datei:%s \n", filename.c_str());
const FileDescriptor* f_desc = imp.Import("file");
const Descriptor* desc = f_desc->FindMessageTypeByName("TestNachricht");
const Message* new_msg_proto = dmf.GetPrototype(desc);
*new_msg = new_msg_proto->New(arena);
//Debug
cout << (*new_msg)->GetTypeName() << endl;
return 0;
}
int main(int argc, char* argv[]){
protobuf::Arena arena;
protobuf::Message *adr2, *adr1;
init_msg("schema-1.proto", &arena, &adr1);
init_msg("schema-1.proto", &arena, &adr2);
printf("MSG_Pointer: %p, %p\n", adr1, adr2);
cout << adr1->GetTypeName() << endl;
arena.Reset();
return 0;
}
I thought if i use Arena, the new Message is also available outside the scope of the function.
But there is always a segfault if i try to access the Message.
I guess it's a simple error, but I couldn't figure out, how to solve this.
Here is the ouput:
Lade Datei:schema-1.proto
packet.TestNachricht
Lade Datei:schema-1.proto
packet.TestNachricht
MSG_Pointer: 0x1b293b0, 0x1b287f0
Speicherzugriffsfehler (Speicherabzug geschrieben)
The problem, I think, is that FileDescriptor et al are destroyed when
init_msg returns, leaving the newly created message with no way to
interrogate its .proto definition. You'd need to move Importer
instance to main and keep it alive. This has nothing to do with
arenas. – Igor Tandetnik
That was the solution.
Here is some working example code
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <memory>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/message.h>
#include <google/protobuf/compiler/importer.h>
#include <google/protobuf/dynamic_message.h>
#include <google/protobuf/arena.h>
using namespace std;
using namespace google::protobuf;
class MuFiErCo : public compiler::MultiFileErrorCollector
{
public:
void AddError(const string & filename, int line, int column, const string & message){
printf("Err: %s\n", message.c_str());
}
void AddWarning(const string & filename, int line, int column, const string & message){
printf("Warn: %s\n", message.c_str());
}
};
compiler::Importer* init_proto_dir(Arena* arena, const std::string &root_dir){
using namespace compiler;
static DiskSourceTree source_tree;
source_tree.MapPath("", root_dir);
static MuFiErCo error_mist;
static Importer* imp = Arena::Create<Importer>(arena, &source_tree, &error_mist);
return imp;
}
void init_proto_def(compiler::Importer* imp, const std::string &proto_file){
using namespace compiler;
imp->Import(proto_file);
return;
}
Message* init_msg(compiler::Importer* imp, Arena* arena, const std::string &msg_name){
const DescriptorPool* pool = imp->pool();
static DynamicMessageFactory dmf;
const Descriptor* desc = pool->FindMessageTypeByName(msg_name);
const Message* msg_proto = dmf.GetPrototype(desc);
return msg_proto->New(arena);
}
int set_value(Message* msg, const char* value_name, unsigned long int value){
const Message::Reflection* reflec = msg->GetReflection();
const Descriptor* desc = msg->GetDescriptor();
const FieldDescriptor* fdesc = desc->FindFieldByName(value_name);
reflec->SetUInt64(msg, fdesc, value);
return 0;
}
int main(int argc, char* argv[]){
Arena arena;
compiler::Importer* imp = init_proto_dir(&arena, "");
init_proto_def(imp, "schema-1.proto");
Message* msg = init_msg(imp, &arena, "packet.TestNachricht");
set_value(msg, "zahl", 23434);
cout << msg->DebugString() << endl;
return 0;
}
After an outfile data is processed, I want to take in that data back in again using an fstream variable called infile. Can an operation like the one below be possible after the outfile is complete? My main goal is to read back in what was randomly put out to the file so I can process it into a linked list eventually. I have tested a code similar to this but I get zeroes as a result. Thanks.
ifstream infile;
string FileName = "Numbers.txt";
infile.open(FileName);
while (infile)
{
infile >> RandomNumber;
cout << RandomNumber << endl;
}
infile.close();
This should work if ( infile ) will check if the file is opened and while ( infile >> RandomNumber ) will get all the numbers in the file:
ifstream infile;
string FileName = "Numbers.txt";
infile.open(FileName);
if ( infile ) {
while ( infile >> RandomNumber ) {
cout << RandomNumber << endl;
}
}
infile.close();
This is not an answer to the OPs question but rather it is provided towards a small conversation within a series of comments about reading files and parsing data. This is directed towards Tony and any who would wish to use this class setup. There are some things with in this class that pertain to other classes within my library, some of these include my Logger which would log messages to either a file or the console, and the message types are either Info, Warning, Error or Console. My Logger is derived from a Singleton class since I would only need one logger per solution to handle all messages. I have a Utility Class that hands a majority of string manipulation functions where the constructor is private and all functions or members are declared as static. This class also relies on an ExceptionHandler class that will accept either std::strings or std::ostringstream objects. My Logger accepts the same. Also some of my classes also rely on a BlockThread class that allows to work with multithreading which upon construction it will Create and Initialize a CriticalSection then Enter the Critical Section and then upon Destruction it will Leave and then Destroy the CriticalSection. Now as for this demonstration I will only be showing my FileHandler Classes along with 2 of its derived types, TextFileReader and TextFileWriter. I have more derived FileHandlers that work with reading in texture files, custom data structures etc, that work on binary files as opposed to text file, but for the purpose of this only the Text File Handlers will be shown.
FileHandler.h
#ifndef FILE_HANDLER_H
#define FILE_HANDLER_H
namespace util {
//class AssetStorage;
class FileHandler {
protected:
//static AssetStorage* m_pAssetStorage;
std::fstream m_fileStream;
std::string m_strFilePath;
std::string m_strFilenameWithPath;
private:
bool m_bSaveExceptionInLog;
public:
virtual ~FileHandler();
protected:
FileHandler( const std::string& strFilename, bool bSaveExceptionInLog );
void throwError( const std::string& strMessage ) const;
void throwError( const std::ostringstream& strStreamMessage ) const;
bool getString( std::string& str, bool appendPath );
private:
FileHandler( const FileHandler& c ); // Not Implemented
FileHandler& operator=( const FileHandler& c ); // Not Implemented
}; // FileHandler
} // namespace util
FileHandler.cpp
#include "stdafx.h"
#include "FileHandler.h"
namespace util {
// ----------------------------------------------------------------------------
// FileHandler()
FileHandler::FileHandler( const std::string& strFilename, bool bSaveExceptionInLog ) :
m_bSaveExceptionInLog( bSaveExceptionInLog ),
m_strFilenameWithPath( strFilename ) {
// Extract Path Info If It Exists
std::string::size_type lastIndex = strFilename.find_last_of( "/\\" );
if ( lastIndex != std::string::npos ) {
m_strFilePath = strFilename.substr( 0, lastIndex );
}
if ( strFilename.empty() ) {
throw ExceptionHandler( __FUNCTION__ + std::string( " missing filename", m_bSaveExceptionInLog ) );
}
} // FileHandler
// ----------------------------------------------------------------------------
// ~FileHandler
FileHandler::~FileHandler() {
if ( m_fileStream.is_open() ) {
m_fileStream.close();
}
} // ~FileHandler
// ----------------------------------------------------------------------------
// throwError()
void FileHandler::throwError( const std::string& strMessage ) const {
throw ExceptionHandler( "File [" + m_strFilenameWithPath + "] " + strMessage, m_bSaveExceptionInLog );
} // throwError( const std::string )
// ----------------------------------------------------------------------------
// throwError()
void FileHandler::throwError( const std::ostringstream& strStreamMessage ) const {
throwError( strStreamMessage.str() );
} // throwError( const std::ostringstream )
// ----------------------------------------------------------------------------
// getString()
bool FileHandler::getString( std::string& str, bool appendPath ) {
m_fileStream.read( &str[0], str.size() );
if ( m_fileStream.fail() ) {
return false;
}
// Trim Right
str.erase( str.find_first_of( char(0) ) );
if ( appendPath && !m_strFilePath.empty() ) {
// Add Path If One Exixsts
str = m_strFilePath + "/" + str;
}
return true;
} // getString
} // namespace util
TextFileReader.h
#ifndef TEXT_FILE_READER_H
#define TEXT_FILE_READER_H
#include "FileHandler.h"
namespace util {
class TextFileReader : public FileHandler {
private:
public:
explicit TextFileReader( const std::string& strFilename );
// virtual ~TextFileReader(); // Default OK
std::string readAll() const;
bool readLine( std::string& strLine );
private:
TextFileReader( const TextFileReader& c ); // Not Implemented
TextFileReader& operator=( const TextFileReader& c ); // Not Implemented
}; // TextFileReader
} // namespace util
#endif // TEXT_FILE_READER_H
TextFileReader.cpp
#include "stdafx.h"
#include "TextFileReader.h"
namespace util {
// ----------------------------------------------------------------------------
// TextFileReader()
TextFileReader::TextFileReader( const std::string& strFilename ) :
FileHandler( strFilename, true ) {
m_fileStream.open( m_strFilenameWithPath.c_str(), std::ios_base::in );
if ( !m_fileStream.is_open() ) {
throwError( __FUNCTION__ + std::string( " can not open file for reading" ) );
}
} // TextFileReader
// ----------------------------------------------------------------------------
// readAll()
std::string TextFileReader::readAll() const {
std::ostringstream strStream;
strStream << m_fileStream.rdbuf();
return strStream.str();
} // readAll
// ----------------------------------------------------------------------------
// readLine()
// Returns A String Containing The Next Line Of Text Stored In The File
bool TextFileReader::readLine( std::string& strLine ) {
if ( m_fileStream.eof() ) {
return false;
}
std::getline( m_fileStream, strLine );
return true;
} // readLine
} // namespace util
TextFileWriter.h
#ifndef TEXT_FILE_WRITER_H
#define TEXT_FILE_WRITER_H
#include "FileHandler.h"
namespace util {
class TextFileWriter : public FileHandler {
private:
public:
TextFileWriter( const std::string& strFilename, bool bAppendToFile, bool bSaveExceptionInLog = true );
// virtual ~TextFileWriter(); // Default OK
void write( const std::string& str );
private:
TextFileWriter( const TextFileWriter& c ); // Not Implemented
TextFileWriter& operator=( const TextFileWriter& c ); // Not Implemented
}; // TextFileWriter
} // namespace util
#endif // TextFileWriter
TextFileWriter.cpp
#include "stdafx.h"
#include "TextFileWriter.h"
namespace util {
// ----------------------------------------------------------------------------
// TextFileWriter()
TextFileWriter::TextFileWriter( const std::string& strFilename, bool bAppendToFile, bool bSaveExceptionInLog ) :
FileHandler( strFilename, bSaveExceptionInLog ) {
m_fileStream.open( m_strFilenameWithPath.c_str(),
std::ios_base::out | (bAppendToFile ? std::ios_base::app : std::ios_base::trunc) );
if ( !m_fileStream.is_open() ) {
throwError( __FUNCTION__ + std::string( " can not open file for writing" ) );
}
} // TextFileWriter
// ----------------------------------------------------------------------------
// write()
void TextFileWriter::write( const std::string& str ) {
m_fileStream << str;
} // write
} // namespace util
And Here is some code fragments of a class that used the FileHandlers.
Here is the class header that uses the FileHandler It reads in a text file and parses it to determine which GUI objects to create, load into memory and how they are to be displayed on the screen, it will also nest one GUI type as a child to another. I will not be showing the full implementation of this class but only a few functions where it concerns the use of using the FileHandlers.
GuiLoader.h
#ifndef GUI_LOADER_H
#define GUI_LOADER_H
#include "Engine.h"
#include "CommonStructs.h"
#include "Property.h"
#include "TextFileReader.h"
namespace vmk {
class AssetStorage;
class GuiCompositeElement;
class GuiElement;
class GuiLayout;
class GuiRenderable;
class GuiText;
class VisualMko;
class GuiLoader sealed {
friend GuiElement* Engine::loadGui( const std::string& strFilename ) const;
private:
std::string m_strFilename;
util::TextFileReader m_file;
bool m_inBlockComment;
unsigned m_uNumBracesOpened;
unsigned m_uLineNumber; // Keep Track OfLine Number For Error Messages
GuiElement* m_pLastGuiRoot;
std::vector<GuiCompositeElement*> m_vpParents;
std::string m_strLine;
std::unordered_map<std::string, TextureInfo> m_mTextureInfos;
std::unordered_map<std::string, FontFace> m_mFontFace;
FontManager* m_pFontManager;
AssetStorage* m_pAssetStorage;
public:
// virtual ~GuiLoader(); // Default Ok
GuiElement* getRoot() const;
private:
GuiLoader( const std::string& strFilename );
GuiLoader( const GuiLoader& c ); // Not Implemented
GuiLoader& operator=( const GuiLoader& c ); // Not Implemented
bool getNextLine();
std::string getFailedLocation() const;
void removeComments();
void parseGui();
bool handleOpenBrace( unsigned uStartLocation, GuiCompositeElement* pGui );
void addToParent( GuiRenderable* pRenderable ) const;
bool getParameter( std::string strParam, unsigned uStartLocation, std::string& strValue, bool isRequired = true, char endCharacter = ' ' ) const;
void setOptionalParameters( unsigned uStartLocation, GuiElement* pGui ) const;
void getOptionalLayoutParameters( unsigned uStartLocation, glm::ivec2& offsetFromParent, Gui::Alignment& eAlignChildren, glm::uvec2& size, std::string& strId ) const;
void setLayoutParameters( unsigned uStartLocation, GuiLayout* pLayout ) const;
bool getOptionalBackgroundParameters( unsigned uStartLocation, TextureInfo& textureInfo, glm::uvec2& origin ) const;
bool getOptionalBackgroundParameters( unsigned uStartLocation, TextureInfo& textureInfo, glm::uvec2& origin, glm::uvec2& size, bool isRequired = true ) const;
void getRequiredTextParameters( unsigned uStartLocation, FontFace& fontFace, std::string& strText ) const;
void setTextShadowParameters( unsigned uStartLocation, GuiText* pGui ) const;
void setVisualMkoParameters( unsigned uStartLocation, VisualMko* pVisualMko ) const;
}; // GuiLoader
} // namespace vmk
#endif // GUI_LOADER_H
GuiLoader.cpp - Only A Few Portions Are Shown
#include "stdafx.h"
#include "GuiLoader.h"
#include "AssetStorage.h"
#include "FontManager.h"
#include "Gui.h"
#include "Image2d.h"
#include "Logger.h"
#include "TextureFileReader.h"
#include "Utility.h"
using namespace util;
namespace vmk {
// ----------------------------------------------------------------------------
// GuiLoader()
GuiLoader::GuiLoader( const std::string& strFilename ) :
m_strFilename( strFilename ),
m_file( strFilename ),
m_inBlockComment( false ),
m_uNumBracesOpened( 0 ),
m_uLineNumber( 0 ),
m_pLastGuiRoot( nullptr ),
m_pFontManager( FontManager::get() ),
m_pAssetStorage( AssetStorage::get() ) {
while ( getNextLine() ) {
parseGui();
}
if ( m_uNumBracesOpened > 0 ) {
std::ostringstream strStream;
strStream << __FUNCTION__ << getFailedLocation() << ". Missing " << m_uNumBracesOpened << " closing brace" << ( m_uNumBracesOpened > 1 ? "s" : "" ) << ".";
throw ExceptionHandler( strStream );
}
if ( m_inBlockComment ) {
std::ostringstream strStream;
strStream << __FUNCTION__ << getFailedLocation() << ". Missing closing block comment */.";
}
} // GuiLoader
// ----------------------------------------------------------------------------
// getRoot()
GuiElement* GuiLoader::getRoot() const {
return m_pLastGuiRoot;
} // getRoot
// ----------------------------------------------------------------------------
// getNextLine()
// Returns True If Got A Line Of Text (Could Be Blank If It Is All Commented
// Out Or If It Truly Was A Blank Line). False is Returned When There Is
// No More Data In The File
bool GuiLoader::getNextLine() {
if ( !m_file.readLine( m_strLine ) ) {
return false;
}
++m_uLineNumber;
m_strLine = Utility::trim( m_strLine );
//std::cout << m_uLineNumber << ": " << m_strLine << std::endl; // Use This For Debugging The GuiLoader
removeComments();
return true;
} // getNextLine
// ... Other Functions Here
} // namespace vmk
This was presented to show the robustness of my file handling classes. There are many other classes not shown here. My library is a part of a 3D Graphics Rendering Library that uses Modern OpenGL. There are several hundred class objects and 100,000s of lines of code for doing 3D graphics rendering, sprite loading, physics simulation, animation, audio playback, audio stream playback, and much more. Not all of this source code is of my own design for this code is copy right protected by Marek A. Krzeminski, MASc and his works can be found at www.MarekKnows.com
All code shown here was not copied and pasted from his site, it was all hand typed while following along with his video tutorials. It was compiled and debug manually. This project has been in the works for a couple of years, and still to this day more is being added. I have been a proud member of his website and community since 2007-2008.
int number = 0;
string filename( "Numbers.txt" );
std::ofstream out;
std::ifstream in;
std::cout << "enter a number: << std::endl;
std::cin >> number;
out.open( filename.c_str(), std::ios_base::out );
if ( !out.is_open() ){
// Print, Log Error, Throw Error, Return Etc.
}
for ( int i = 0; i < number; i++ ) {
out << rand() % 100 << std::endl;
}
out.close();
in.open( filename.c_str(), std::ios_base::in );
if ( !in.is_open() ) {
// Error Case Here
}
while ( !in.eof() ) { // Usually bad practice; but my file handling classes are parser objects are too large to show here.
in >> RandomNumber;
std::cout << RandomNumber << endl;
}
infile.close();
I'd like to save custom class to XML using QSettings. But I always get XML without structure members.
#include <QCoreApplication>
#include <QtCore/qdatastream.h>
#include <qxmlstream.h>
#include <qdebug.h>
#include <QtCore/QSettings>
#include <QMetaType>
struct Interface_struct
{
QString name;
QString ip;
};
Q_DECLARE_METATYPE(Interface_struct)
QDataStream& operator <<(QDataStream& out, const Interface_struct& s)
{
out << s.name << s.ip;
return out;
}
QDataStream& operator >>(QDataStream& in, Interface_struct& s)
{
in >> s.name;
in >> s.ip;
return in;
}
static bool readXmlFile(QIODevice &device, QSettings::SettingsMap &map)
{
qDebug()<< "read";
QXmlStreamReader reader(&device);
QString key;
while(!reader.atEnd())
{
reader.readNext();
if( reader.isStartElement() && reader.tokenString() != "Settings")
{
if( reader.text().isNull() )
{
// key = Settings
if(key.isEmpty())
{
key = reader.tokenString();
}
// key = Settings/Intervall
else
{
key += "/" + reader.tokenString();
}
}
else
{
map.insert(key, reader.text().data());
}
}
}
return true;
}
static bool writeXmlFile(QIODevice &device, const QSettings::SettingsMap &map)
{
qDebug()<< "write";
QXmlStreamWriter writer(&device);
writer.writeStartDocument("1.0");
writer.writeStartElement("Settings");
foreach(QString key, map.keys())
{
foreach(QString elementKey, key.split("/"))
{
writer.writeStartElement(elementKey);
}
writer.writeCharacters(map.value(key).toString());
writer.writeEndElement();
}
writer.writeEndElement();
writer.writeEndDocument();
return true;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qRegisterMetaType<Interface_struct>("Interface_struct");
qRegisterMetaTypeStreamOperators<Interface_struct>("Interface_struct");
{
Interface_struct s;
s.name = QString("br03000");
s.ip = QString("172.16.222.5");
const QSettings::Format xml_format =
QSettings::registerFormat("xml", readXmlFile, writeXmlFile);
if(xml_format == QSettings::InvalidFormat)
{
qDebug() << "InvalidFormat!";
return 0;
}
QSettings::setPath(xml_format, QSettings::UserScope, "/home/farit/test/");
QSettings settings(xml_format, QSettings::UserScope, "xml_cfg");
settings.setValue("network", QVariant::fromValue(s));
}
{
QSettings::Format xml_format =
QSettings::registerFormat("xml", readXmlFile, writeXmlFile);
QSettings::setPath(xml_format, QSettings::UserScope, "/home/farit/test/");
QSettings settings(xml_format, QSettings::UserScope, "xml_cfg");
QVariant value = settings.value("network");
Interface_struct interface = value.value<Interface_struct>();
qDebug() << "TEST: " << interface.name << interface.ip;
}
return 0;
}
I get this output:
read
write
read
TEST: "" ""
Press <RETURN> to close this window...
And XML looks like this:
<?xml version="1.0" encoding="UTF-8"?><Settings><network></network></Settings>
How can I save structure members of custom class to XML using QSettings?
UPDATE: I'm sorry, I forgot to mention, that is supposed to be done in Qt4.
I am trying to write a SingleApplication class that will only allow one instance of the program to be running. I am implementing this using QSharedMemory
The program works fine, unless I use a key with the value "42". Is there something wrong I am doing? Is this undefined behavior?
Main.cpp
int main(int argc, char *argv[])
{
//QApplication a(argc, argv);
SingleApplication a(argc, argv, "42"); //Does not work with '42'. Will work for any other value.
MainWindow w;
w.show();
return a.exec();
}
SingleApplication.h
class SingleApplication : public QApplication
{
Q_OBJECT
public:
SingleApplication(int &argc, char *argv[], const QString uniqueKey);
bool alreadyExists() const{ return bAlreadyExists; }
bool isMasterApp() const { return !alreadyExists(); }
bool sendMessage(const QString &message);
public slots:
//void checkForMessages();
signals:
//void messageAvailable(const QStringList& messages);
private:
bool bAlreadyExists;
QSharedMemory sharedMemory;
};
SingleApplication.cpp
SingleApplication::SingleApplication(int &argc, char *argv[], const QString uniqueKey) : QApplication(argc, argv){
sharedMemory.setKey(uniqueKey);
//Create if one does not exist already
if(sharedMemory.create(5000))
{
qDebug() << "Created!";
bAlreadyExists = false;
}
else{
if(sharedMemory.error() == QSharedMemory::AlreadyExists){
qWarning() << "Program is already running!";
}
}
}
I propose you next solution. It's tested, but it's doesn't support sending messages between instances. And it resolves some bugs of your solution. Because it is not enough just to test memory. You need to guard a creation of shared memory.
RunGuard.h
#ifndef RUNGUARD_H
#define RUNGUARD_H
#include <QObject>
#include <QSharedMemory>
#include <QSystemSemaphore>
class RunGuard
{
public:
RunGuard( const QString& key );
~RunGuard();
bool isAnotherRunning();
bool tryToRun();
void release();
private:
const QString key;
const QString memLockKey;
const QString sharedmemKey;
QSharedMemory sharedMem;
QSystemSemaphore memLock;
Q_DISABLE_COPY( RunGuard )
};
#endif // RUNGUARD_H
RunGuard.cpp
#include "RunGuard.h"
#include <QCryptographicHash>
namespace
{
QString generateKeyHash( const QString& key, const QString& salt )
{
QByteArray data;
data.append( key.toUtf8() );
data.append( salt.toUtf8() );
data = QCryptographicHash::hash( data, QCryptographicHash::Sha1 ).toHex();
return data;
}
}
RunGuard::RunGuard( const QString& key )
: key( key )
, memLockKey( generateKeyHash( key, "_memLockKey" ) )
, sharedmemKey( generateKeyHash( key, "_sharedmemKey" ) )
, sharedMem( sharedmemKey )
, memLock( memLockKey, 1 )
{
QSharedMemory fix( sharedmemKey ); // Fix for *nix: http://habrahabr.ru/post/173281/
fix.attach();
}
RunGuard::~RunGuard()
{
release();
}
bool RunGuard::isAnotherRunning()
{
if ( sharedMem.isAttached() )
return false;
memLock.acquire();
const bool isRunning = sharedMem.attach();
if ( isRunning )
sharedMem.detach();
memLock.release();
return isRunning;
}
bool RunGuard::tryToRun()
{
if ( isAnotherRunning() ) // Extra check
return false;
memLock.acquire();
const bool result = sharedMem.create( sizeof( quint64 ) );
memLock.release();
if ( !result )
{
release();
return false;
}
return true;
}
void RunGuard::release()
{
memLock.acquire();
if ( sharedMem.isAttached() )
sharedMem.detach();
memLock.release();
}
I would drop the whole own single application concept implemented by you from scratch personally. The qt-solutions repository has contained the QtSingleApplication class which was ported to Qt 5, too. You ought to be able to use that. There is little to no point in reinventing the wheel in my humble opinion.
Should you still insist on doing it on your own, while your idea seems to be a little strange at first about passing the key to the constructor and not to manage that inside the class transparently, this could be a workaround for your case to make the solution more robust:
SingleApplication::SingleApplication(int &argc, char *argv[], const QString uniqueKey) : QApplication(argc, argv)
{
sharedMemory.setKey(uniqueKey);
if (!sharedMemory.create(5000)) {
while (sharedMemory.error() == QSharedMemory::AlreadyExists) {
// Set a new key after some string manipulation
// This just a silly example to have a placeholder here
// Best would be to increment probably, and you could still use
// a maximum number of iteration just in case.
sharedMemory.setKey(sharedMemory.key() + QLatin1String("0"));
// Try to create it again with the new key
sharedMemory.create(5000);
}
if (sharedMemory.error() != QSharedMemory::NoError)
qDebug() << "Could not create the shared memory:" << sharedMemory.errorString();
else
{
qDebug() << "Created!";
bAlreadyExists = false;
}
}
}
Disclaimer: this is just pseudo code and I have never tested it, actually, not even tried to compile it myself!
Why istream object after calling readsome() method don't give any chars in buffer? Is there any error in class construction?
StreamBuffer.h
#ifndef StreamBuffer_h
#define StreamBuffer_h
#include <string>
#include <fstream>
#include <iostream>
#include <iterator>
enum StreamBufferState
{
STREAMBUFFER_OK = 0,
STREAMBUFFER_EOF = 1
};
class StreamBuffer
{
std::fstream file;
std::istream istrm;
int maxBufferSize;
std::string buffer;
public:
StreamBuffer(int maxBuffSize, const std::string& filename);
~StreamBuffer();
void SetMaxBufferSize(unsigned int maxBuffSize);
StreamBufferState FullBufferWithData();
std::string GetDataBuffer();
};
#endif
StreamBuffer.cpp
#include "StreamBuffer.h"
using namespace std;
StreamBuffer::StreamBuffer(int maxBuffSize, const std::string& filename) : istrm( !filename.empty() ? file.rdbuf() : cin.rdbuf() )
{
SetMaxBufferSize(maxBuffSize);
if(!filename.empty())
{
file.open(filename.c_str(),ios::in | ios::binary);
}
else
{
std::cin>>noskipws;
}
}
StreamBuffer::~StreamBuffer()
{
file.close();
}
void StreamBuffer::SetMaxBufferSize(unsigned int maxBuffSize)
{
maxBufferSize = maxBuffSize;
}
StreamBufferState StreamBuffer::FullBufferWithData()
{
istrm.readsome((char*)&buffer[0],maxBufferSize);
if(istrm.eof())
return STREAMBUFFER_EOF;
return STREAMBUFFER_OK;
}
std::string StreamBuffer::GetDataBuffer()
{
string buf = buffer;
return buf;
}
File is opened, but readsome() don't read buffer.
You have undefined behavior in your code, as you try read into an empty string. You need to set the size of buffer.
An unrelated logical error: In the FullBufferWithData function you will return "OK" even if there is an error reading the file.