如何编写通用的“getData”功能?
我有一个类,例如“CDownloader”,它读取一些 XML 数据并提供按节点名称的访问。它具有一些 getter 函数,如下所示:
BOOL CDownloader::getInteger ( const CString &name, int *Value );
BOOL CDownloader::getImage ( const CString &name, BOOL NeedCache, CImage *Image );
BOOL CDownloader::getFont ( const CString &name, CFont *Font );
我无法更改 CDownloader 类。相反,我想编写一些函数,通过使用布尔标志而不是实际名称来下载项目。像这样的事情:
BOOL DownloadFont( const CDownloader &Loader, bool Flag, CFont *Font )
{
if (Flag) {
// first try the "name_1"
if ( Loader.getFont("name_1", Font) ) return TRUE;
}
// if "name_1" fails or disabled by flag, try "name_2"
return Loader.getFont("name_2", Font);
}
我可以单独编写 Download(Font|Integer|Image) 函数,但这会导致代码重复。我的想法是编写一个模板,但我仍然不知所措:如何确定应该从 CDownloader 类中调用哪个方法?为每种数据类型专门化模板意味着再次陷入代码重复。将 getter 函数作为“函数指针”参数传递?但是 CDownloader 中的 getter 签名有所不同...
总而言之,问题是:是否可以围绕 CDownloader 编写一个通用包装器,或者我是否必须为每个“get***”函数复制代码?提前致谢!
I have a class, say, "CDownloader", that reads some XML data and provides access by node names. It features some getter functions, something like this:
BOOL CDownloader::getInteger ( const CString &name, int *Value );
BOOL CDownloader::getImage ( const CString &name, BOOL NeedCache, CImage *Image );
BOOL CDownloader::getFont ( const CString &name, CFont *Font );
I cannot change CDownloader class. Instead I would like to write some functions, that downloads items by using a bool flag, not an actual name. Something like this:
BOOL DownloadFont( const CDownloader &Loader, bool Flag, CFont *Font )
{
if (Flag) {
// first try the "name_1"
if ( Loader.getFont("name_1", Font) ) return TRUE;
}
// if "name_1" fails or disabled by flag, try "name_2"
return Loader.getFont("name_2", Font);
}
I can write Download(Font|Integer|Image) functions separatly, but this will result in code duplication. My idea is to write a template, but I am still at a loss: how can I determine what method should I call from CDownloader class? To specialize template for each data type means to stuck into code duplication again. To pass getter funciton as a "pointer-to-function" parameter? But the getter signatures differ in CDownloader...
Summing it up, the question is: is it possible to write a generic wrapper around CDownloader or do I have to duplicate code for each "get***" function? Thanks in advance!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
只要您有三个不同命名的函数并且需要根据类型选择一个函数,那么在某些时候您必须具有重载或某些特征类才能选择正确的函数。我认为没有办法解决这个问题。但是,由于对这些函数之一的调用是唯一需要此功能的事情,因此如果这些
DownloadXXX()
函数的代码比您向我们展示的更多,那么它可能仍然有意义。下面是使用重载替代方案可以执行的操作的草图。首先,您需要同一函数的三个重载,每个重载调用三个不同函数之一。其中一个函数的附加
BOOL
参数在某种程度上对通用性造成了严重破坏,但我通过让所有函数接受该BOOL
来解决这个问题,但其中两个函数忽略它:现在您可以编写该通用函数了。不过,您需要决定如何处理该
BOOL
:但是,正如您所看到的,只有当
Download
函数复杂得多时,这才真正值得麻烦。比您的示例代码中的。否则,增加的复杂性很容易超过增加通用性带来的收益。As long as you have three differently named functions and need to pick one depending on the type, at some point you have to have either an overload or some traits class to picks the right one. I don't think there's a way around that. However, since the call to one of these function is the only thing that needs this, if there is more code to these
DownloadXXX()
functions than you showed us, then it might still make sense.Here's a sketch of what you might do using the overload alternative. First you need three overloads of the same function each calling one of the three different functions. The additional
BOOL
parameter for one of the functions somewhat wreaks havoc with the genericity, but I got around that by having all functions accept thatBOOL
, but two of them ignoring it:Now you can go and write that generic function. You need to decide what to do about that
BOOL
, though:However, as you can see, this is really only worth the hassle if that
Download
function is a lot more complicated than in your sample code. Otherwise the added complexity easily outweighs the gains that the increased genericity brings.正如 Ates 在他的回答中所写,您仍然必须为 CDownloader 成员编写包装器,因此最终结果可能比直接的方式更冗长且更难理解。例如,这可能是一种可能性(警告:前面未经测试的代码):
尝试变得“聪明”,可以尝试制作一个 boost::fusion::getters 映射,由“getted”类型索引:
当你可见,与直接方式相比,收益并不明显。
As Ates writes in his answer, you still have to write wrappers for the CDownloader members, so the end result is likely to be as verbose and harder to understand than the straightforward way. For example, this could be a possibility (warning: untested code ahead):
Trying to be "cleverer", one could try to make a boost::fusion::map of the getters, indexed by the "getted" type:
As you see, the gain compared to the straightforward way is not obvious.
我认为函数对象是最好的,因为你可以适应不同的签名。
调用将如下所示:
传入的结构将包含下载的对象。在 C++0x 中,您将能够定义一个概念,该概念将为模板参数 T 提供更好的类型检查。
I think function objects are best because you can adapt to different signatures.
The calls would then look like:
and the structs passed in would contain the downloaded objects. In C++0x you will be able to define a concept that will provide better type checking on the template parameter T.
我不认为编写通用包装器最终会减少代码/重复,因为 3 个 getter 的方法签名不同。无论如何,您都需要围绕这些函数进行包装函数。您不妨采用具有 3 种不同的下载* 功能的简单方法。您可以使用宏将条件逻辑保留在中心位置,但这可能会使您的代码非常不可读,而且不值得。
I don't think writing a generic wrapper can end up being less code/duplication due to the fact that the method signatures for the 3 getters are different. You'll need wrapper functions around these no matter what. You might as well go with the straightforward way of having 3 different Download* functions. You could use macros to keep the conditional logic in a central location, but that would probably make your code grossly unreadable, and it's not worth it.
您可以通过指向成员函数的指针找到某个地方:
现在 getImage 方法的异常使其变得更加困难。也许可以尝试使用 boost::bind / std::tr1::bind 实例之类的东西来完成这项工作。
You can get somewhere with a pointer to member function:
Now the exception of the getImage method makes it harder. May-be try making this work with something like boost::bind / std::tr1::bind instances instead.
这是一种 C-hacky 的方法。
最后,您将需要一个与整数/字体/图像/foobars/魔术猴子的专门获取相关的逻辑。我只是忍气吞声,写一个 Download*() 系列。
Here's a C-hacky way to do it.
In the end, you're going to need a logic somehow that relates to specialized getting of integer/font/image/foobars/magic monkeys. I'd just suck it up and write a Download*() family.