views:

48

answers:

2

Hello I am using expat to read an xml file. I want to populate some of my class member variables from the configruation specified in thexml file. I have defined my startelement handler,

void Start(void *data,const XML_Char *el, const XML_Char **attr)

and this will be referenced as following:

XML_SetElementHandler(parser,Start, NULL);

At the moment I am using a global structure, g_stConfigInfo to store all the values in Start()

For example,

void Start(void *data,const XML_Char *el, const XML_Char **attr)
{
    if( _tcscmp(el,_T("blah"))==0 )
    {
        for (int i=0; attr[i]; i+=2)
        {
            if(_tcscmp(attr[i],_T("name"))==0)
            {
                g_stConfigInfo.sInputName = attr[i+1];
            }
            .........

Then I am doing myclass.sInputName = g_stConfigInfo.sInputname

I would prefer not to use the global variable, instead to be able to make this a member function of the class whose member variables need to be populated. I dont want to have an instance of this class inside Start() either. What's the best way of doing this?

+1  A: 

Well, a class member function is just like regular function with an implicit data context attached to it. So if you want to avoid referencing global data, one way or another you have to pass a non-NULL context argument to Start.

Idiomatic way to do it is usually like this:

class MyHandler {
public:
    void Start(const XML_Char *el, const XML_Char **attr) {
        // ...dispatch on "el" here.
        // e.g. a map of strings to member function pointers
    }
};
...

template<T>
void
Start(void *data, const XML_Char *el, const XML_Char **attr)
{
    T *handler = static_cast<T*>(data);
    handler->Start(el, attr);
}

MyHandler handler;
XML_SetUserData(parser, handler);
XML_SetElementHandler(parser, Start<MyHandler>, NULL);

In other words, if your class corresponds to some XML element, it's not wrong to pass it to the handler function.

Alex B
I've usually done the same thing without templates. This makes it much cleaner. +1
Ferruccio
+1  A: 

I haven't used Expat before, but I think using XML_SetUserData is what you want.

class my_data
{
    public:
    static void start_callback(void *data, const XML_Char *el, const XML_Char **attr)
    {
        static_cast<my_data*>(data)->start(el, attr);
    }

    void start(const XML_Char *el, const XML_Char **attr);
};

//...
my_data data;
XML_SetUserData(parser, &data);
XML_SetElementHandler(parser, my_data::start_callback, NULL);

SetUserData will cause the parser to pass the pointer you give it to any handler callbacks. http://www.xml.com/pub/a/1999/09/expat/index.html?page=3#setuserdata

Matt Edlefsen