https://github.com/graetz23/paxcc
A pretty simple C++ Data Passenger - building object trees that can write and may read from XML
https://github.com/graetz23/paxcc
fast handy object-oriented object-oriented-programming pointer pointers pretty simple tag tree tree-structure trees value xml
Last synced: 2 months ago
JSON representation
A pretty simple C++ Data Passenger - building object trees that can write and may read from XML
- Host: GitHub
- URL: https://github.com/graetz23/paxcc
- Owner: graetz23
- License: other
- Created: 2025-02-01T14:44:56.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2025-11-22T17:01:42.000Z (4 months ago)
- Last Synced: 2025-11-22T19:04:28.928Z (4 months ago)
- Topics: fast, handy, object-oriented, object-oriented-programming, pointer, pointers, pretty, simple, tag, tree, tree-structure, trees, value, xml
- Language: C++
- Homepage: https://github.com/graetz23/paxcc
- Size: 129 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# paxcc
_A pretty simple C++ Data Passenger_; PAX is an airborne code or peace in latin.
## Introduction
The PAX class is a C++ written _double linked object tree_.
It is an enabler for parsing any kind of - hierarchically - structured data; e.g. _extensible markup language_ (XML).
A simple SAX like interface is available for parsing to a PAX object tree.
A _Pax_ objects can:
- store a _tag_ as std::string,
- store a _value_ as std::string,
- add multiple _attribute_ as a pair of _tag_ and _value_,
- add multiple _Pax_ object as _children_ of the current,
- generate themselves recursively to XML,
- write themselves to XML using the _Writer_,
- parse themselves by XML to a _double linked object tree_ using the _Reader_.
PAXCC is written in C++11 currently (may switch to C++98).
There are not further dependencies to other libraries or framework.
It is ideal for standalone hierarchical structured data handling using XML.
## HowTo
How to use _Pax_, build object tree, and what can go wrong.
### Creating, Setting, Changing, and Deleting _Pax_ trees
#### Creating a Pax Object
Creating _Pax_ objects:
```C++
Pax* pax1 = new Pax("Bob", "Dylon");
Pax *pax2 = new Pax();
pax2->Tag("Bob");
pax2->Val("Dylon");
Pax* pax3 = new Pax("Bob");
```
#### Adding Attributes to a Pax Object
Adding _Attributes_ to _Pax_ objects:
```C++
Pax* pax1 = new Pax("Bob", "Dylon");
pax1->Attrib()->add("plays", "guitar");
pax1->Attrib()->add("sings", "songs");
```
#### Adding Children to a Pax Object
Adding _Children_ to _Pax_ objects:
```C++
Pax* pax1 = new Pax("Bob", "Dylon");
Pax* pax2 = new Pax("Dolly", "Parton");
Pax* pax3 = new Pax("Johnny", "Cash");
pax1->Child()->add(pax2); // pax2 as child of pax1
pax2->Child()->add(pax3); // pax3 as child of pax2
```
#### Deleting Pax Object
Deleting any _Pax_ object by recursive destructor:
```C++
Pax* root = new Pax("Bob", "Dylon");
Pax* pax2 = new Pax("Dolly", "Parton");
Pax* pax3 = new Pax("Johnny", "Cash");
root->Child()->add(pax2); // pax2 as child of pax1
pax2->Child()->add(pax3); // pax3 as child of pax2
delete root; // runs recursively ..
```
#### Generating XML from a Pax Object
Generating XML from _Pax_ and writing to _std::out_:
```C++
Pax* pax1 = new Pax("Bob", "Dylon");
pax1->Attrib()->add("plays", "guitar");
pax1->Attrib()->add("sings", "songs");
Pax* pax2 = new Pax("Dolly", "Parton");
pax2->Attrib()->add("sings", "songs");
pax2->Attrib()->add("plays", "country guitar");
Pax* pax3 = new Pax("Johnny", "Cash");
pax3->Attrib()->add("plays", "guitar");
pax3->Attrib()->add("sings", "country songs");
Pax* pax4 = new Pax("John", "Denver");
pax4->Attrib()->add("sings", "country songs");
pax4->Attrib()->add("plays", "country guitar");
pax1->Child()->add(pax2); // pax2 as child of pax1
pax2->Child()->add(pax3); // pax3 as child of pax2
pax2->Child()->add(pax4); // pax3 as child of pax2
std::string xml = pax1->XML();
std::cout << xml << std::endl << std::flush;
delete pax1; // runs recursively
```
The XML of above example looks like:
```XML
Cash
Denver
````
#### Access Pax Child Objects
Retrieving _Pax_ from above example:
```C++
// assume we have build above Pax object tree ..
Pax* pax2_ = pax1->Child("Dolly");
Pax* pax3_ = pax2->Child("Johnny"); // from pax2
Pax* pax4_ = pax1->Child("Dolly")->Child("John"); // chained
```
#### Accessing Tag and Value
Retrieving _Tag_ and _Value_:
```C++
Pax* pax = pax1->Child("Dolly")->Child("John"); // chained
std::string tag = pax->Tag();
std::string val = pax->Val();
```
#### Accessing Tag and Value of Attributes
Retrieving _Attributes_ _Tag_ and _Value_:
```C++
Pax* pax = pax1->Child("Dolly")->Child("John"); // chained
std::string attribTag = pax->Attrib("sings")->Tag();
std::string attribVal = pax->Attrib("sings")->Val();
```
### What can go wrong
#### Deleting treed _Pax_ outside the tree
The problem in pointered C++ is, that there is no garbage collector that
collects any memory address and holds a pointer for automatic deletion on it.
Therefore, within _Pax_ it is possible to delete objects not recursively by
the tree, but outside the tree. This leads to a memory gap, where the object
tree still holds the pointer but the object is deleted. The recursive calling
methods stumbles over it an crashes off course. Here is an easy example to
produce this error:
```C++
Pax* root = new Pax("Bob", "Dylon");
Pax* pax2 = new Pax("Dolly", "Parton");
Pax* pax3 = new Pax("Johnny", "Cash");
root->Child()->add(pax2); // pax2 as child of pax1
pax2->Child()->add(pax3); // pax3 as child of pax2
delete pax2; // the error one can make ..
delete root; // runs recursively .. and crashes!
```
The reason is simple. The Higher _Pax_ holds in his child list still the pointer
to the address of the delete _pax2_. As soon as this list of child is iterated,
the crash is there. There is no known chance to overcome this issue in ANSI C++.
#### Changing _Tags_ and _Values_ of a treed _Pax_
If an _already treed Pax_ is retrieved and the _Tag_ or the _Value_ is changed,
the object tree is not updated. Therefore, the object tree holds in the matching
_Pax_ of higher hierarchy still the old _Tag_ to the address of the changed _Pax_.
The following example details this issue:
```C++
Pax* root = new Pax("Bob", "Dylon");
Pax* pax2 = new Pax("Dolly", "Parton");
Pax* pax3 = new Pax("Johnny", "Cash");
root->Child()->add(pax2); // pax2 as child of pax1
pax2->Child()->add(pax3); // pax3 as child of pax2
Pax* pax = root->Child("Dolly")->Child("Johnny");
pax->Tag("John"); // updating Tag
pax->Val("Denver"); // updating Value
Pax* pax2_ = root->Child("Dolly"); // remeber Dolly knows Johnny
Pax* pax3_ = pax2->Child("Johnny"); // exists
std::string tag = pax3_.Tag(); // John was stored
std::string val = pax3_.Val(); // Denver was stored
// and
Pax* pax3__ = pax2->Child("John"); // null pointer
delete root; // runs recursively ..
```
This leads to the fact, that if one wants to change data on treed _Pax_,
he has to also deal with the object tree. Therefore, retrieving the object
above, deleting the _Pax_ from object tree, changing it, and adding it again.
### Input and Output with Pax Objects
#### Writing a Pax Object to Console
Writing a _Pax_ Object to Console:
```C++
// build some Pax object named pax1
std::string xml = pax1->XML(); // generate XML from object tree ..
std::cout << xml << std::endl; // print out
```
#### Writing Pax Object to File
Writing a _Pax_ Object to file:
```C++
// build some Pax object named pax1
PaxWriter writer;
writer.write("example_output.xml", pax1); // write to file
delete pax1; // working recursively well on first run ..
```
### Reading a Pax Object from File
Reading a _Pax_ Object from File:
```C++
PaxReader reader;
Pax* pax1 = reader.read("example_output.xml"); // read from file
delete pax1; // working recursively well on first run ..
```
#### Reading some Pax Objects from Folder
Reading several _Pax_ Objects from a folder:
```C++
PaxReader reader;
std::vector paxList = reader.readAll("./folderPath"); // read all from folder
for(int i = 0; i < paxList.size(); i++) {
Pax* pax1 = paxList[i];
delete pax1; // working recursively well on first run ..
} // for
```
#### Reading some Pax Objects from Folder for certain File Ending
Reading several _Pax_ Objects from a folder by a given file ending:
```C++
PaxReader reader;
std::vector paxList = reader.readAll("./folderPath", ".xsd"); // read all from folder
for(int i = 0; i < paxList.size(); i++) {
Pax* pax1 = paxList[i];
delete pax1; // working recursively well on first run ..
} // for
```
## Building the PAXCC
For building PAXCC the [CMake - Software Build System](https://cmake.org/) is available.
### Cmake Build Tooling
For modern CMake version install _CMake_ and [Ninja](https://ninja-build.org/).
#### Build PAXCC
For generating the _ninja build files_ with _CMake_ and building binaries via _ninja_:
```bash
./build.sh
```
If you want to use classical make, you can edit the _build.sh_ bash script easily.
#### Clean PAXCC
For cleaning up all compiled objects:
```bash
./clean.sh
```
To remove the build folder:
```bash
rm -rf build/
```
## Closings
Have fun :-)