Last active
February 18, 2019 21:33
-
-
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/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
# 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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