The primary purpose of usercode is to supply a tool that shall add the ability to keep usercode inside generated code.
The code-generator must produce some fancy comments inside the generated code in order to use usercode. Every text surrounded by such comments may be extracted by usercode and reapplied, after the generator has recreated the code.
Let's suppose, that we have a codegenerator, that has produced a file smp.hxx:
#include <something.h>
//UC(smp..include.includes
//)
class smp : public parent {
protected:
// attributes of class smp
int _number;
//UC(smp..include.attributes
//)
public:
// constructor / destructor
smp();
~smp();
// std interface methods
virtual const char* get_attribute(t_AttrId) const;
virtual void set_attribute(t_AttrId, const char*);
virtual void connect (t_AttrId, void*, int);
virtual void disconnect (t_AttrId, void*, int);
virtual void* get_connected(t_AttrId, int) const;
virtual void* num_connected(t_AttrId) const;
virtual t_ClassDesc ClassDesc() const { return __desc(); }
static t_ClassDesc __desc();
// access methods
int number() const { return _number; }
void number(int value) { _number = value; }
//UC(smp.number.include.methods
//)
// additional unrelated methods
//UC(smp..include.methods
//)
}
The user decides to add some methods manually. Thus he has to change the generated file.
#include <something.h>
//UC(smp..include.includes
#include <iostreams.h>
//)
class smp : public parent {
protected:
// attributes of class smp
int _number;
//UC(smp..include.attributes
//)
public:
// constructor / destructor
smp();
~smp();
// std interface methods
virtual const char* get_attribute(t_AttrId) const;
virtual void set_attribute(t_AttrId, const char*);
virtual void connect (t_AttrId, void*, int);
virtual void disconnect (t_AttrId, void*, int);
virtual void* get_connected(t_AttrId, int) const;
virtual void* num_connected(t_AttrId) const;
virtual t_ClassDesc ClassDesc() const { return __desc(); }
static t_ClassDesc __desc();
// access methods
int number() const { return _number; }
void number(int value) { _number = value; }
//UC(smp.number.include.methods
//)
// additional unrelated methods
//UC(smp..include.methods
void print() const { printOn(cout); }
printOn(ostream&) const;
//)
}
Now the code does, what the user wants, but as soon, as we rerun the code generator, all user supplied changes get overwritten. Thus the code generator must not be called again. This is not a good idea, since perhaps some mayor changes in design may force a generator call
At that moment usercode can back up the code, the user has supplied in the file, and re-apply it after the code generator has overwritten the file. (e.g.)
% usercode -ncxx -dbUserSuppliedCodeDB -r smp.hxx smp.cxx % call-the-code-generator % usercode -ncxx -dbUserSuppliedCodeDB -u smp.hxx smp.cxx
Current disadvantages arise from the fact, that there ist no clear interface between code generator and usercode. (usercode should be integrated in the generator framework for this.)
usercode only backs up, what's in between comments consisting of "UC(" and ")". Thus the code generator must write out such comments. This is unfortunately not true for most available code-generators. Since however for most generators source code is available, user may easily add the functionality of generating such comments.
It is possible to go around this problem if we keep a complete backup of the initially generated code somewhere, and thus extract all differences between user changed code and initial code (e.g. find then with diff) and after re-generation re-apply those changes (e.g. via patch). If however parts of the generated code change or are even omitted, reapplication tends to get a bit more difficult.
If user changes names e.g. of classes of of attributes in the generator framework, those changes are not propagated to usercode; thus there is no possibility to re-apply usercode to the modified components.
We might solve that problem by supplying a "rename" mechanism; but this will only work, if user keeps track of such changes on his own (again missing integration). Furthermore user it must be clear the complete changed path would have to be given. (e.g. renaming the number to no attribute in the above example would make us call something like rename smp.number smp.no.) Thus the generated keys inside the code must have some std appearance (e.g. class.attribute.purpose...) to make such a mechanism work.
The complete package can be downloaded at Sources subdirectory