Pregunta

¿Es posible establecer un conjunto de punteros de función con plantilla, sin la molestia de hacerlo manualmente?Aquí hay un ejemplo para ilustrar de qué diablos estoy hablando.

Digamos que tengo una función llamada "escribir" con frecuencia, de la cual tengo dos implementaciones (escribir0 y escribir1) entre las que me gustaría poder cambiar dinámicamente.Estas funciones de escritura tienen una plantilla según el tipo de argumento.Una forma de hacer esto es simplemente tener una función de front-end con plantilla write() que utilice internamente una declaración if.

Esto resulta ser lo suficientemente rápido para mis necesidades, pero ahora me preguntaba si puedo hacer lo mismo usando punteros de función (solo por diversión).El problema con este enfoque es que configurar los punteros de función es una molestia.¿Existen otras formas de lograr esencialmente el ideal de write() pero sin el condicional (despacho estático directo)?

(Otras "reglas":No puedo cambiar las clases de Msg para que tengan métodos write() y no puedo cambiar el código de uso del sitio para reemplazar Msgs con adaptadores para Msgs.)

FWIW, encontré Este artículo Básicamente digo lo mismo que digo aquí.

#include <iostream>
using namespace std;

template<typename T> void write0(T msg) { cout << "write0: " << msg.name() << endl; }
template<typename T> void write1(T msg) { cout << "write1: " << msg.name() << endl; }

// This isn't so bad, since it's just a conditional (which the processor will
// likely predict correctly most of the time).
bool use_write0;
template<typename T> void write(T msg) { if (use_write0) write0(msg); else write1(msg); }

struct MsgA { const char *name() { return "MsgA"; } };
struct MsgB { const char *name() { return "MsgB"; } };
struct MsgC { const char *name() { return "MsgC"; } };
struct MsgD { const char *name() { return "MsgD"; } };

// This doesn't work: templates may not be virtual.
#if 0
struct Writer { template<typename T> virtual void write(T msg) = 0; };
struct Writer0 { template<typename T> virtual void write(T msg) { cout << "write0: " << msg.name() << endl; } };
struct Writer1 { template<typename T> virtual void write(T msg) { cout << "write0: " << msg.name() << endl; } };
#endif

int main(int argc, char **argv) {
  use_write0 = argc == 1;

  // I can do this:
  write(MsgA());

  // Can I achieve the following without the verbosity (manual setup, named
  // template instantiations, etc.)?
  void (*pwriteA)(MsgA) = use_write0 ? (void(*)(MsgA)) write0<MsgA> : (void(*)(MsgA)) write1<MsgA>;
  void (*pwriteB)(MsgB) = use_write0 ? (void(*)(MsgB)) write0<MsgB> : (void(*)(MsgB)) write1<MsgB>;
  void (*pwriteC)(MsgC) = use_write0 ? (void(*)(MsgC)) write0<MsgC> : (void(*)(MsgC)) write1<MsgC>;
  void (*pwriteD)(MsgD) = use_write0 ? (void(*)(MsgD)) write0<MsgD> : (void(*)(MsgD)) write1<MsgD>;
  pwriteA(MsgA());
  pwriteB(MsgB());
  pwriteC(MsgC());
  pwriteD(MsgD());

  return 0;
}
¿Fue útil?

Solución

Si desea cambiar las funciones de registro de ida y vuelta, mientras que el programa se ejecuta, yo creo que hay que configurar manualmente el puntero de función para cada tipo.

Si es suficiente con sólo seleccionar la función de registro en el arranque, se puede hacer de una manera totalmente genérica sin siquiera saber para qué tipo de la función se llamará más tarde:

// writer functions
template<typename T> void write0(T msg) { std::cout << 0; };
template<typename T> void write1(T msg) { std::cout << 1; };

// global flag
bool use_write0;

// function pointers for all types
template<typename T>
struct dispatch {
   typedef void (*write_t)(T);
   static write_t ptr;
};

// main write function
template<typename T>
inline void write(T msg) {
   (*dispatch<T>::ptr)(msg);
}

// the fun part
template<typename T>
void autoinit(T msg) {
   if (use_write0)
      dispatch<T>::ptr = &write0<T>;
   else
      dispatch<T>::ptr = &write1<T>;
   // call again for dispatch to correct function
   write(msg);
}

