2009-09-16 5 views
0

필자는 XML 데이터를 읽고 노드 이름별로 액세스를 제공하는 "CDownloader"클래스를 가지고 있습니다.일반 "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 funciton을 "함수 포인터에 대한 포인터"매개 변수로 전달하려면? 하지만 Getter 서명은 CDownloader에서 다릅니다.

요약하면 다음과 같습니다. CDownloader에 대한 일반적인 래퍼를 작성할 수 있습니까? 아니면 "get ***"함수마다 코드를 복제해야합니까? 미리 감사드립니다!

+0

피하고자하는 복제본을보기가 어렵습니다. 표시하는 "DownloadFont()"에는 이름을 선택하는 것과 CDownloader에서 적절한 함수를 호출하는 두 줄이 있습니다. 나는 이름이 다른 타입 (다른 라인이 될 것임)에서 다른 것으로 가정하고, 두 번째 라인은 서로 다른 서명을 가진 다른 CDownloader 멤버를 호출하기 때문에 두 번째 라인도 가지고있다. 다른 것). –

+0

사용 예제가 좀 더 복잡해졌습니다. 이것이 내가 성취하려고하는 것입니다. 그리고 저는 회원 함수에서만 다른 5 개의 "get ..."함수를 가지고 있는데, CDownloader를 호출했습니다 ... – SadSido

답변

1

이름이 다른 세 개의 함수가 있고 유형에 따라 함수를 선택해야하는 경우, 어느 시점에서 오버로드 또는 일부 특성 클래스를 선택하여 올바른 함수를 선택해야합니다. 나는 그 주위에 방법이 있다고 생각하지 않는다. 그러나이 함수 중 하나에 대한 호출이이 것을 필요로하는 유일한 것이므로, 우리가 보여준 것보다 더 많은 코드가이 코드에있는 경우에는 여전히 의미가있을 수 있습니다.

다음은 과부하 대안을 사용하여 수행 할 수있는 작업에 대한 개요입니다. 먼저 세 가지 다른 함수 중 하나를 호출하는 동일한 함수의 세 가지 오버로드가 필요합니다. 기능 중 하나에 대한 추가 BOOL 매개 변수는 다소들이 generic과 파괴를 낸다, 그러나 나는함으로써 모든 기능을 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에 대해 무엇을 결정해야합니다 : 당신이 볼 수있는 그 Download 기능은 훨씬 더 복잡 샘플 코드에서보다 긴 경우

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

그러나, 이러한 번거 로움 정말 유일한 가치가있다. 그렇지 않으면 추가 된 복잡성이 증가 된 일반성이 가져 오는 이익보다 쉽게 ​​크다.

+1

알립니다! 오버로드 된 "로드"함수 세트를 동일한 시그니처와 함께 도입하면 일반 "다운로드"를 작성할 수 있습니다. "다운로드"가 복잡 할뿐만 아니라 (실제로는 아닙니다), "다운로드"의 논리가 시간에 따라 변경 될 수도 있습니다. 따라서 코드를 한 번만 변경하고 다섯 번 변경하면 안됩니다 ... 감사합니다! – SadSido

+0

@SadSido : 그건 완전히 잊어 버린 아주 좋은 주장입니다! – sbi

0

일반 래퍼를 쓰는 것이 결국 3 게터의 메서드 서명이 다르기 때문에 적은 코드/중복이 될 수 있다고 생각하지 않습니다. 무엇이든 관계없이 래퍼 함수가 필요합니다. 3 가지 다른 Download * 기능을 사용하는 간단한 방법을 사용할 수도 있습니다. 은 매크로를 사용하여 조건부 논리를 중앙 위치에 유지할 수 있지만 코드를 읽을 수 없게 만들 가능성이 있으며 가치가 없습니다.

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

을, 하나는 부스트 :: 게터의 융합 :지도의 색인을 만들려고 수 "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); 
} 

알다시피, 직접적인 방법에 비해 이득이 명확하지 않습니다.

+0

동일한 서명으로 "가져 오기"가 오버로드 된 집합을 만드는 것에 대해 감사드립니다. 불행히도, 질문마다 단 하나의 대답만을 받아 들일 수 있습니다 ... – SadSido

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 해커가 있습니다. 결국

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

, 당신은 어떻게 든 그 전문 정수/폰트/이미지/푸바/마법 원숭이의 얻기에 관한 논리를해야 할 것입니다. 나는 그것을 빨아 들여 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

''Download (Loader, Flag, FontLoader()); 그리고''Download (Loader, Flag, ImageLoader (true)) '가되지 않습니까? – sbi

+0

@sbi : 아니요, 익명의 함수 객체를 만들면 그 결과를 얻을 수 없습니다. –

+0

@Mark : 죄송합니다. 코드를 너무 빨리 건너 뜁니다. ': (' – sbi