문제

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

요약하면 질문은 다음과 같습니다.CDownloader 주위에 일반 래퍼를 작성하는 것이 가능합니까, 아니면 각 "get***" 함수에 대해 코드를 복제해야 합니까?미리 감사드립니다!

도움이 되었습니까?

해결책

세 가지 서로 다른 이름의 함수가 있고 유형에 따라 하나를 선택해야 하는 한, 어떤 시점에서는 올바른 함수를 선택하기 위해 오버로드 또는 일부 특성 클래스가 있어야 합니다.나는 그 문제를 해결할 방법이 없다고 생각합니다.그러나 이러한 함수 중 하나에 대한 호출만이 이것이 필요한 유일한 것이므로, 이러한 함수에 대한 코드가 더 있으면 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 함수는 샘플 코드보다 훨씬 더 복잡합니다.그렇지 않으면 추가된 복잡성이 증가된 일반성이 가져오는 이득보다 쉽게 ​​커집니다.

다른 팁

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" 유형으로 색인이 지정된 게터의 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);
}

보시다시피, 간단한 방법에 비해 이득이 명확하지 않습니다.

다른 서명에 적응할 수 있기 때문에 기능 객체가 가장 좋다고 생각합니다.

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

그리고 통과 된 structs에는 다운로드 된 개체가 포함됩니다. C ++ 0x에서는 템플릿 매개 변수 T에서 더 나은 유형 확인을 제공 할 개념을 정의 할 수 있습니다.

3 개의 게터에 대한 메소드 서명이 다르다는 사실 때문에 일반 래퍼를 작성하면 코드/복제가 줄어들 수 있다고 생각하지 않습니다. 무슨 일이 있어도 래퍼 기능이 필요합니다. 3 가지 다른 다운로드* 함수를 갖는 간단한 방법을 사용할 수도 있습니다. 너 ~할 수 있었다 매크로를 사용하여 조건부 논리를 중앙 위치에 보관하지만 코드를 심하게 읽을 수 없게 만들면 그만한 가치가 없습니다.

회원 기능에 대한 포인터가있는 곳에서 얻을 수 있습니다.

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 :: 대신 인스턴스와 같은 것을 사용 하여이 작업을 시도해보십시오.

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

다음은 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/image/foobars/magic monkeys의 전문화 된 섭취와 관련된 논리가 필요합니다. 나는 단지 그것을 빨고 다운로드*() 가족을 씁니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top