Skip to content

Instantly share code, notes, and snippets.

@roxlu
Last active February 18, 2019 21:33
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save roxlu/6610811 to your computer and use it in GitHub Desktop.
Save roxlu/6610811 to your computer and use it in GitHub Desktop.
Tiny, fast and clean configuration file parser using rapidxml with example of a to be released streaming video library http://rapidxml.sourceforge.net/
#include <iostream>
#include <fstream>
#include <rapidxml.hpp>
#include "Config.h"
Config::Config() {
}
Config::~Config() {
}
bool Config::load(std::string filepath) {
std::ifstream ifs(filepath.c_str(), std::ios::in);
if(!ifs.is_open()) {
printf("error: cannot open the xml from: `%s`\n", filepath.c_str());
return false;
}
std::string str( (std::istreambuf_iterator<char>(ifs)) , std::istreambuf_iterator<char>());
if(!str.size()) {
printf("error: empty xml.\n");
return false;
}
try {
doc.parse<parse_full>((char*)str.c_str());
}
catch(...) {
printf("error: error while parsin xml.\n");
return false;
}
return true;
}
/*
# Config
Simple XML config file parser.
_Pseudo code example_
````c++
#if 0
<?xml version="1.0" encoding="UTF-8"?>
<config>
<width>400</width>
<height>500</height>
<fps>60</fps>
</config>
#endif
Config cfg;
if(!cfg.load("myconfig.xml")) {
::exit(EXIT_FAILURE);
}
try {
xml_node<>* n = cfg.getNode("config");
uint16_t width = cfg.getU16(n, "width");
uint16_t height = cfg.getU16(n, "height");
uint16_t fps = cfg.getU16(n, "fps");
}
catch(ConfigException ex) {
printf("error: %s\n", ex->what());
::exit(EXIT_FAILURE);
}
````
# Todo
- handle cdata elements
- implement attributes
*/
#ifndef ROXLU_XML_CONFIG_H
#define ROXLU_XML_CONFIG_H
#include <sstream>
#include <string>
#include <vector>
#include <inttypes.h>
#include <rapidxml.hpp>
using namespace rapidxml;
struct ConfigException : public std::exception {
ConfigException(std::string m):m(m){}
~ConfigException() throw() {}
const char* what() const throw() { return m.c_str(); }
std::string m;
};
// ----------------------------------------------
class Config {
public:
Config();
~Config();
bool load(std::string filepath);
int8_t readS8(xml_node<>* parent, std::string path);
int16_t readS16(xml_node<>* parent, std::string path);
int32_t readS32(xml_node<>* parent, std::string path);
int64_t readS64(xml_node<>* parent, std::string path);
uint8_t readU8(xml_node<>* parent, std::string path);
uint16_t readU16(xml_node<>* parent, std::string path);
uint32_t readU32(xml_node<>* parent, std::string path);
uint64_t readU64(xml_node<>* parent, std::string path);
float readFloat(xml_node<>* parent, std::string path);
double readDouble(xml_node<>* parent, std::string path);
std::string readString(xml_node<>* parent, std::string path);
template<class T>
T read(xml_node<>* parent, std::string path) {
std::stringstream ss;
if(!getNodeValue(parent, path, ss)) {
throw ConfigException("error: node not found: " +path);
}
T result;
ss >> result;
return result;
}
bool parsePath(std::string& path, std::vector<std::string>& result);
xml_node<>* getNode(std::string path); /* get node from the "root" element */
xml_node<>* getNode(xml_node<>* parent, std::string path); /* get node from the given parent */
bool getNodeValue(xml_node<>* parent, std::string path, std::stringstream& ss);
private:
xml_document<> doc;
};
inline uint8_t Config::readU8(xml_node<>* parent, std::string path) {
return read<uint16_t>(parent, path); // when read as u8 it's converted to a char
}
inline uint16_t Config::readU16(xml_node<>* parent, std::string path) {
return read<uint16_t>(parent, path);
}
inline uint32_t Config::readU32(xml_node<>* parent, std::string path) {
return read<uint32_t>(parent, path);
}
inline uint64_t Config::readU64(xml_node<>* parent, std::string path) {
return read<uint64_t>(parent, path);
}
inline int8_t Config::readS8(xml_node<>* parent, std::string path) {
return read<int16_t>(parent, path); // when read as u8 it's converted to a char
}
inline int16_t Config::readS16(xml_node<>* parent, std::string path) {
return read<int16_t>(parent, path);
}
inline int32_t Config::readS32(xml_node<>* parent, std::string path) {
return read<int32_t>(parent, path);
}
inline int64_t Config::readS64(xml_node<>* parent, std::string path) {
return read<int64_t>(parent, path);
}
inline float Config::readFloat(xml_node<>* parent, std::string path) {
return read<float>(parent, path);
}
inline double Config::readDouble(xml_node<>* parent, std::string path) {
return read<double>(parent, path);
}
inline std::string Config::readString(xml_node<>* parent, std::string path) {
return read<std::string>(parent, path);
}
inline bool Config::getNodeValue(xml_node<>* parent, std::string path, std::stringstream& ss) {
xml_node<>* child = getNode(parent, path);
if(!child) {
return false;
}
ss << child->value();
return true;
}
// use a path string to get the child node of the parent
// e.g. video/width
inline xml_node<>* Config::getNode(xml_node<>* parent, std::string path) {
if(!parent) {
throw ConfigException("error: cannot getNode() because given parent is null.");
}
std::vector<std::string> els;
if(!parsePath(path, els)) {
throw ConfigException("error: cannot getNode() because we cannot find it.");
}
std::vector<std::string>::iterator it = els.begin();
xml_node<>* child = parent;
xml_node<>* prev_child = child;
while(it != els.end()) {
std::string el = *it;
prev_child = child;
child = child->first_node(el.c_str());
if(!child) {
throw ConfigException("error: cannot getNode() because we cannot find it: " +el);
}
++it;
}
return child;
}
inline xml_node<>* Config::getNode(std::string path) {
return getNode(&doc, path);
}
// splits on '/'
inline bool Config::parsePath(std::string& path, std::vector<std::string>& result) {
std::stringstream ss(path);
std::string item;
while(std::getline(ss, item, '/')) {
result.push_back(item);
}
return result.size();
}
#endif
<?xml version="1.0" encoding="UTF-8"?>
<videostreamer>
<streams>
<stream>
<id>0</id>
<server>
<rtmp_url>rtmp://localhost/flvplayback/livestream</rtmp_url>
</server>
<video>
<width>640</width>
<height>480</height>
<fps>25</fps>
</video>
<audio>
<samplerate>44100</samplerate>
<mode>2</mode>
<bitsize>2</bitsize>
<quality>6</quality>
<in_bitsize>3</in_bitsize>
<in_interleaved>0</in_interleaved>
</audio>
</stream>
</streams>
</videostreamer>
#include "DaemonConfig.h"
// ----------------------------------------------
StreamConfig::StreamConfig() {
reset();
}
StreamConfig::~StreamConfig() {
reset();
}
void StreamConfig::reset() {
id = 0;
rtmp_url.clear();
width = 0;
height = 0;
fps = 0;
samplerate = 0;
bitsize = 0;
in_bitsize = 0;
in_interleaved = 0;
}
void StreamConfig::print() {
printf("config.id: %d\n", id);
printf("config.rtmp_url: %s\n", rtmp_url.c_str());
printf("config.width: %d\n", width);
printf("config.height: %d\n", height);
printf("config.fps: %d\n", fps);
printf("config.samplerate: %d\n", samplerate);
printf("config.bitsize: %d\n", bitsize);
printf("config.quality: %d\n", quality);
printf("config.in_bitsize: %d\n", in_bitsize);
printf("config.in_interleaved: %d\n", in_interleaved);
}
// ----------------------------------------------
DaemonConfig::DaemonConfig() {
}
DaemonConfig::~DaemonConfig() {
for(std::map<uint32_t, StreamConfig*>::iterator it = configs.begin(); it != configs.end(); ++it) {
delete it->second;
}
configs.clear();
}
bool DaemonConfig::load(std::string filepath) {
if(!conf.load(filepath)) {
return false;
}
StreamConfig* sc = NULL;
try {
xml_node<>* streams = conf.getNode("videostreamer/streams");
for(xml_node<>* stream = streams->first_node(); stream; stream = stream->next_sibling()) {
sc = new StreamConfig();
sc->id = conf.readU32(stream, "id");
sc->width = conf.readU16(stream, "video/width");
sc->height = conf.readU16(stream, "video/height");
sc->fps = conf.readU16(stream, "video/fps");
sc->samplerate = conf.readU32(stream, "audio/samplerate");
sc->bitsize = conf.readU8(stream, "audio/bitsize");
sc->quality = conf.readU8(stream, "audio/quality");
sc->in_bitsize = conf.readU8(stream, "audio/in_bitsize");
sc->in_interleaved = conf.readU8(stream, "audio/in_interleaved");
sc->rtmp_url = conf.readString(stream, "server/rtmp_url");
sc->print();
configs[sc->id] = sc;
sc = NULL;
}
}
catch(ConfigException ex) {
printf("error: %s\n", ex.what());
if(sc) {
delete sc;
sc = NULL;
}
return false;
}
return true;
}
#ifndef ROXLU_VIDEOSTREAMER_CONFIG_H
#define ROXLU_VIDEOSTREAMER_CONFIG_H
#include "Config.h"
#include <map>
// ----------------------------------------------
struct StreamConfig {
StreamConfig();
~StreamConfig();
void reset();
void print();
uint32_t id;
/* server */
std::string rtmp_url;
/* video */
uint16_t width;
uint16_t height;
uint8_t fps;
/* audio */
uint32_t samplerate;
uint8_t bitsize;
uint8_t quality;
uint8_t in_bitsize;
uint8_t in_interleaved;
};
// ----------------------------------------------
class DaemonConfig {
public:
DaemonConfig();
~DaemonConfig();
bool load(std::string filepath);
private:
Config conf;
std::map<uint32_t, StreamConfig*> configs;
};
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment