2012-03-30 12 views
12

Problema en resumen:
¿Cómo podría implementarse la funcionalidad static if, propuesta en C++ 11, en lenguaje simple C++?estática si está en formato C++?

Historia y problema original:
Recientemente surgió un problema como este. Necesito una clase Sender con una interfaz como

class Sender 
{ 
    void sendMessage(...); 
    void sendRequest(...); 
    void sendFile(...); 
    // lots of different send methods, not important actually 
} 

En algunos casos, voy a necesitar para crear un DoubleSender, es decir, una instancia de esta clase, lo que llamar a sus métodos dos veces, es decir, cuando se llama a, digamos, un método sendMessage(...), el mismo mensaje debe enviarse dos veces.

Mis soluciones:
Primera aproximación:
tienen un miembro de isDouble, y al final de cada llamada al método crea un cheque

sendMessage(...) { ... if(isDouble) { sendMessage(...); } 

Bueno, no quieren esto, porque en realidad necesitaré publicar dos veces recientemente, y esta parte del código en la sección de tiempo crítico será pasiva en un 98%.

Segundo enfoque:
Heredar una clase DoubleSender de Sender, y poner en práctica sus métodos como:

void DoubleSender::sendMessage(...) 
{ 
    Sender::sendMessage(...); 
    Sender::sendMessage(...); 
} 

Bueno, esto es aceptable, pero necesita mucho espacio de código desagradable (realmente mucho, porque hay un montón de diferentes métodos send..

Tercer enfoque:.
Ima Creo que estoy usando C++ 11 :). Entonces puedo hacer esta clase genérica y producir la parte necesaria de código de acuerdo con tempalte argumento usando static if:

enum SenderType { Single, Double }; 
template<SenderType T> 
class Sender 
{ 
    void sendMessage(...) 
    { 
     // do stuff 
     static if (T == Single) 
     { 
     sendMessage(...); 
     } 
    } 
}; 

Esto es más corto, más fácil de leer que las soluciones anteriores, no genera código adicional y ... es C++ 11, que desafortunadamente no puedo usar en mi trabajo.

Así que, aquí es donde llegué a mi pregunta: ¿cómo puedo implementar static if analógico en C++?
Además, agradecería cualquier otra sugerencia sobre cómo resolver mi problema original.
Gracias de antemano.

+1

Se podía ver cómo Boost lo hace con su [static_assert] (http://www.boost.org/doc/libs/1_49_0/doc/html/boost_staticassert.html) tal vez? –

+0

Es posible que desee familiarizarse con boost.mpl – Anycorn

+10

'static if' no existe en C++ 11. –

Respuesta

8

Citando @JohannesSchaubLitb

with my static_if that works on gcc one can do it :)
de alguna manera limitado

(véase también here)

Este truco implica una interpretación GCC específica de las especificaciones de Lambdas en C++ 11.Como tal, se convertirá (probablemente) en un informe de defectos contra el estándar. Esto conducirá a que el truco ya no funcione en la versión más reciente de GCC (ya no funciona en 4.7).

Ver el hilo de comentarios a continuación para algunos detalles más de JohannesS

http://ideone.com/KytVv:

#include <iostream> 

namespace detail { 
template<bool C> 
struct call_if { template<typename F> void operator<<(F) { } }; 

template<> 
struct call_if<true> { 
    template<typename F> 
    void operator<<(F f) { f(); } 
}; 
} 

#define static_if(cond) detail::call_if<cond>() << [&] 

template<bool C, typename T> 
void f(T t) { 
    static_if(C) { 
    t.foo(); 
    }; 
} 

int main() { 
    f<false>(42); 
} 
+0

Eso solo funciona en GCC 4.5.1 debido a un error porque el contenido de la lambda no se verifica correctamente. Debería obtener un "tipo" int "no tiene ningún miembro llamado error" foo ". – Xeo

+1

@Xeo ¿puede ser más preciso?No es un error sino una interpretación de lo que el estándar no define con mucha precisión (la instanciación de miembros de las clases locales no se realizará a menos que GCC y Clang lo requieran). Comeau/EDG lo interpretan de manera diferente y siempre crean una instancia de todos los miembros de la clase local. Tendremos esto como un nuevo informe de problema en la próxima lista de problemas. –

+0

Desafortunadamente, GCC4.7 siempre parece instanciar el operador() (por el motivo que sea), por lo que este truco ya no funciona. Ni para Clang :( –

7

por qué no hacer la puesta en práctica de enviar una política de la clase remitente y el uso CRTP:

template<class Derived> 
class SingleSenderPolicy 
{ 
    public: 
    template< class memFunc > 
    void callWrapperImpl(memFunc f, ...) 
    { 
     static_cast<Derived *>(this)->f(...); 
    } 
}; 

template< class Derived > 
class DoubleSenderPolicy 
{ 
    public: 
    template< class memFunc > 
    void callWrapperImpl(memFunc f, ...) 
    { 
     static_cast<Derived *>(this)->f(...); 
     static_cast<Derived *>(this)->f(...); 
    } 
}; 

template< class SendPolicy> 
class Sender : public SendPolicy<Sender> 
{ 
public: 
    void sendMessage(...) 
    { 
     // call the policy to do the sending, passing in a member function that 
     // acutally performs the action 
     callWrapperImpl(&Sender::sendMessageImpl, ...); 
    } 

    void doSomethingElse(...) 
    { 
     callWrapperImpl(&Sender::doSomethingElseImpl, ...); 
    } 


protected: 
    void sendMessageImpl(...) 
    { 
     // Do the sending here 
    } 

    void doSomethingElseImpl(...) 
    { 
     // Do the sending here 
    } 
}; 

Las funciones públicas sendXXX en su clase simplemente reenvían al contenedor de llamadas, pasando una función miembro que implementa la funcionalidad real. Esta función miembro se llamará según el SendPolicy de la clase. CRTP guarda el uso de bind para envolver los argumentos y este puntero con la función de miembro para llamar.

Con una función, en realidad no reduce la cantidad de código, pero si tiene muchas llamadas, podría ser útil.

Nota: Este código es un esqueleto para proporcionar una posible solución, no se ha compilado.

Nota: Sender<DoubleSenderPolicy> y Sender<SingleSenderPolicy> son tipos completamente diferentes y no comparten una relación de herencia dinámica.

+1

+1 para la respuesta 'seria'. El diseño basado en políticas es de hecho un ajuste natural aquí – sehe

5

mayoría de los compiladores hacen plegado constante y la eliminación de código muerto, por lo que si se escribe un regular si declaración como esta:

enum SenderType { Single, Double }; 
template<SenderType T> 
class Sender 
{ 
    void sendMessage(...) 
    { 
     // do stuff 
     if (T == Single) 
     { 
     sendMessage(...); 
     } 
    } 
}; 

La rama si conseguirá eliminado cuando se genera el código.

La necesidad de static if es cuando las instrucciones causarían un error de compilación. Así que decir que tenía algo como esto (su código un tanto pseudo):

static if (it == random_access_iterator) 
{ 
    it += n; 
} 

Dado que no se puede llamar += en iteradores de acceso no aleatorias, a continuación, el código sería siempre fallan a compilar con un habitual if, incluso con eliminación de código muerto. Porque el compilador aún comprobará la sintaxis antes de eliminar el código. Al usar static if, el compilador omitirá verificar la sintaxis si la condición no es verdadera.

+1

¿realmente se saltará el análisis de sintaxis o solo el análisis semántico? –

+0

@Janus omitirá el análisis semántico. Todavía necesita hacer un análisis sintáctico para encontrar el final del bloque condicional. – Ponkadoodle

0
std::string a("hello world"); 
// bool a = true; 
if(std::is_same<std::string, decltype(a)>::value) { 
    std::string &la = *(std::string*)&a; 
    std::cout << "std::string " << la.c_str() << std::endl; 
} else { 
    bool &la = *(bool*)&a; 
    std::cout << "other type" << std::endl; 
}