// initialization
template<typename T>
typename dispatch<T>::write_t dispatch<T>::ptr = &autoinit<T>;

// usage example
int main(int argc, char **argv) {
   use_write0 = (argc == 1);
   write("abc");
   return 0;
}

Para cada tipo T la primera llamada a write<T>() decide qué función de escritura debe ser usado. llamadas posteriores a continuación, utilizar directamente la función de puntero a esa función.

Otros consejos

También es posible usar FastDelegates cabecera de don Clugston. Genera ninguna sobrecarga en tiempo de ejecución en absoluto y verdaderamente delegados orientada a objetos. Mientras que la sintaxis para su uso no es perfecto, que es un poco más simple que jugando con los punteros de función primas.

¿Por qué no se utiliza una matriz de punteros de función?

#include <iostream>
using namespace std;

template<typename T> void write0(T msg) { cout << "write0: " << msg.name() << endl; }
template<typename T> void write1(T msg) { cout << "write1: " << msg.name() << endl; }

template<typename T> struct WriteSelector
{
    static void(* const s_functions[])(T msg);
};
template<typename T> void(* const WriteSelector<T>::s_functions[])(T msg)=
{
    &write0<T>,
    &write1<T>
};

unsigned write_index=0;
template<typename T> void write(T msg)
{
    WriteSelector<T>::s_functions[write_index](msg);
}


struct MsgA { const char *name() { return "MsgA"; } };
struct MsgB { const char *name() { return "MsgB"; } };
struct MsgC { const char *name() { return "MsgC"; } };
struct MsgD { const char *name() { return "MsgD"; } };

void Test()
{
    write(MsgA());
    write(MsgB());
    write(MsgC());
    write(MsgD());
}

int main()
{
    Test();
    write_index=1;
    Test();
    return 0;
}

Hay dos axises de variación en la escritura: el / la elección write1 write0 y la MSGA / B / C .... elección.

Conceptualmente que significa que necesita implementaciones NxM de una función write. Por supuesto, si se añade una aplicación de escritura, o se añade un tipo de mensaje, esto lleva a resp. M o N funciones adicionales que se añaden.

Para ambos axises puede elegir si se debe implementar usando polimorfismo estático o dinámico. El polimorfismo estática se puede hacer uso de plantillas o el uso de las anulaciones de función.

Se podría hacer mediante la creación de una jerarquía de clases de N elementos con funciones M de escritura en cada clase. Pero pronto se convertiría en una pesadilla de mantenimiento. También a menos que el contenido del mensaje es tiempo de ejecución polimórfica. Pero la pregunta es sobre el polimorfismo estático para los mensajes.

Desde el polimorfismo en tiempo de ejecución se descartó debido a demasiado elaborado (y no se puede tener una función de plantilla virtual, lo que disminuiría el nivel de detalle de las anulaciones), tenemos que aplicar una pequeña rutina tipo de despacho, la conversión de la información en tiempo de ejecución en la compilación -tiempo información.

Más específicamente:. Crear plantillas de la acción principal (en el ejemplo se llama Tmain) con el uso escritor a, y lo llaman con el argumento de plantilla desde el main 'real'

Esto omite el uso de una variable de elección 'global', sin embargo, es orientado a objetos y conciso.

    // twodimensionalpolymorph.cpp
    //

    #include <iostream>

    using namespace std;

    class Write0 {
        public: 
        template< typename tMsg > 
        void operator()( /*const*/ tMsg& msg ) { cout << "write0: " << msg.name() << endl; };
    };

    class Write1 {
        public: 
        template< typename tMsg > 
        void operator()( /*const*/ tMsg& msg ) { cout << "write1: "<< msg.name() << endl; };
    };

    struct MsgA { const char *name() { return "MsgA"; } };
    struct MsgB { const char *name() { return "MsgB"; } };
    struct MsgC { const char *name() { return "MsgC"; } };
    struct MsgD { const char *name() { return "MsgD"; } };

    // the Tmain does the real action
    //
    template< typename Writer >
    int Tmain( Writer& write, int argc, char** args ) {

        write( MsgA() );
        write( MsgB() );
        write( MsgB() );
        write( MsgD() );

        return 0;
    }

    // the main merely chooses the writer to use
    //
    int main( int argc, char** args ) {

        if( argc==1 )
            return Tmain( Write0(), argc, args);
        else
            return Tmain( Write1(), argc, args);

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