2009-09-16 99 views
0

我有一类,说:“CDownloader”,读取一些XML数据,并通过节点名称提供了访问。它具有一些吸气功能,如下所示:如何编写通用的“getData”函数?

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

我可以写下载(字体|整型|图片)功能separatly,但这样会导致代码重复。我的想法是编写一个模板,但我仍然不知所措:我如何确定我应该从CDownloader类调用什么方法?为每个数据类型专门化模板意味着再次陷入代码重复。将getter函数作为“指针函数”参数传递?但吸气签名不同CDownloader ...

总结起来,问题是:是否有可能写一个CDownloader的通用包装或我必须重复每个“get ***”函数的代码?提前致谢!

+0

很难看出您试图避免什么样的重复。您显示的“DownloadFont()”有两行:一个用于选择名称,另一个用于在CDownloader上调用相应的函数。我假设名称将从一个类型到另一个不同(因此第一行必须不同),第二行也必须因为您想调用具有不同签名的不同CDownloader成员(所以第二行也有不同)。 – 2009-09-16 14:40:51

+0

将使用示例更改为更复杂。这是我试图达到的目标。我有一个5“get ...”函数,它只有在成员函数中才有所不同,所以需要CDownloader ... – SadSido 2009-09-16 14:47:32

回答

1

只要你有三个不同的命名功能,需要选择一个根据类型,在某些时候你必须有任何过载或某些特质类来挑选合适的人。我认为没有办法解决这个问题。但是,由于对这些函数之一的调用是唯一需要的功能,如果这些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

明白了!使用相同的签名引入一组重载的“加载”函数允许编写一个通用的“下载”。不仅如果“下载”是复杂的(实际上不是这样),而且如果“下载”的逻辑可能会及时改变,这也很重要。因此,我必须更改代码一次,而不是五次...谢谢! – SadSido 2009-09-16 17:59:58

+0

@SadSido:这是一个非常好的论点,我完全忘了! – sbi 2009-09-16 18:03:12

0

我不认为写一个通用的包装可以最终被更少的代码/复制由于,对于3个干将方法签名是不同的。无论如何,你都需要包装功能。您不妨使用拥有3种不同的Download *功能的简单方法。您可以使用宏保留条件逻辑在一个中心位置,但是这可能会令你的代码不可读严重,这是不值得的。

1

正如阿泰在他的答案写,你还是得写的CDownloader成员的外包装,所以最终的结果很可能是verbose和难度比直截了当的方式来理解。例如,这可能是一种可能性(警告:今后未经测试的代码):

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

正如你所看到的,增益与直接方式相比并不明显。

+0

感谢您使用相同的签名制作一组超载的“获取”。不幸的是,我只能接受每个问题的答案...... – SadSido 2009-09-16 18:06:52

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

最终,你将需要一个逻辑不知何故,涉及到的专业越来越整数/ FONT /图像/ foobars /魔法猴子。我只是吮吸它并编写一个Download *()家族。

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更好的类型检查。

+0

这不是'下载(装载机,标志,FontLoader());'和'下载(装载机,标志,ImageLoader(true))'? – sbi 2009-09-16 18:06:07

+0

@sbi:不,如果您创建匿名函数对象,您将无法从中得到结果。 – 2009-09-16 18:27:17

+0

@Mark:我很抱歉,我的代码太快了。 '(' – sbi 2009-09-16 19:37:58