Pregunta

Tengo una clase, por ejemplo, "CDownloader", que lee algunos datos XML y proporciona acceso por nombre de nodo. Cuenta con algunas funciones getter, algo como esto:

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

No puedo cambiar la clase de CDownloader. En cambio, me gustaría escribir algunas funciones, que descargan elementos mediante el uso de una bandera bool, no un nombre real. Algo como esto:

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

Puedo escribir las funciones Descargar (Fuente | Entero | Imagen) por separado, pero esto dará como resultado la duplicación de código. Mi idea es escribir una plantilla, pero todavía estoy perdido: ¿cómo puedo determinar a qué método debo llamar desde la clase CDownloader? Especializar la plantilla para cada tipo de datos significa quedarse atascado en la duplicación de código nuevamente. Para pasar la función getter como '' puntero a función '' ¿parámetro? Pero las firmas getter difieren en CDownloader ...

En resumen, la pregunta es: ¿es posible escribir un contenedor genérico alrededor de CDownloader o tengo que duplicar el código para cada " get *** " ¿función? Gracias de antemano!

¿Fue útil?

Solución

Siempre que tenga tres funciones con nombres diferentes y necesite elegir una según el tipo, en algún momento deberá tener una sobrecarga o alguna clase de rasgos para elegir la correcta. No creo que haya una forma de evitar eso. Sin embargo, dado que la llamada a una de estas funciones es lo único que necesita esto, si hay más código para estas funciones DownloadXXX () del que usted nos mostró, entonces podría tener sentido.

Aquí hay un bosquejo de lo que podrías hacer usando la alternativa de sobrecarga. Primero necesita tres sobrecargas de la misma función, cada una de las cuales llama a una de las tres funciones diferentes. El parámetro adicional BOOL para una de las funciones causa estragos en la genérica, pero lo solucioné al hacer que todas las funciones acepten ese BOOL , pero dos de ellos lo ignoraron:

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

Ahora puedes ir y escribir esa función genérica. Sin embargo, debe decidir qué hacer con ese 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);
}

Sin embargo, como puede ver, esto realmente solo vale la molestia si esa función Descargar es mucho más complicada que en su código de muestra. De lo contrario, la complejidad añadida supera fácilmente las ganancias que aporta la mayor genericidad.

Otros consejos

Como Ates escribe en su respuesta, todavía tiene que escribir envoltorios para los miembros del CDownloader, por lo que es probable que el resultado final sea tan detallado y difícil de entender que la forma directa. Por ejemplo, esto podría ser una posibilidad (advertencia: código no probado por delante):

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

Intentando ser '' más inteligente '', uno podría intentar hacer un impulso :: fusion :: mapa de los captadores, indexado por el '' getted '' tipo:

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 puede ver, la ganancia en comparación con la forma directa no es obvia.

Creo que los objetos de función son mejores porque puede adaptarse a diferentes firmas.

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

Las llamadas se verían así:

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

y las estructuras pasadas contendrían los objetos descargados. En C ++ 0x podrá definir un concepto que proporcionará una mejor comprobación de tipo en el parámetro de plantilla T.

No creo que escribir un contenedor genérico pueda terminar siendo menos código / duplicación debido al hecho de que las firmas del método para los 3 captadores son diferentes. Necesitará funciones de envoltura alrededor de estos sin importar qué. También podría optar por la forma directa de tener 3 funciones diferentes de Descarga *. podría usar macros para mantener la lógica condicional en una ubicación central, pero eso probablemente haría que su código sea extremadamente ilegible, y no vale la pena.

Puede llegar a algún lugar con un puntero a la función miembro:

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

Ahora la excepción del método getImage lo hace más difícil. Puede intentar hacer que esto funcione con algo como boost :: bind / std :: tr1 :: bind en su lugar.

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

Aquí hay una manera C-hacky de hacerlo.

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

Al final, necesitará una lógica que se relacione de alguna manera con la obtención especializada de enteros / fuentes / imágenes / foobars / monos mágicos. Simplemente lo asimilaría y escribiría una familia Download * ().

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top