Вложенные классы C ++ статические внутренние методы (анализ XML и попытка заполнения вектора значениями) - PullRequest
0 голосов
/ 02 октября 2010

Так вот чего я пытаюсь достичь.Я пытаюсь использовать синтаксический анализатор саксофона для анализа XML.похоже, мне нужно назвать все их методы статическими.Так что если я хочу передать значение обратно, скажем, startElement, то это статический void startElement.Что подводит меня к моему примеру кода.Я пытался обновить значение в классе Nesting из статической функции-члена.

Я рассмотрел несколько вещей, таких как определение OuterClass * oc;затем пытается сослаться на oc-> allRecords, но, поскольку это статический метод внутри, происходит сбой.Я уверен, что я делаю что-то не так в архитектуре, поэтому любая обратная связь о том, как правильно сделать это, будет очень полезна.Спасибо.

class Attribute {
    string AttributeName;
    string AttributeValue;
};
typedef shared_ptr<Attribute> AttributePtr;

class AttributeSet {
    vector<AttributePtr> Attributes;
};
typedef shared_ptr<AttributeSet> AttributeSetPtr;

class OuterClass {
    public :
    vector<AttributeSetPtr> allRecords;
    class InnerClass {
         public:
         static mymethod1() {
            // I need to be able to set attributes here :
            // This would be the characters method for sax parsing 
            // What is the right way to Attributes.push_back(new Attribute(Name,Value));
         }


         static mymethod2() {
            // I also need to be able to add Records here :
            // This would be the endElement for sax parsing
            //  What is the right way to allRecords.push_back(AttributeSet);
         }
   };

   // EDIT: CALLING CODE GOES HERE (WAS EDITED - SEE BELOW) 
};



// ADDING INFORMATION REGARDING HOW METHOD 1 & 2 are called
xmlSAXHandler saxHandler;
memset(&saxHandler, 0, sizeof(saxHandler));
saxHandler.initialized = XML_SAX2_MAGIC;
...
saxHandler.endElementsNs = &InnerClass::method2;
saxHandler.characters = &InnerClass::method1;
...
InnerClass innerXmlParsingClass
xmlSaxUserParseMemory( &saxHandler, &innerXmlParsingClass, xmlString, xmlString.length());

Ответы [ 3 ]

3 голосов
/ 02 октября 2010

Ваша ошибка - использование внутреннего класса (вы пришли из Java?).

Я не знаю, чего, по вашему мнению, вы достигаете с помощью внутреннего класса, но это не сработает. Не используйте внутренние классы в C ++, если вы действительно не знаете, что он делает ( для внутренних классов, защищенные и закрытые члены внешних классов выглядят так, как если бы они были открытыми ).

Теперь, как решение вашей проблемы, я думаю, это зависит от используемой вами реализации (я однажды использовал Apache Xerces SAX, но я знаю, что Microsoft предлагает свою собственную реализацию SAX, и что должно быть много других альтернатив). итак ...)

Редактировать

После комментария я нашел следующий урок:

http://www.jamesh.id.au/articles/libxml-sax/libxml-sax.html

Я должен сказать, что, переходя с Java на C ++ и используя C API, вы набираетесь смелости ...

: - D

Если вы недостаточно знакомы с указателями на функции и C вообще, использование libxml2 будет проблемой. Убедитесь, что в конце вы поймете эти понятия ... Обратите внимание, что в C есть способ обработки данных, которые разработчики C ++, Java или C # связывают с this. Способ C состоит в том, чтобы передать указатель на ваши данные (пользовательские данные) в функцию, и когда вызывается обратный вызов, он возвращает этот указатель, напечатанный как void *. Затем вы должны вернуть его к нужному типу, и вуаля , у вас есть this назад.

: -)

В любом случае, читая документацию, я вижу, что когда вы анализируете файл, вы вызываете следующую функцию C:

int xmlSAXUserParseFile(   xmlSAXHandlerPtr   sax,
                           void *             user_data,
                           const char *       filename);

user_data - это та часть, которая вас интересует, потому что она позволяет вам иметь контекст. Итак, оборачивая эту функцию в класс C ++, вы можете получить что-то вроде:

// MySaxBase.hpp
class MySaxBase
{
   public :
       MySaxBase() ;
       int parseFile(const std::string & p_filename) ;
       virtual void startDocument() ;
       virtual void endDocument() ;
   private :
       static void do_startDocument(void *p_user_data) ;
       static void do_endDocument(void *p_user_data) ;
       xmlSAXHandler     m_sax ;
}

