TinyXml how to serialize - c++

EDIT:
How can I serialize an xml using tinyxml?
TiXmlDocument doc;
TiXmlElement * root;
root = new TiXmlElement( "Data" );
doc.SaveFile( "madeByHand.xml" );
Question 2: Can I deserialize the xml i've created with the same library tinyxml? does it have this advantage?

Have you tried reading the documentation? There's details of the print function on that first page. Those docs link to a tutorial which has an example of writing to and reading from a file:
#include <string>
#include <map>
using namespace std;
typedef std::map<std::string,std::string> MessageMap;
// a basic window abstraction - demo purposes only
class WindowSettings
{
public:
int x,y,w,h;
string name;
WindowSettings()
: x(0), y(0), w(100), h(100), name("Untitled")
{
}
WindowSettings(int x, int y, int w, int h, const string& name)
{
this->x=x;
this->y=y;
this->w=w;
this->h=h;
this->name=name;
}
};
class ConnectionSettings
{
public:
string ip;
double timeout;
};
class AppSettings
{
public:
string m_name;
MessageMap m_messages;
list<WindowSettings> m_windows;
ConnectionSettings m_connection;
AppSettings() {}
void save(const char* pFilename);
void load(const char* pFilename);
// just to show how to do it
void setDemoValues()
{
m_name="MyApp";
m_messages.clear();
m_messages["Welcome"]="Welcome to "+m_name;
m_messages["Farewell"]="Thank you for using "+m_name;
m_windows.clear();
m_windows.push_back(WindowSettings(15,15,400,250,"Main"));
m_connection.ip="Unknown";
m_connection.timeout=123.456;
}
};
int main(void)
{
// block: customise and save settings
{
AppSettings settings;
settings.m_name="HitchHikerApp";
settings.m_messages["Welcome"]="Don't Panic";
settings.m_messages["Farewell"]="Thanks for all the fish";
settings.m_windows.push_back(WindowSettings(15,25,300,250,"BookFrame"));
settings.m_connection.ip="192.168.0.77";
settings.m_connection.timeout=42.0;
settings.save("appsettings2.xml");
}
// block: load settings
{
AppSettings settings;
settings.load("appsettings2.xml");
printf("%s: %s\n", settings.m_name.c_str(),
settings.m_messages["Welcome"].c_str());
WindowSettings & w=settings.m_windows.front();
printf("%s: Show window '%s' at %d,%d (%d x %d)\n",
settings.m_name.c_str(), w.name.c_str(), w.x, w.y, w.w, w.h);
printf("%s: %s\n", settings.m_name.c_str(), settings.m_messages["Farewell"].c_str());
}
return 0;
}

Related

Populate and execute callbacks from array class attribute

I've recently started learning C++/Arduino and am working on abstracting some of my Arduino code in order to keep it more manageable.
I'm trying to construct a class with 2 arrays as attributes on it, one to store strings that represent commands, and a second one to store pointers to those functions.
The below code works (compiles), but when uploaded to the device both the listen and execute functions don't appear to work. I've searched around quite a lot, but can't find where I've gone wrong.
/* main.ino */
// SETUP
#include "SoftwareSerial.h"
SoftwareSerial bt(btRx, btTx);
#include "CMD.h"
const int cmdMax = 6;
ArriCMD cmd;
// COMMANDS
void cmdStatus()
{
Serial.println("OK");
}
// START
void setup()
{
Serial.begin(9600);
bt.begin(9600);
cmd.add("AH+STAT", cmdStatus);
}
void loop()
{
cmd.listen(bt);
}
/* ArriCMD.h */
#ifndef ArriCMD_h
#define ArriCMD_h
#include "Arduino.h"
#include "SoftwareSerial.h"
class ArriCMD
{
public:
ArriCMD();
void add(String cmd, void (*cb)());
void listen(SoftwareSerial serial);
void execute(String cmd);
private:
int _max = 16;
int _amt = 0;
String _cmds[16];
void (*_cbs[16])();
};
/* ArriCMD.cpp */
#include "Arduino.h"
#include "SoftwareSerial.h"
#include "ArriCMD.h"
ArriCMD::ArriCMD()
{
//
}
void ArriCMD::add(String cmd, void (*cb)())
{
if (_amt < _max) {
_cmds[_amt] = cmd;
_cbs[_amt] = *cb;
}
}
void ArriCMD::listen(SoftwareSerial serial)
{
if (serial.available()) {
String cmd = serial.readString();
Serial.print(cmd);
execute(cmd);
}
}
void ArriCMD::execute(String cmd)
{
for (int i = 0; i < _amt; i++) {
if (cmd == _cmds[i]) {
_cbs[i]();
}
}
}
While I've been programming for over a decade, C++ and microcontrollers are brand new to me, any and all help here would be hugely appreciated.
I do intend to open source these libraries, and the subsequent platforms they're built for, once I'm more comfortable with my code quality.
Looks like you forgot to increment your commands counter _amt
void ArriCMD::add(String cmd, void (*cb)())
{
if (_amt < _max) {
_cmds[_amt] = cmd;
_cbs[_amt] = *cb;
_amt++; // <-- here, don't you need it?
}
}
Apart from that, is there some particular reason for using raw array and raw function pointers in your code? I do not use Arduino, so I am not sure, but maybe this solution is a bit cleaner:
class ArriCMD
{
public:
ArriCMD();
void add(String cmd, std::function<void()> cb);
void listen(SoftwareSerial serial);
void execute(String cmd);
private:
std::map<String, std::function<void()> > _cmdMap;
};

