Solved stuff

Published January 09, 2008
Advertisement
Found a solution to yesterday's problem by building a little class that wraps a std::list::iterator but dereferences automatically with the -> and * operators:

class node{public:	class iterator	{	private:		std::list::iterator it;	public:		typedef std::list::iterator::difference_type difference_type;		typedef std::list::iterator::iterator_category iterator_category;		typedef node* pointer;		typedef node& reference;		typedef node value_type;		explicit iterator(std::list::iterator i) : it(i) { }		bool operator==(const iterator &i) const { return it==i.it; }		bool operator!=(const iterator &i) const { return it!=i.it; }		pointer operator->(){ return *it; }		reference operator*(){ return *(*it); }				iterator &operator++(){ ++it; return *this; }		iterator operator++(int){ iterator i(*this); ++it; return i; }		iterator &operator--(){ --it; return *this; }		iterator operator--(int){ iterator i(*this); --it; return i; }	};    // rest of node class snipped, including const_iterator version of above};


So now the xcs::node class contains a std::list to avoid yesterday's problems, and the node class typedefs iterator_base as node::iterator.

So the node's begin() and end() methods return the above iterator, constructed simply from the node's std::list<>'s begin() and end() methods.

So you are back to:

for(xcs::node::iterator i=n.begin();i!=n.end();++i) std::cout << i->name << "\n";


I've also implemeneted an include system in the xcs files so that you can include one xcs file in another.

Another major improvement is the abolishment of a seperate xcs::tree class. Now, you just do:

xcs::node root;xcs::open("sample.xcs",root);


You can even open a file into a node of an existing xcs tree. Support has also been added for manually building trees in code with node::add() and node::remove() methods.

In case it is of interest, here's the complete xcs library:

xcs.h
#ifndef xcs_H#define xcs_H#include #include #include #include namespace xcs{class exception{protected:	std::string text;public:	exception(const std::string &s) : text(s) { }		std::string parameter() const { return text; }	virtual std::string what() const=0;};class bad_name : public exception{public:	bad_name(const std::string &s) : exception(s) { }		virtual std::string what() const { return "Name not found - "+text; }};class bad_conversion : public exception{public:	bad_conversion(const std::string &s) : exception(s) { }		virtual std::string what() const { return "Invalid conversion - "+text; }};class bad_path : public exception{public:	bad_path(const std::string &s) : exception(s) { }		virtual std::string what() const { return "Unable to open - "+text; }};class bad_syntax : public exception{private:	std::string path; size_t line;public:	bad_syntax(const std::string &p,size_t n,const std::string &s) : exception(s),path(p),line(n) { }		virtual std::string what() const;};class node{public:	class iterator	{	private:		std::list::iterator it;	public:		typedef std::list::iterator::difference_type difference_type;		typedef std::list::iterator::iterator_category iterator_category;		typedef node* pointer;		typedef node& reference;		typedef node value_type;		explicit iterator(std::list::iterator i) : it(i) { }		bool operator==(const iterator &i) const { return it==i.it; }		bool operator!=(const iterator &i) const { return it!=i.it; }		pointer operator->(){ return *it; }		reference operator*(){ return *(*it); }				iterator &operator++(){ ++it; return *this; }		iterator operator++(int){ iterator i(*this); ++it; return i; }		iterator &operator--(){ --it; return *this; }		iterator operator--(int){ iterator i(*this); --it; return i; }	};	class const_iterator	{	private:		std::list::const_iterator it;	public:		typedef std::list::const_iterator::difference_type difference_type;		typedef std::list::const_iterator::iterator_category iterator_category;		typedef const node* pointer;		typedef const node& reference;		typedef node value_type;		explicit const_iterator(std::list::const_iterator i) : it(i) { }		bool operator==(const const_iterator &i) const { return it==i.it; }		bool operator!=(const const_iterator &i) const { return it!=i.it; }		pointer operator->(){ return *it; }		reference operator*(){ return *(*it); }				const_iterator &operator++(){ ++it; return *this; }		const_iterator operator++(int){ const_iterator i(*this); ++it; return i; }		const_iterator &operator--(){ --it; return *this; }		const_iterator operator--(int){ const_iterator i(*this); --it; return i; }	};private:	node *pa; std::list ch;public:	node(const std::string &n="",const std::string &v="") : name(n),value(v),pa(0) { }	~node();		node &add(const std::string &n,const std::string &v="");	void remove(const std::string &n);	bool contains(const std::string &n) const;	node &operator[](const std::string &n);	const node &operator[](const std::string &n) const;	iterator begin(){ return iterator(ch.begin()); }	iterator end(){ return iterator(ch.end()); }	const_iterator begin() const { return const_iterator(ch.begin()); }	const_iterator end() const { return const_iterator(ch.end()); }	bool is_root() const { return(pa ? false : true); }	std::string full_name() const;	template<class T> bool is() const;	template<> bool is() const { return true; }	template<> bool is<char*>() const { return false; }	template<> bool is<const char*>() const { return false; }		template<class T> T as() const;	template<> std::string as() const { return value; }		template<class T> void assign(const T &t){ std::stringstream s; s << t; value=s.str(); }	std::string name,value;};template<class T> bool node::is() const{	std::stringstream ss(value); T t;	ss >> t; if(ss.fail()) return false; std::string s; ss >> s; return ss.fail();}template<class T> T node::as() const{	if(!is()) throw bad_conversion(value);	std::stringstream ss(value); T t=T(); ss >> t; return t;}void open(const std::string &path,node &root);}#endif


xcs.cpp
#include "xcs.h"#include #include #include std::string xcs::bad_syntax::what() const{	std::ostringstream os; os << "Invalid syntax in " << path << " at line " << line << " - " << text; return os.str();}xcs::node::~node(){	for(std::list::iterator i=ch.begin();i!=ch.end();++i) delete *i;}xcs::node &xcs::node::add(const std::string &n,const std::string &v){	node *a=new node(n,v); a->pa=this; ch.push_back(a); return *a;}void xcs::node::remove(const std::string &n){	std::list::iterator i=ch.begin();	while(i!=ch.end())		{		if((*i)->name==n){ delete (*i); i=ch.erase(i); }		else ++i;		}}bool xcs::node::contains(const std::string &n) const{	for(std::list::const_iterator i=ch.begin();i!=ch.end();++i) if((*i)->name==n) return true;	return false;}xcs::node &xcs::node::operator[](const std::string &n){	for(std::list::iterator i=ch.begin();i!=ch.end();++i) if((*i)->name==n) return *(*i);	throw bad_name(std::string(pa ? full_name()+"." : "")+n);}const xcs::node &xcs::node::operator[](const std::string &n) const{	for(std::list::const_iterator i=ch.begin();i!=ch.end();++i) if((*i)->name==n) return *(*i);	throw bad_name(std::string(pa ? full_name()+"." : "")+n);}std::string xcs::node::full_name() const{	std::stack st;		const node *p=pa;	while(p && p->pa)		{		st.push(p->name); p=p->pa;		}			std::string r;	while(!st.empty())		{		r+=st.top()+"."; st.pop();		}	return r+name;}namespace{bool is_absolute_path(const std::string &s){	if(s.length()>0)		{		if(s[0]=='\\' || s[0]=='/') return true;		}	if(s.length()>3)		{		if(s.substr(1,2)==":\\" || s.substr(1,2)==":/") return true;		}	return false;}class source{private:	std::ifstream is; int last;public:	source(const std::string &p) : path(p),line(1) { is.open(path.c_str()); }	bool fail() const { return is.fail(); }	int get(){ int c=is.get(); if(c=='\n') ++line; return last=c; }	void unget(){ is.unget(); if(last=='\n') --line; }	std::string path; size_t line;};inline std::string ch_to_str(char c){ std::ostringstream os; os << c; return os.str(); }class scanner{public:	enum types { eof,id,lb,rb,assign,semi,rwinclude };private:	int skip();	types strlit();	types next();	std::stack src;public:	scanner(){ }	~scanner(){ while(!src.empty()) pop(); }	void push(const std::string &path);	void pop(){ if(!src.empty()){ delete src.top(); src.pop(); } }	scanner &operator()(bool get){ if(get) type=next(); return *this; }	scanner &operator()(types t,bool get){ (*this)(get); if(t!=type) error(text); return *this; }	void error(const std::string &s){ throw xcs::bad_syntax(src.top()->path,src.top()->line,s); }	types type;	std::string text;	std::string rootdir;};int scanner::skip(){	int c=src.top()->get(); while(std::isspace(c)) c=src.top()->get(); return c;}scanner::types scanner::strlit(){	int c=src.top()->get();	while(c!='\"')		{		if(c=='\n' || c==EOF) error("non-terminated string constant");				if(c=='\\')			{			c=src.top()->get();						switch(c)				{				case 'n': c='\n'; break;				case 't': c='\t'; break;				}			}				text+=char(c); c=src.top()->get();		}	c=skip();	if(c=='\"') return strlit();	else		{		src.top()->unget();		return id;		}}scanner::types scanner::next(){	text=""; int c=skip();		if(c==EOF){ text="EOF"; return eof; }	if(c=='{'){ text+=char(c); return lb; }	if(c=='}'){ text+=char(c); return rb; }	if(c=='='){ text+=char(c); return assign; }	if(c==';'){ text+=char(c); return semi; }	if(c=='\"') return strlit();	if(std::isalnum(c))		{		while(std::isalnum(c) || c=='_'){ text+=char(c); c=src.top()->get(); }		src.top()->unget();		if(text=="include") return rwinclude;				return id;		}	error(ch_to_str(c));	return eof;}void scanner::push(const std::string &path){	source *s=new source(path); if(s->fail()){ delete s; throw xcs::bad_path(path); }	src.push(s);}void block_construct(scanner &sc,xcs::node &root,bool get);void construct(scanner &sc,xcs::node &root,bool get);void load_from_file(scanner &sc,xcs::node &root,const std::string &path);void id_construct(scanner &sc,xcs::node &root,bool get){	sc(sc.id,get); std::string name=sc.text,value=sc.text;	sc(true);	if(sc.type==sc.assign)		{		sc(sc.id,true); value=sc.text; sc(true);		}	if(root.contains(name)) sc.error(name+" duplicated in scope");	xcs::node &n=root.add(name,value);	switch(sc.type)		{		case sc.lb  : block_construct(sc,n,false); break;		case sc.semi: sc(true); break;		default: sc.error(sc.text);		}}void block_construct(scanner &sc,xcs::node &root,bool get){	sc(sc.lb,get)(true);	while(sc.type!=sc.rb) construct(sc,root,false);		sc(true);}void include_construct(scanner &sc,xcs::node &root,bool get){	sc(sc.rwinclude,get)(sc.id,true); std::string path=sc.text; sc(sc.semi,true);	if(!is_absolute_path(path)) path=sc.rootdir+path;	load_from_file(sc,root,path);	sc(true);}void construct(scanner &sc,xcs::node &root,bool get){	sc(get);		switch(sc.type)		{		case sc.rwinclude: include_construct(sc,root,false); break;		case sc.id       : id_construct(sc,root,false); break;		default: sc.error(sc.text);		}}void load_from_file(scanner &sc,xcs::node &root,const std::string &path){	sc.push(path);		sc(true); while(sc.type!=sc.eof) construct(sc,root,false);		sc.pop();}}void xcs::open(const std::string &path,node &root){	scanner sc;	if(is_absolute_path(path)) sc.rootdir=path.substr(0,path.find_last_of("\\/"))+"\\";		load_from_file(sc,root,path);}
Previous Entry Hmm
Next Entry Direct3D GUI
0 likes 4 comments

Comments

Drilian
Well played, good sir!
January 09, 2008 07:18 PM
dcosborn
You appear to have reinvented Boost's indirect_iterator.
January 09, 2008 07:50 PM
Aardvajk
Quote:Original post by dcosborn
You appear to have reinvented Boost's indirect_iterator.


As I was writing it, I kind of figured that boost probably provided something like that.

My half-arsed justification is that one of the aims of xcs is to be as lightweight as possible and "just work" within standard C++.

Also, given that I'm reinveting XML anyway, it seems only appropriate [smile].
January 10, 2008 01:14 AM
dcosborn
I've reimplemented a lot of Boost functionality for the sake of avoiding dependencies as well, including an indirect_iterator [smile]. I think this would happen less persistently if the various parts of Boost were self-contained, so that you could rip them out and include them directly in your own codebase.
January 10, 2008 06:42 PM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement
Advertisement