.

// MySaxBase.cpp

extern "C"
{

void do_startDocument(void *p_user_data)
{
   // this static method will convert the p_user_data into
   // the this pointer...
   MySaxBase * saxBase = static_cast<MySaxBase *>(p_user_data) ;
   // ...and call the right virtual method
   saxBase->startDocument() ;
}

void do_endDocument(void *p_user_data)
{
   // this static method will convert the p_user_data into
   // the this pointer...
   MySaxBase * saxBase = static_cast<MySaxBase *>(p_user_data) ;
   // ...and call the right virtual method
   saxBase->endDocument() ;
}

} // extern "C"

MySaxBase::MySaxBase()
{
   // the m_sax structure must be set to zero to NULL all its
   // pointers to functions
   memset(&m_sax, 0, sizeof(xmlSAXHandler)) ;
   // Now, we initialize some pointers to the static method we
   // want to be called
   this->m_sax.startDocument = do_startDocument ;
   this->m_sax.endDocument = do_endDocument ;
}

int MySaxBase::parseFile(const std::string & p_filename)
{
   // the important thing, here, is the this pointer, passed as
   // a user_data parameter
   return xmlSAXUserParseFile(&m_sax, this, p_filename.c_str()) ;
}

void MySaxBase::startDocument()
{
   // The document started. Override this method to
   // actually do something
}

void MySaxBase::endDocument()
{
   // The document ended. Override this method to
   // actually do something
}

Я не тестировал это, и я никогда не использовал libxml2, но я думаю, что код должен быть в порядке, и этого должно быть достаточно для продолжения самостоятельно: просто добавьте методы, которые вы хотите поддерживать, инициализируйте обработчик sax с соответствующими указателями на функции, и ваш класс будет завершен.

Методы MySaxBase::startDocument и MySaxBase::endDocument являются виртуальными для того, чтобы вы могли наследовать их от MySaxBase и затем переопределить эти методы.

Редактировать 2

Я воспроизведу здесь превосходный комментарий Стива Джессопа:

+ 1. Одно маленькое замечание - я не думаю, что static функции-члены гарантированы стандартом C ++, чтобы иметь соглашение о связывании / вызове C, но использовать их в качестве обратного вызова из C API, это то, что им нужно. Я не знаю точно, какие реализации это имеет значение, но для безопасности do_startDocument должна быть свободной функцией, объявленной с extern "C". По той же теме: Java-программист может не понимать, что вы должны убедиться, что функция не может выдать исключение (потому что в C их нет). Таким образом, вы обычно хотели бы видеть try/catch(...) в функции оболочки. - Стив Джессоп

После этого и после прочтения Йоханнесом Шаубом - не менее отличного ответа от статического против внешнего "C" / "C ++" Йоханнеса Шауба, я изменил код так, чтобы сделать do_startDocument и do_endDocument настоящие функции C (то есть обернутые во внешний блок "C"). Обычно это не важно (я никогда не сталкивался с подобными проблемами), но лучше, чем потом сожалеть.

1 голос
/ 02 октября 2010

Поскольку вы обновили свой вопрос, вот как вы должны это сделать:

class OuterClass {
public:
    vector<AttributeSetPtr> allRecords;

    void characters(const xmlChar* ch, int len)
    {
        // do here whatever you want
        allRecords.push_back(bla bla);
    }

    static void static_characters(void* ctx, const xmlChar* ch, int len) {
        // retrieve this pointer from ctx
        static_cast<OuterClass*>(ctx)->characters(ch, len);
    }
};

saxHandler.characters = &OuterClass::static_characters;
...
OuterClass outerClass;
xmlSaxUserParseMemory(&saxHandler, static_cast<void*>(&outerClass), xmlString, xmlString.length());
1 голос
/ 02 октября 2010

Ваша основная проблема в том, что static методы не для каждого экземпляра, поэтому указатель this отсутствует. Вам нужно как-то передать OuterClass* на mymethod1 и mymethod2.

Если вы покажете нам, как называются mymethod1 и mymethod2, мы можем помочь вам в дальнейшем.

Если он просто вызван вами где-то, где у вас есть OuterClass объект, тогда ваше решение простое:

class OuterClass
{
// ...
  static void mymethod1(OuterClass* oc)
  {
    oc->all_records.push_back( something );
  }
};

void some_func()
{
  OuterClass oc;
  OuterClass::method1(&oc);
}
...