嵌套类 C++静态内部方法(Xml 解析并尝试用值填充向量)

发布于 2024-09-26 00:22:48 字数 1669 浏览 3 评论 0原文

这就是我正在努力实现的目标。我正在尝试使用 sax 解析器来解析一些 XML。看起来我需要将它们的所有方法称为静态方法。因此,如果我想从 startElement 传回一个值,它是 static void startElement。这让我看到了我的示例代码。我一直在研究如何从静态成员函数更新嵌套类中的值。

我已经研究了一些东西,例如定义 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());

So this is what I am trying to accomplish. I am trying to use a sax parser to parse some XML. it looks like I need to call all their methods as statics. So if I want to pass a value back from say startElement it is static void startElement. Which brings me to my example code. I have been pulling my hair on how to update a value in a Nesting class from a static member function.

I have looked at several things such as defining OuterClass * oc; then trying to reference oc->allRecords, but since it is a static method inside, it fails. I am sure I am doing something wrong architecturally, so any feedback on what would be the right way to do this would be a great help. Thanks.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

因为看清所以看轻 2024-10-03 00:22:48

你的错误是使用内部类(你来自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 *。然后,您必须将其转换回正确的类型,然后voilà,您就恢复了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::startDocumentMySaxBase::endDocument 方法是虚拟的,只是为了让您从 MySaxBase 派生,然后重写这些方法。

编辑 2

我将在这里重现 Steve Jessop 的精彩评论:

+1。一个小问题 - 我不认为 C++ 标准保证 static 成员函数具有 C 链接/调用约定,但要将它们用作 C API 的回调,这就是它们所需要的。我具体不知道它会产生什么影响,但为了安全起见,do_startDocument应该是一个用extern“C”声明的自由函数。关于同一主题:Java 程序员可能没有意识到您已确保该函数不会抛出异常(因为 C 没有异常)。因此,您通常希望在包装函数中看到 try/catch(...)。 – 史蒂夫·杰索普

在此之后,并在阅读 Johannes Schaub - litb(还有谁?)< a href="https://stackoverflow.com/questions/592160/static-vs-extern-c/592205#592205">static vs extern "C"/"C++" ,我修改了代码以使do_startDocumentdo_endDocument 真正的 C 函数(即包装在 extern“C”块中)。这通常并不重要(我从未遇到过此类问题),但是,安全总比后悔好。

Your mistake is using an inner class (are you coming from Java?).

I don't know what you believe you are are achieving with an inner class, but it won't work. Don't use inner classes in C++ unless you really know what it does (for inner classes, protected and private members of the outer classes are seen as if they were public).

Now, as the solution to your problem, I guess it depends on the implementation you're using (I used once Apache's Xerces SAX, but I know Microsoft offers its own SAX implementation, and that there should be a lot other alternatives, so...)

Edit

After the comment, I found the following tutorial:

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

I must say that, coming from Java to C++, and using a C API, you have a kind of courage...

:-D

If you are not familiar enough with function pointers, and C in general, using libxml2 will be a challenge. Be sure that in the end, you will understand those notions... Note that C have a way to handle the data that C++, Java or C# developers associate to this. The C way is to pass a pointer to your data (the user data) to a function, and when the callback is called, it passes back this pointer, typed as a void *. You must then cast it back to its right type, and voilà, you have your this back.

:-)

Anyway, reading the doc, I see that when you parse the file, you'll call the following C function:

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

the user_data part is the one that interest you because it enables you to have a context. So, wrapping this function in a C++ class, you could have something like:

// 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
}

I did not test this, and I never used libxml2, but I guess the code must be Ok, and this should be enough for you to continue on your own: Just add the methods you want to support, initialize the sax handler with the relevant function pointers, and you'll have your class complete.

The MySaxBase::startDocument and MySaxBase::endDocument methods are virtual just for you to derive from MySaxBase and then override those methods.

Edit 2

I'll reproduce here Steve Jessop's excellent comment:

+1. One tiny quibble - I don't think that static member functions are guaranteed by the C++ standard to have C linkage / calling convention, but to use them as a callback from a C API, that's what they need. I don't specifically know what implementations it makes a difference, but for safety do_startDocument should be a free function declared with extern "C". On the same subject: a Java programmer may not realise you have make sure that the function can't throw an exception (because C doesn't have them). So you'd normally want to see a try/catch(...) in the wrapper function. – Steve Jessop

Following this, and after reading Johannes Schaub - litb (who else?) no less excellent answer at static vs extern "C"/"C++" , I modified the code to make do_startDocument and do_endDocument real C functions (i.e. wrapped in an extern "C" block). This usually is not important (I never encountered this kind of problem), but, better safe than sorry.

淡忘如思 2024-10-03 00:22:48

您的基本问题是 static 方法不是每个实例的,因此没有 this 指针。您以某种方式需要将 OuterClass* 传递给 mymethod1mymethod2

如果您向我们展示如何调用 mymethod1mymethod2,我们可以为您提供进一步帮助。

如果您只是在有 OuterClass 对象的地方调用它,那么您的解决方案很简单:

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

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

Your basic problem is that static methods are not per-instance, so there is no this pointer. You somehow need to get a OuterClass* passed to mymethod1 and mymethod2.

If you show us how mymethod1 and mymethod2 are called, we can help you further.

If it's simply called by you someplace where you have a OuterClass object, then your solution is simple:

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

void some_func()
{
  OuterClass oc;
  OuterClass::method1(&oc);
}
清君侧 2024-10-03 00:22:48

既然您在这里更新了您的问题,那么您应该如何执行此操作:

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

Since you updated your question here is how you should do this:

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());
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文