如何编写通用的“getData”功能?

发布于 2024-08-05 02:44:31 字数 981 浏览 5 评论 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 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

橘香 2024-08-12 02:44:31

只要您有三个不同命名的函数并且需要根据类型选择一个函数,那么在某些时候您必须具有重载或某些特征类才能选择正确的函数。我认为没有办法解决这个问题。但是,由于对这些函数之一的调用是唯一需要此功能的事情,因此如果这些 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 函数复杂得多时,这才真正值得麻烦。比您的示例代码中的。否则,增加的复杂性很容易超过增加通用性带来的收益。

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 that BOOL, but two of them ignoring it:

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

Now you can go and write that generic function. You need to decide what to do about that BOOL, though:

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

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.

夜空下最亮的亮点 2024-08-12 02:44:31

正如 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::getters 映射,由“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);
}

当你可见,与直接方式相比,收益并不明显。

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):

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

Trying to be "cleverer", one could try to make a boost::fusion::map of the getters, indexed by the "getted" type:

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

As you see, the gain compared to the straightforward way is not obvious.

此刻的回忆 2024-08-12 02:44:31

我认为函数对象是最好的,因为你可以适应不同的签名。

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

I think function objects are best because you can adapt to different signatures.

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

The calls would then look like:

FontLoader Font_func;
BOOL ret1 = Download(Loader, Flag, Font_func);
ImageLoader Image_func(TRUE);
BOOL ret2 = Download(Loader, Flag, Image_func);

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.

夕色琉璃 2024-08-12 02:44:31

我不认为编写通用包装器最终会减少代码/重复,因为 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.

記柔刀 2024-08-12 02:44:31

您可以通过指向成员函数的指针找到某个地方:

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

You can get somewhere with a pointer to member function:

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

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.

#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);
}
凹づ凸ル 2024-08-12 02:44:31

这是一种 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*() 系列。

Here's a C-hacky way to do it.

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文