代码之家  ›  专栏  ›  技术社区  ›  SadSido

如何编写通用的“getData”函数?

  •  0
  • SadSido  · 技术社区  · 15 年前

    我有一个类,比如说“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 );
    

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

    总而言之,问题是:是否有可能围绕CDownloader编写一个通用包装器,或者我必须为每个“get***”函数复制代码?提前谢谢!

    6 回复  |  直到 15 年前
        1
  •  1
  •   sbi    15 年前

    只要您有三个不同命名的函数,并且需要根据类型选择一个函数,那么在某些情况下,您必须使用重载或某些traits类来选择正确的函数。我认为没有办法解决这个问题。但是,由于对其中一个函数的调用是唯一需要这样做的事情,因此如果这些函数有更多代码的话 DownloadXXX()

    下面是一个使用重载替代方法的示意图。首先,您需要三个相同函数的重载,每个重载调用三个不同函数中的一个。附加的 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);
    

    现在您可以开始编写通用函数了。你需要决定怎么做 布尔

    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 函数比示例代码中的要复杂得多。否则,增加的复杂性很容易超过增加的泛型带来的好处。

        2
  •  1
  •   Éric Malenfant    15 年前

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

    为了变得“更聪明”,可以尝试创建一个由“getted”类型索引的getter的boost::fusion::map:

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

    正如您所看到的,与直截了当的方法相比,收益并不明显。

        3
  •  1
  •   Mark Ruzon    15 年前

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

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

        4
  •  0
  •   Ates Goral    15 年前

    能够 使用宏将条件逻辑保持在一个中心位置,但这可能会使您的代码非常不可读,这是不值得的。

        5
  •  0
  •   UncleBens    15 年前

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

    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);
    }
    
        6
  •  -1
  •   Paul Nathan    15 年前

    这里有一个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);
    }