2009-09-16 48 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