• entries
    743
  • comments
    1924
  • views
    580390

Solved stuff

Sign in to follow this  

146 views

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



Sign in to follow this  


4 Comments


Recommended Comments

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].

Share this comment


Link to comment
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.

Share this comment


Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now