gpt4 book ai didi

c++ - 如何解决 C++ 指向成员函数的指针限制

转载 作者:行者123 更新时间:2023-11-30 03:35:35 27 4
gpt4 key购买 nike

C++ 使用指向成员函数的指针的能力有限。我需要一些允许我动态选择回调成员函数的东西,以便使用 TinyXML2 库中 XMLNode::Accept(XMLVisitor *visitor) 方法的访问者模式。

要使用 XMLNode::Accept(),我必须使用实现了 XMLVisitor 接口(interface)的类来调用它。因此:

typedef bool (*Callback)(string, string);

class MyVisitor : public tinyxml2::XMLVisitor {
public:
bool VisitExit(const tinyxml2::XMLElement &e) {
callback(e.Name(), e.GetText());
}
Callback callback;
}

如果我的调用者不是一个想要使用它自己的方法之一作为回调函数(以便它可以访问类变量)的对象,这就可以正常工作。例如,这有效:

bool myCallBackFunc(string e, string v) {
cout << "Element " << e << " has value " << v << endl;
return true;
}

int main(...) {
tinyxml2::XMLDocument doc;
doc.LoadFile("somefile.xml");
MyVisitor visit;
visit.callback = myCallBackFunc;
doc.Accept(&visit);
}

但是,在我的用例中,解析是在类的方法内完成的。我有多个应用程序具有相似但独特的此类。我只想使用一个通用的 MyVisitor 类,而不是让访问者类对调用它的每个类的内部结构有独特的了解。

因此,如果回调函数是每个调用类中的一个方法,这样我就可以影响从该调用类实例化的对象的内部状态,这将很方便。

顶级:我有 5 个服务器应用程序,它们与 5 个不同的贸易伙伴对话,它们都发送 XML 响应,但每个应用程序都足够不同,以至于每个服务器应用程序都有一个对该贸易伙伴唯一的类。我正在努力遵循良好的 OO 和 DRY 设计,并避免额外的类具有独特的知识,同时仍然做基本相同的工作。

这是我希望 Accept() 回调的类方法。

ServiceClass::changeState(string elem, string value) {
// Logic which sets member vars based on element found and its value.
}

这是将调用 Accept() 遍历 XML 的类方法:

ServiceClass::processResponse(string xml) {
// Parse XML and do something only if certain elements present.

tinyxml2::XMLDocument doc;
doc.Parse(xml.c_str(), xml.length());

MyVisitor visit;
visit.callback = &changeState; // ERROR. Does not work.
visit.callback = &ServiceClass::changeState; // ERROR. Does not work.
doc.Accept(&visit);
}

获得我想要的东西的简单方法是什么?我可以想象更多的类具有针对每种情况的唯一派生类,但这看起来非常冗长和笨拙。

注意:为简洁起见,我上面的示例代码没有错误检查,没有空检查,甚至可能有小错误(例如,将 const char * 视为字符串 ;-)。

最佳答案

下面是您尝试在 C++11 中执行的操作的 std::bind(..) 示例。对于早期的 C++ 版本,您可以使用 boost::bind 实用程序。

顺便说一下,修复您的 MyVisitor::VisitExit(...) 方法以返回一个 bool 值。

代码正在将 const char * 转换为 std::string。 tinyxml2 不保证 Name()GetText()char * 参数不为空。事实上,根据我的经验,它们在某些时候将为空。你应该提防这一点。为了不过多修改您的示例,我没有在示例中的任何地方都防止这种可能性。

typedef bool(*Callback)(string, string);
using namespace std;
class MyVisitor : public tinyxml2::XMLVisitor {
public:
bool VisitExit(const tinyxml2::XMLElement &e) {
// return callback(e.Name(), e.GetText());
return true;
}
Callback callback;
};


/** Typedef to hopefully save on confusing syntax later */
typedef std::function< bool(const char * element_name, const char * element_text) > visitor_fn;

class MyBoundVisitor : public tinyxml2::XMLVisitor {
public:
MyBoundVisitor(visitor_fn fn) : callback(fn) {}

bool VisitExit(const tinyxml2::XMLElement &e) {
return callback(e.Name() == nullptr ? "\0" : e.Name(), e.GetText() == nullptr ? "\0": e.GetText());
}
visitor_fn callback;
};

bool
myCallBackFunc(string e, string v) {
cout << "Element " << e << " has value " << v << endl;
return true;
}

int
main()
{
tinyxml2::XMLDocument doc;
doc.LoadFile("somefile.xml");
MyVisitor visit;
visit.callback = myCallBackFunc;
doc.Accept(&visit);

visitor_fn fn = myCallBackFunc; // copy your function pointer into the std::function<> type
MyBoundVisitor visit2(fn); // note: declare this outside the Accept(..) , do not use a temporary
doc.Accept(&visit2);
}

所以在 ServiceClass 方法中你会做:

ServiceClass::processResponse(string xml) {
// Parse XML and do something only if certain elements present.

tinyxml2::XMLDocument doc;
doc.Parse(xml.c_str(), xml.length());
// presuming changeState(const char *, const char *) here
visitor_fn fn = std::bind(&ServiceClass::changeState,this,std::placeholders::_1,std::placeholders::_2);
MyBoundVisitor visit2(fn); // the method pointer is in the fn argument, together with the instance (*this) it is a method for.
doc.Accept(&visit);
}

关于c++ - 如何解决 C++ 指向成员函数的指针限制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41190125/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com