Home » U++ Library support » U++ Library : Other (not classified elsewhere) » Polymorphic XML
Polymorphic XML [message #24336] |
Mon, 11 January 2010 13:57 |
mdelfede
Messages: 1307 Registered: September 2007
|
Ultimate Contributor |
|
|
I need to save/restore a class hyerarchy to/from stream; the result should be something like that :
<Pages>
<Page class = "myclass">
....here object content, as per Xmlize
</Page>
</Pages>
I already have a class factory able to create class by name, but I don't know how to put all together. In particular, I need to work with streams, not files; the code should be able to read/write from the current byte of stream, and leave it positioned on next data.
Assuming 'Page' is my hierarchy base class, it should be :
Page *CreatePage(const char *className); <== DONE
Array<String> GetAvailablePages(void); <== DONE
class page
{
protected:
virtual void Xmlize(XmlIo xml) = 0; <== EASY, I think
public:
static Page *LoadStream(Stream &s); <== ???
virtual bool SaveStream(Stream &s); <== ???
}
Any hint on how to do it ?
Ciao
Max
|
|
|
Re: Polymorphic XML [message #24349 is a reply to message #24336] |
Mon, 11 January 2010 20:26 |
mdelfede
Messages: 1307 Registered: September 2007
|
Ultimate Contributor |
|
|
Well, got it !
Here the relevant code :
Panel containing pages, which are derived from 'Page' base class,
xmlize contained pages and restore page list on load :
void JobPane::Xmlize(XmlIO xml)
{
xml
("Pages", pages)
;
if(xml.IsLoading())
{
for(int i = 0; i < pages.GetCount(); i++)
pageList.Add(pages[i].GetPosition() + "-" + pages[i].GetDescription());
pageList.SetCursor(pageList.GetCount()-1);
}
}
Base class definition, relevant parts :
// Page base class -- inside each jobpane along with pagelist
class Page : public ParentCtrl
{
friend void __SetPageClass(Page *page, const char *className);
private:
const char *className;
protected:
..................
public:
typedef Page CLASSNAME;
// constructor
Page();
// gets ascii name of page class
String GetClassName(void) { return className; }
.................
// xml i/o
virtual void Xmlize(XmlIO xml) {}
}; // END Class Page
Note, here Xmlize does nothing, as the base class is just empty.
Could do more, indeed.
Now the polymorphic array of pages :
class Pages : public Array<Page>
{
public:
void Xmlize(XmlIO xml);
};
And the xmlize part for Pages class, which does the trick :
// xmlize pages
void Pages::Xmlize(XmlIO xml)
{
if(xml.IsStoring())
{
for(int i = 0; i < GetCount(); i++)
{
Page &page = operator[](i);
String tag = page.GetClassName();
::Xmlize(xml.Add(tag), page);
}
}
else
{
Clear();
for(int i = 0; i < xml->GetCount(); i++)
{
if(xml->Node(i).IsTag())
{
String tag = xml->Node(i).GetTag();
Page *page = CreatePage(tag);
if(page)
{
page->Xmlize(xml.At(i));
Add(page);
}
}
}
}
}
CreatePage(const String &className) is the class factory, which creates an empty Page-derived class of type 'className'
Here the files, PageFactory.h
class Page;
void __LamelRegisterPage(const char *className, Page *(*Create)(void), const char *desc, int index);
void __SetPageClass(Page *page, const char *className);
#define LAMELL_REGISTER(pageClass, pageDesc, pageIndex) \
Page *__##pageClass##_Create(void) \
{ \
Page *page = (Page *)new pageClass; \
__SetPageClass(page, #pageClass); \
return page; \
} \
INITBLOCK { \
__LamelRegisterPage(#pageClass, __##pageClass##_Create, pageDesc, pageIndex);\
}
Page *CreatePage(const char *className);
Page *CreatePage(int idx);
Array<String> GetAvailablePages(void);
And PageFactory.cpp (important part) :
struct __LamelPageFactory
{
// class creation
Page *(*Create)(void);
// class name
String className;
// page description
String desc;
// page 'index' used to sort
// pages in list
int index;
};
typedef ArrayMap<String, __LamelPageFactory> __LamelPageArray;
__LamelPageArray &LamelPageFactory()
{
static One<__LamelPageArray> pages;
if(!pages)
pages = new __LamelPageArray;
return *pages;
}
void __LamelRegisterPage(const char *className, Page *(*Create)(void), const char *desc, int index)
{
__LamelPageArray &pages = LamelPageFactory();
__LamelPageFactory *page = new __LamelPageFactory;
page->Create = Create;
page->className = className;
page->desc = desc;
page->index = index;
pages.Add(className, page);
}
void __SetPageClass(Page *page, const char *className)
{
page->className = className;
}
Page *CreatePage(const char *className)
{
__LamelPageArray &pages = LamelPageFactory();
__LamelPageFactory &f = pages.Get(className);
Page *p = f.Create();
// update units upon page creation
globalSettings().PropagateUnitsChanges();
return p;
}
......................
You must register each Page derived class (in its source file) with this :
LAMELL_REGISTER(Trave, t_("Simply supported beam"), 10);
Where 'Trave' is the derived class type, the text is a description and the number is just an index (both non-important, just part of my app)
As a note, each Page derived class MUST define Xmlize() AND call
it's base class Xmlize function; as example, here :
class Trave : public WithTraveLayout<Page>
{
int test;
public:
typedef Trave CLASSNAME;
Trave();
// xml i/o
void Xmlize(XmlIO xml) { this->Page::Xmlize(xml); xml("test", test);}
};
With all that stuff, from 'JobPane' class you can load/save it's pages simply with:
// read/saves job on file
bool JobPane::Load(String const &fileName)
{
return LoadFromXMLFile(*this, fileName);
}
bool JobPane::Save(String const &fileName)
{
return StoreAsXMLFile(*this, "LAMELL", fileName);
}
Hope it can be useful for somebody
Ciao
Max
|
|
|
|
Re: Polymorphic XML [message #24358 is a reply to message #24353] |
Tue, 12 January 2010 10:42 |
mdelfede
Messages: 1307 Registered: September 2007
|
Ultimate Contributor |
|
|
luzr wrote on Tue, 12 January 2010 08:05 |
__LamelPageArray &LamelPageFactory()
{
static One<__LamelPageArray> pages;
if(!pages)
pages = new __LamelPageArray;
return *pages;
}
Why not
__LamelPageArray &LamelPageFactory()
{
static __LamelPageArray pages;
return pages;
}
?
|
Yep, better... and it could be made a static member of Page class, also.
Quote: |
Also, I believe that
void CreateClassInstance(One<BaseClass>& x);
(Note that you can do x.Create<DerivedClass>())
or even in some cases
void CreateClassInstance(Array<BaseClass>& x);
is cleaner class factory interface. (But depends on your taste).
Mirek
|
mhhh... I don't understand your point. I need the class creation at runtime by name, so an ascii string, not a type.
Anyways, thinking about it, all the stuff could be polished a bit, also using callbacks, maybe, instead of function pointers.
BTW, yesterday I was in a hurry because the problem was starting boring me, so I solved it quick and dirty
Ciao
Max
|
|
|
|
Goto Forum:
Current Time: Mon Apr 29 17:26:54 CEST 2024
Total time taken to generate the page: 0.01553 seconds
|