我有一个类,比如“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标志下载项目,而不是实际名称。像这样:
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 ***”函数复制代码?提前谢谢!
答案 0 :(得分:1)
只要你有三个不同命名的函数并且需要根据类型选择一个函数,在某些时候你必须有一个重载或一些traits类来选择正确的函数。我不认为有办法解决这个问题。但是,由于调用其中一个函数是唯一需要这个函数的函数,如果这些DownloadXXX()
函数的代码多于您向我们展示的代码,那么它可能仍然有意义。
这是使用过载替代方案可能会做的草图。首先,您需要三个相同功能的重载,每个重载调用三个不同功能中的一个。其中一个函数的附加BOOL
参数对通用性造成了严重破坏,但我通过让所有函数接受BOOL
来解决这个问题,但是其中两个忽略了它:
inline BOOL Load(CDownloader& Loader, const CString &name, int &Value, BOOL)
{return Loader.getInteger(name, &Value);
inline BOOL Load(CDownloader& Loader, const CString &name, CImage &Value, BOOL NeedCache)
{return Loader.getImage(name, NeedCache, &value);
inline BOOL Load(CDownloader& Loader, const CString &name, CFont &Value, BOOL)
{return Loader.getFont(name, &Font);
现在你可以去写那个通用函数了。但是,您需要决定如何处理BOOL
:
template< typename T >
BOOL Download(const CDownloader &Loader, bool Flag, T &Obj, BOOL NeedCache /*= true*/)
{
if (Flag) {
if ( Load(Loader, "name_1", Obj, NeedCache) ) return TRUE;
}
return Load(Loader, "name_1", Obj, NeedCache);
}
但是,正如您所看到的,如果Download
函数比示例代码复杂得多,那么这真的值得麻烦。否则,增加的复杂性很容易超过增加的通用性带来的收益。
答案 1 :(得分:1)
正如Ates在他的回答中写道,你仍然必须为CDownloader成员编写包装器,因此最终结果可能比直接的方式更冗长,更难理解。例如,这可能是一种可能性(警告:未经测试的代码):
BOOL Get(const CDownloader &Loader, const CString& Name, int* Result)
{
return Loader.getInteger(Name, Result);
}
BOOL Get(const CDownloader &Loader, const CString& Name, CImage* Result)
{
return Loader.getImage(Name, SomeDefaultValueForNeedCache, Result);
}
BOOL Get(const CDownloader &Loader, const CString& Name, CFont* Result)
{
return Loader.getFont(Name, Result);
}
template<class T>
BOOL Download(const CDownloader &Loader, bool Flag, T* Result)
{
if (Flag) {
// first try the "name_1"
if ( Get(Loader, "name_1", Result) ) return TRUE;
}
// if "name_1" fails or disabled by flag, try "name_2"
return Get (Loader, "name_2", Result);
}
试图成为“聪明人”,可以尝试制作一个吸气剂的boost :: fusion :: map,由“getted”类型索引:
fusion::map<
fusion::pair<int, boost::function<BOOL(const CDownloader&, int*)>,
fusion::pair<CImage, boost::function<BOOL(const CDownloader&, CImage*)>,
fusion::pair<CFont, boost::function<BOOL(const CDownloader&, CFont*)>
>
GetterMap = fusion::make_map(
fusion::make_pair<int>(bind(&CDownloader::getInteger, _1, _2)),
fusion::make_pair<CImage>(&CDownloader::getImage, _1, SomeDefaultValueForNeedCache, _2),
fusion::make_pair<CFont>(&CDownloader::getFont, _1, _2)
);
template<class T>
BOOL Download(const CDownloader &Loader, bool Flag, T* Result)
{
if (Flag) {
// first try the "name_1"
if ( fusion::at<T>(GetterMap)(Loader, "name_1", Result) ) return TRUE;
}
// if "name_1" fails or disabled by flag, try "name_2"
return fusion::at<T>(GetterMap)(Loader, "name_2", Result);
}
如您所见,与直接方式相比的收益并不明显。
答案 2 :(得分:1)
我认为函数对象是最好的,因为你可以适应不同的签名。
struct FontLoader {
CFont *Font;
FontLoader() {}
BOOL operator()(const CDownloader& Loader, bool Flag) {
if (Flag && Loader.getFont("name_1", Font) )
return TRUE;
return Loader.getFont("name_2", Font);
}
};
struct ImageLoader {
CImage *Image;
BOOL NeedCache;
ImageLoader(BOOL nc) : NeedCache(nc) {}
BOOL operator()(const CDownloader& Loader, bool Flag) {
if (Flag && Loader.getImage("name_3", NeedCache, Image) )
return TRUE;
return Loader.getImage("name_4", NeedCache, Image);
}
};
template <typename T> // T has application operator CDownloader x bool -> T1
BOOL Download( const CDownloader &Loader, bool Flag, T& func)
{
return func(Loader, Flag);
}
这些电话看起来像是:
FontLoader Font_func;
BOOL ret1 = Download(Loader, Flag, Font_func);
ImageLoader Image_func(TRUE);
BOOL ret2 = Download(Loader, Flag, Image_func);
传入的结构将包含下载的对象。在C ++ 0x中,您将能够定义一个概念,以便对模板参数T提供更好的类型检查。
答案 3 :(得分:0)
我不认为编写通用包装器最终会减少代码/重复,因为3个getter的方法签名是不同的。无论如何,你都需要围绕这些的包装函数。您可以选择使用3种不同的Download *功能的直接方式。你可能使用宏来将条件逻辑保存在一个中心位置,但这可能会使你的代码严重不可读,而且不值得。
答案 4 :(得分:0)
你可以到某个地方找到一个指向成员函数的指针:
struct X
{
bool getInt(int* p) const { *p = 42; return true; }
bool getFloat(float* p) const { *p = 3.14; return true; }
};
template <class Func, class T>
bool load(const X& x, Func f, T* t)
{
return (x.*f)(t);
}
int main()
{
int i;
float f;
X x;
load(x, &X::getInt, &i);
load(x, &X::getFloat, &f);
//load(x, &X::getFloat, &i);
}
现在getImage方法的例外使它变得更难。可以尝试使用类似boost :: bind / std :: tr1 :: bind实例的工作。
#include <boost/bind.hpp>
struct X
{
bool getInt(int* p) const { *p = 42; return true; }
bool getFloat(float* p, bool b) const { *p = 3.14; return b; }
};
template <class Func, class T>
bool load(Func f, T* t)
{
return f(t);
}
int main()
{
using namespace boost;
int i;
float f;
X x;
load(bind(&X::getInt, x, _1), &i);
load(bind(&X::getFloat, x, _1, true), &f);
}
答案 5 :(得分:-1)
这是一种C-hacky方式。
void* DownloadFont( const CDownloader &Loader, bool Flag, CFont *Font )
{
if (Flag) {
// first try the "name_1"
if ( Loader.getFont("name_1", Font) ) return (void*)1; //access this directly and *die*
}
// if "name_1" fails or disabled by flag, try "name_2"
return (void*)(Loader.getFont("name_2", Font);
}
最后,你将需要一个与整数/字体/图像/ foobars /魔术猴的专业获取相关的逻辑。我只是吮吸它并编写一个Download *()系列。