Dynamically generate protobuf Message and return a pointer to it

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;
}

Design Issue - Making a Font Global (C++, Marmalade)

I have a Marmalade C++ project where the built in font doesn't scale to screen size. To deal with this issue I'm making a custom font, which is basically a png and a .xml file, containing the (0,0) vector of each character, and each character's size. I'm then creating an instance of the png, and drawing regions of it in a for loop to write on the screen.
The issue I'm having is the scope of this class. I want it to be used throughout the entire project. So the static DebugLogger would use this font, any UI's, or GameObjects, anything which need characters, would use this font. Almost like it's pre-built into the system.
The Logger in this project is a static class, with static functions. The custom font class I currently have is a Singleton, as is my understanding, it must be an instantiated object for the program to draw images of it. So I did not make it static. Is there a clear cut answer to this type of problem? The issue is the static class won't create a reference to a singleton, and I've been told the singleton is to be avoided from a design perspective anyway!
Thanks in advance guys!
Here's the code for reference:
CustomFontClass
#ifndef _CHARACTER_SET_H_
#define _CHARACTER_SET_H_
#include "Iw2D.h"
#include "Singleton.h"
#include "BaseImage.h"
#include <map>
#include <string>
typedef CIwArray<std::string> STR_ARR;
class CharacterSet : public Singleton<CharacterSet>
{
public:
CharacterSet();
void Write(std::string, CIwFVec2, float);
void Write(std::string, float, float, float);
void Write(char, CIwFVec2, float);
void Write(char, float, float, float);
bool IsInitialised() const { return mIsInitialised; }
// Space between characters is equivalent to 1 character width. This float multiplies that number. Default is .55f
void SetFontSpacing(float);
protected:
BaseImage* mspCharacters;
bool Initialise();
private:
std::map<const char, CIwFVec2> mFontMap;
static const std::string TEXTURE_PATH;
static const std::string TEXTURE_NAME;
static const CIwFVec2 mFontSize;
float mFontSpacing;
STR_ARR maTextureLocations;
bool mIsInitialised;
};
#define CHARACTERSET Singleton<CharacterSet>::GetInstance()
#endif
cpp
#include "Iw2D.h"
#include "Singleton.h"
#include "BaseImage.h"
#include "CharacterSet.h"
#include "IwLogger.h"
#include <map>
#include <sstream>
#include <string>
const std::string CharacterSet::TEXTURE_PATH = "textures/";
const std::string CharacterSet::TEXTURE_NAME = "font_main.png";
const CIwFVec2 CharacterSet::mFontSize = CIwFVec2(50, 63);
CharacterSet::CharacterSet() : Singleton<CharacterSet>()
{
mFontSpacing = 0.55f;
mIsInitialised = Initialise();
};
void CharacterSet::SetFontSpacing(float spacing)
{
if (spacing != NULL)
{
mFontSpacing = spacing;
}
};
void CharacterSet::Write(std::string text, CIwFVec2 position = CIwFVec2(50,50), float fontSize = 25.0f)
{
if (!text.empty())
{
mspCharacters->SetSize(CIwFVec2(fontSize, fontSize));
float newPosition = 0;
for (char& c : text)
{
mspCharacters->SetPosition(CIwFVec2((position.x + newPosition), position.y));
if (mFontMap.find(c) == mFontMap.end())
{
std::stringstream ss;
std::string mCharRef;
ss << c;
ss >> mCharRef;
std::string error = "CharacterSet::mFontMap[" + mCharRef + "] - Does not exist!";
IW_LOG_ERROR(error);
}
else
{
mspCharacters->Render(mFontMap[c], mFontSize);
}
newPosition += (fontSize * mFontSpacing);
}
}
else
{
IW_LOG_ERROR("CharacterSet::Write - std::string text is empty!");
}
};
bool CharacterSet::Initialise()
{
maTextureLocations.push_back(std::string(TEXTURE_PATH + TEXTURE_NAME));
mspCharacters = new BaseImage(maTextureLocations[0].c_str());
if (mspCharacters == NULL)
{
IW_LOG_ERROR("CharacterSet::mspCharacters - Failed to create BaseImage!");
return false;
}
//Numerical symbols
mFontMap['1'] = CIwFVec2(0, 0);
mFontMap['2'] = CIwFVec2(50, 0);
mFontMap['3'] = CIwFVec2(100, 0);
mFontMap['4'] = CIwFVec2(150, 0);
mFontMap['5'] = CIwFVec2(200, 0);
// ect... lots of these
IwLogger
#ifndef _IW_LOGGER_H_
#define _IW_LOGGER_H_
#include "IwDebug.h"
#include <string>
class IwLogger
{
public:
IwLogger();
enum LogLevel
{
INFO,
WARN,
ERROR
};
static void Log(LogLevel level, std::string msg);
static void Log(LogLevel level, std::string callingFunc, std::string msg);
static void LogFailedResource(std::string callingFunc, std::string resourcePath);
static void LogError(std::string msg) { Log(ERROR, msg); }
static void LogWarn(std::string msg) { Log(WARN, msg); }
static void LogInfo(std::string msg) { Log(INFO, msg); }
static void LogError(std::string callingFunc, std::string msg) { Log(ERROR, callingFunc, msg); }
static void LogWarn(std::string callingFunc, std::string msg) { Log(WARN, callingFunc, msg); }
static void LogInfo(std::string callingFunc, std::string msg) { Log(INFO, callingFunc, msg); }
};
#define IW_LOG_ERROR(msg) IwLogger::LogError(__FUNCTION__, msg)
#define IW_LOG_WARN(msg) IwLogger::LogWarn(__FUNCTION__, msg)
#define IW_LOG_INFO(msg) IwLogger::LogInfo(__FUNCTION__, msg)
#define IW_LOG_FAILED_RES(resPath) IwLogger::LogFailedResource(__FUNCTION__, resPath)
#endif
cpp
#include "CharacterSet.h"
#include "IwLogger.h"
void IwLogger::Log(IwLogger::LogLevel level, std::string msg)
{
switch (level)
{
case INFO:
IwTrace(INFO, (msg.c_str()));
break;
case WARN:
IwTrace(WARN, (msg.c_str()));
break;
case ERROR:
IwTrace(ERROR, (msg.c_str()));
break;
default:
IwTrace(ERROR, ("IwLogger::Log() - Uncatered for case! Nothing to log!"));
break;
}
}
void IwLogger::Log(IwLogger::LogLevel level, std::string callingFunc, std::string msg)
{
std::string finalMsg = callingFunc + "() - " + msg;
switch (level)
{
case INFO:
IwTrace(INFO, (finalMsg.c_str()));
break;
case WARN:
IwTrace(WARN, (finalMsg.c_str()));
break;
case ERROR:
IwTrace(ERROR, (finalMsg.c_str()));
break;
default:
IwTrace(ERROR, ("IwLogger::Log() - Uncatered for case! Nothing to log!"));
break;
}
}
void IwLogger::LogFailedResource(std::string callingFunc, std::string resourcePath)
{
std::string msg = "Failed to load resource: " + resourcePath;
LogError(callingFunc, msg);
}
The error messages:
1>c:\users\...\source\framework\customfont.h(12): error C2504: 'Singleton' : base class undefined (..\source\framework\BaseObject.cpp)
1>c:\users\...\source\framework\customfont.h(12): error C2143: syntax error : missing ',' before '<' (..\source\framework\BaseObject.cpp)
1>c:\users\nigel\...\source\framework\customfont.h(12): error C2504: 'Singleton' : base class undefined (..\source\framework\CustomFont.cpp)

Writing C++ program compressing/decompressing data

I have to write C++ program that like gzip can
*Take input from file or from char stream like compression below
gzip file
type file | gzip
*Program have file or char stream output like decompression below
gzip -d file.gz
gzip -dc file.gz
I don't know how to take to the task and what techniques have to use and how to create classes buffering input and output. I have classes buffering input and output and read/write data from/to file.
DataBuffer.h (taking uncompressed data from file):
#ifndef DataBuffer_h
#define DataBuffer_h
#include <fstream>
#include <string>
enum DataBufferState
{
DATABUFFER_OK = 0,
DATABUFFER_EOF = 1
};
class DataBuffer
{
std::fstream file;
std::string buffer;
unsigned int maxBufferSize;
public:
DataBuffer(const std::string& filename, unsigned int maxBuffSize);
~DataBuffer();
bool OpenFile(const std::string& filename);
void SetMaxBufferSize(unsigned int maxBuffSize);
DataBufferState FullBufferWithDataOld();
DataBufferState FullBufferWithData();
std::string GetDataBuffer();
};
#endif
DataBuffer.cpp:
#include "DataBuffer.h"
using namespace std;
DataBuffer::DataBuffer(const string& filename, unsigned int maxBuffSize)
{
OpenFile(filename);
SetMaxBufferSize(maxBuffSize);
}
DataBuffer::~DataBuffer()
{
file.close();
}
bool DataBuffer::OpenFile(const string& filename)
{
file.open(filename.c_str(),ios::in);
if(!file.is_open())
return false;
return true;
}
void DataBuffer::SetMaxBufferSize(unsigned int maxBuffSize)
{
maxBufferSize = maxBuffSize;
}
DataBufferState DataBuffer::FullBufferWithDataOld()
{
while(true)
{
string line;
streampos pos = file.tellg(); // Zapamietaj polozenie przed pobraniem linii
getline(file,line);
if( buffer.size()+line.size()>maxBufferSize )
{
// Cofnac wskaznik pliku
file.seekg(pos,ios::beg); // Przywroc polozenie sprzed pobrania linii
break;
}
buffer += line + "\n";
if(file.eof())
return DATABUFFER_EOF;
}
return DATABUFFER_OK;
}
DataBufferState DataBuffer::FullBufferWithData()
{
char c;
for(unsigned int i=0;i<maxBufferSize;++i)
{
c = file.get();
if(file.eof()) break;
buffer += c;
}
if(file.eof())
return DATABUFFER_EOF;
return DATABUFFER_OK;
}
string DataBuffer::GetDataBuffer()
{
string buf = buffer;
buffer.clear();
return buf;
}
BufferWriter.h (Save uncompressed data into file):
#ifndef BufferWriter_h
#define BufferWriter_h
#include <string>
#include <fstream>
class BufferWriter
{
std::string filename;
std::fstream file;
public:
BufferWriter(const std::string& filename_);
~BufferWriter();
bool OpenFile(const std::string& filename, bool appending);
void SendBufferToFile(std::string& buffer);
};
#endif
BufferWriter.cpp
#include "BufferWriter.h"
using namespace std;
BufferWriter::BufferWriter(const string& filename_)
{
filename = filename_;
OpenFile(filename.c_str(),false);
file.close();
}
BufferWriter::~BufferWriter()
{
file.close();
}
bool BufferWriter::OpenFile(const string& filename, bool appending)
{
if(appending)
file.open(filename.c_str(),ios::out | ios::app);
else
file.open(filename.c_str(),ios::out);
if(!file.is_open())
return false;
return true;
}
void BufferWriter::SendBufferToFile(string& buffer)
{
OpenFile(filename,true);
file.write(buffer.c_str(),buffer.size());
file.close();
}
Can you give me some hints how to improve code for input and output mechanisms?
Assume that I have class presented below, how to use istream or iterators to fill buffer with data from file or standard input. What classes from std or boost? What parameters? Somelike to support definition of class with this functionality.
[EDIT]:
#ifndef StreamBuffer_h
#define StreamBuffer_h
#include <string>
using namespace std;
enum DataBufferState
{
DATABUFFER_OK = 0,
DATABUFFER_EOF = 1
};
// gzip plik
// type plik | gzip -d
// gzip -d plik.gz
// gzip -dc plik.gz
// Parametr konstruktora to strumien z ktorego chcemy czytac i dlugosc bufora
class StreamBuffer
{
int maxBufferSize;
std::string buffer;
StreamBuffer(int maxBuffSize)
{
SetMaxBufferSize(maxBuffSize);
}
~StreamBuffer()
{
}
void SetMaxBufferSize(unsigned int maxBuffSize)
{
maxBufferSize = maxBuffSize;
}
DataBufferState FullBufferWithData()
{
// What to use what to do in this method to read part of file or standard char input to buffer?
}
std::string GetDataBuffer()
{
return buffer;
}
};
#endif
[EDIT2]:
I want to do the same thing as in this thread: Read from file or stdin, but in C++.
In general you read input from a source and write it to a sink. The simplest case is when you simply write what you read. You, however, want to apply a transformation (or filter) to the data that you read. Seeing as you're after "the c++ way," I'd suggest taking a look at boost::iostreams which abstracts the task in terms of sources/sinks.
Boost defines an abstract source by:
struct Source {
typedef char char_type;
typedef source_tag category;
std::streamsize read(char* s, std::streamsize n)
{
// Read up to n characters from the input
// sequence into the buffer s, returning
// the number of characters read, or -1
// to indicate end-of-sequence.
}
};
And sinks are defined in a similar way (with a write instead of a read, of course). The benefit of this is that the details of the source/sink is irrelevant - you can read/write to file, to a network adapter, or whatever, without any structural changes.
To apply filters I'd again suggest looking at boost::iostreams, although they do abstract a lot which somewhat complicates implementation..

istream object don't read any char

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.