0

我有一个类,比如“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 中有所不同......

总结起来,问题是:是否可以围绕 CDownloader 编写通用包装器,或者我是否必须为每个“get***”函数复制代码?提前致谢!

4

6 回答 6

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但是,正如您所看到的,如果该函数比您的示例代码中的复杂得多,这确实值得麻烦。否则,增加的复杂性很容易超过增加通用性带来的收益。

于 2009-09-16T15:30:23.300 回答
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);
}

为了“更聪明”,可以尝试制作 getter 的 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);
}

如您所见,与直接方式相比,收益并不明显。

于 2009-09-16T15:36:31.383 回答
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 提供更好的类型检查。

于 2009-09-16T15:54:08.757 回答
0

我不认为编写通用包装器最终会减少代码/重复,因为 3 个 getter 的方法签名不同。无论如何,您都需要围绕这些的包装函数。您不妨采用具有 3 种不同下载 * 功能的简单方法。您可以使用宏将条件逻辑保存在中央位置,但这可能会使您的代码非常不可读,而且不值得。

于 2009-09-16T15:17:22.423 回答
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);
}
于 2009-09-16T15:46:24.780 回答
-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/magic Monkeys 相关的逻辑。我只是把它吸起来,写一个 Download*() 系列。

于 2009-09-16T15:54:05.033 回答