Pergunta

Eu tenho uma classe, digamos, "CDownloader", que lê alguns dados XML e fornece acesso por nomes de nó. Ele possui algumas funções getter, algo como isto:

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

Eu não posso mudar de classe CDownloader. Em vez disso eu gostaria de escrever algumas funções, que baixa itens usando uma bandeira bool, não um nome real. Algo parecido com isto:

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

Eu posso escrever Baixar (Font | Integer | Imagem) funciona separatly, mas isso vai resultar em duplicação de código. Minha idéia é escrever um modelo, mas eu ainda estou em uma perda: como posso determinar qual o método que eu deveria chamar de classe CDownloader? Para especializar-modelo para cada um dos meios tipo de dados para preso em duplicação de código novamente. Para passar funciton getter como parâmetro "pointer-to-função"? Mas as assinaturas getter diferem em CDownloader ...

Resumindo-se, a pergunta é: é possível escrever um wrapper genérico torno CDownloader ou eu tenho que duplicar o código para cada função "get ***"? Agradecemos antecipadamente!

Foi útil?

Solução

Enquanto você tem três funções com nomes diferentes e necessidade de escolher um, dependendo do tipo, em algum ponto você tem que ter tanto uma sobrecarga ou alguns traços de classe para picaretas o caminho certo. Eu não acho que há uma maneira de contornar isso. No entanto, desde a chamada para uma destas funções é a única coisa que precisa disso, se houver mais código para essas funções DownloadXXX() do que você nos mostrou, então ele pode ainda fazer sentido.

Aqui está um esboço do que você pode fazer usando a alternativa de sobrecarga. Primeiro você precisa de três sobrecargas da mesma função de cada um chamando uma das três funções diferentes. O parâmetro BOOL adicional para uma das funções tanto causa estragos com o genericidade, mas eu cheguei que por ter todas as funções aceitar que BOOL, mas dois deles ignorá-lo:

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

Agora você pode ir e escrever essa função genérica. Você precisa decidir o que fazer sobre isso BOOL, no entanto:

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

No entanto, como você pode ver, este é realmente só vale o aborrecimento se essa função Download é muito mais complicado do que em seu código de exemplo. Caso contrário, a complexidade adicional supera facilmente os ganhos que o aumento genericidade traz.

Outras dicas

Como Ates escreve na sua resposta, você ainda tem que invólucros de escrita para os membros CDownloader, de modo que o resultado final é provável que seja tão detalhado e mais difícil de entender do que a maneira simples. Por exemplo, esta poderia ser uma possibilidade (aviso: frente código não testado):

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

Tentando ser "inteligente", pode-se tentar fazer um boost :: fusion :: mapa dos getters, indexado pelo tipo "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);
}

Como você pode ver, o ganho em comparação com a forma simples não é óbvia.

Eu acho que objetos de função são melhores porque você pode se adaptar a diferentes assinaturas.

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

As chamadas seria semelhante:

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

e as estruturas passaram em conteria os objetos baixados. Em C ++ 0x você será capaz de definir um conceito que irá proporcionar uma melhor verificação de tipo no modelo de parâmetro T.

Eu não acho que escrever um invólucro genérico pode acabar sendo menos código / duplicação devido ao fato de que as assinaturas de método para os 3 getters são diferentes. Você vai precisar de funções de invólucro em torno destes, não importa o quê. Você pode ir tão bem com a maneira simples de ter 3 diferentes funções de download *. Você pode usar macros para manter a lógica condicional em um local central, mas que provavelmente faria o seu código grosseiramente ilegível, e não vale a pena.

Você pode chegar a algum lugar com um ponteiro para função de membro:

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

Agora, a exceção do método getImage torna mais difícil. Pode-se tentar fazer este trabalho com algo como boost :: bind / std :: tr1 :: casos de vinculação vez.

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

Aqui está uma maneira C-hacky fazê-lo.

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

No final, você vai precisar de uma lógica de alguma forma que se relaciona com ficando especializada de inteiro / font / imagem / foobars / macacos mágicas. Eu só iria chupar-lo e escrever uma família Download * ().

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top