2010-05-20 21 views
6

ACTUALIZACIÓN: Después de algunas lecturas adicionales, lo que realmente quería era garantizar la vinculación anticipada (que debería traducirse en una llamada inmediata para funciones no virtuales y código no PIC), lo que se puede hacer pasando una función (miembro) como un parámetro de plantilla. El problema que tuve fue que gcc < 4.5 y icc 11.1 pueden generar algunas instrucciones funky para las llamadas a parámetros de plantilla de puntero de funciones miembro. AFAICT, gcc> = 4,5 y vs2008 manejan bien estas llamadas al parámetro de la plantilla.¿Dónde están los literales de dirección de función en C++?

En primer lugar, tal vez literales no es el término correcto para este concepto, pero es lo más parecido que pude pensar (no literales en el sentido de funciones como ciudadanos de primera clase).

La idea es que cuando se hace una llamada a la función convencional, se compila a algo como esto:

callq <immediate address> 

Pero si usted hace una llamada a la función utilizando un puntero de función, que se compila a algo como esto:

mov <memory location>,%rax 
callq *%rax 

Que está todo bien. Sin embargo, ¿qué sucede si estoy escribiendo una biblioteca de plantillas que requiere una devolución de llamada de algún tipo con una lista de argumentos especificada y se espera que el usuario de la biblioteca sepa a qué función desea llamar al compilar tiempo? Entonces me gustaría escribir mi plantilla para aceptar un literal de función como parámetro de plantilla. Así, similar a

template <int int_literal> 
struct my_template {...};` 

me gustaría escribir

template <func_literal_t func_literal> 
struct my_template {...}; 

y tienen las llamadas a func_literal dentro my_template compilar a callq <immediate address>.

¿Hay alguna instalación en C++ para esto, o una solución alternativa para lograr el mismo efecto? Si no, ¿por qué no (por ejemplo, algunos efectos secundarios cataclísmicos)? ¿Qué hay de C++ 0x u otro idioma?

+0

Este tema es útil, ya que permite intercambiar diferentes funcionalidades durante el tiempo de ejecución. Por ejemplo, la API DOS usa ID para mapear funciones. Esto permite que la funcionalidad de DOS cambie con efectos mínimos para el ejecutable. Simplemente cambie los contenidos (direcciones de funciones) en la tabla de búsqueda. –

+0

Los funtores en combinación con las plantillas son de tiempo de compilación. La mayoría de los STL funcionan así o ¿hay alguna advertencia en su problema? – pmr

+0

@pmr advertencia es que la solución no debería tener ningún impacto en el código de los usuarios; si no están utilizando objetos de función, no quiero obligarlos a usar objetos de función. – academicRobot

Respuesta

3

Si utiliza un tipo de puntero a función en su plantilla y crea una instancia con una función fija, entonces el compilador debe usar una llamada directa para esa llamada de puntero a función.

+0

+1 Acabo de probarlo, funciona muy bien. ¿Qué ocurre con la aceptación de funciones miembro de una clase arbitraria (sin considerar las funciones virtuales)? Tu respuesta me da algunas ideas para esto ... – academicRobot

1

CodeProject.com:

que he usado en varias plataformas: http://www.codeproject.com/kb/cpp/FastDelegate.aspx

vio en los resultados de búsqueda, se lee: http://www.codeproject.com/KB/cpp/ImpossiblyFastCppDelegate.aspx

... o esto no es el tipo de cosa que usted '¿que estas buscando?

+0

El primer enlace todavía usa la llamada de puntero, pensó que era más rápido que las llamadas a función de miembro.No estaba al tanto del segundo, lo investigaré ... – academicRobot

+0

No vi antes que intentaras leer ese segundo enlace. Jugué con eso hace un minuto y sin dados. En realidad, es un poco extraño. Hay una estructura interna que incrusta la llamada fina (en modo inmediato), pero que se llama a través de un puntero a la función. Creo que esto es necesario para obtener interoperabilidad entre los delegados, lo cual no es un requisito aquí. – academicRobot

0

En el lenguaje C++, los traductores no doblan nombres de funciones en el ejecutable, se pierden y desaparecen para siempre.

Puede crear una tabla con el nombre de la función en función de la dirección de la función. Dado que C y C++ son estrictos para la información de tipo, esto puede ser más fácil de declarar en lenguaje ensamblador. En el lenguaje de alto nivel, debe tener una tabla para cada tipo diferente de puntero de función. Sin embargo, en el montaje, no le importa. Aunque podría usar una función con switch para devolver un puntero de función o ejecutar la función.

Una alternativa son los ID de función (enums) frente a las direcciones de función.

+0

No deseo usar el nombre de la función en tiempo de ejecución, quiero usar un sustituto para el nombre de la función en * compile time *. Las soluciones propuestas siguen utilizando punteros a función en tiempo de ejecución. A menos que malinterprete, por favor explique si este es el caso. – academicRobot

0

Al menos si entiendo bien su pregunta, esto es trivial:

template <class func> 
struct whatever { 
    operator()() { 
     func(); 
    } 
}; 

La llamada a func(); normalmente terminar como una llamada directa a func() como lo solicitado, o si func() es pequeña, su código a menudo se generará en línea. Si desea mejorar las posibilidades de que se genere en línea, generalmente desea escribirlo como un funtor (clase que sobrecarga operator()) en lugar de una función normal. Como función normal, con más frecuencia terminará llamando a la función real (pero será una llamada directa, no una llamada a través de un puntero).

Editar: No estoy seguro de lo que estaba pensando, pero tiene toda la razón: esto solo funcionará con un functor, no una función real. Mis disculpas.

+0

Funciona muy bien con un functor. Pero no se compilará para una función: int eco (int i) {return i;} int main (void) {lo whateverObj; return 1;} da "error: se esperaba un tipo, consiguió 'eco '" – academicRobot

+0

Esta fue una de las primeras cosas que intenté, así que no me criticarán :) – academicRobot

+0

Esta es la solución correcta. Consulte 'std :: less ', que usa exactamente el mismo patrón, y por el mismo motivo. La llamada directa es por qué 'std :: sort' beats' std :: qsort'. – MSalters

1
#include <iostream>                

template<void F()>                
struct CALLER                 
{                    
    static void do_call()               
    {                    
    std::cout << __PRETTY_FUNCTION__ << std::endl;        
    F();                   
    };                    
};                    

void f()                   
{                    
    std::cout << __PRETTY_FUNCTION__ << std::endl;         
}                    

int main()                  
{                    
    CALLER<f>::do_call();               
    return(0);                  
}                    
0

Pensé en compartir mi propia solución para funciones estándar, se extendió desde otras respuestas aquí. Esto usa parámetros variados como una mano corta. No sería difícil (simplemente tedioso) hacer esto como un conjunto de plantillas N-ary. Curiosamente, las plantillas N-ary son más flexibles para este patrón, ya que la estructura anidada ya no sería necesaria. Compila con g++ -std=c++0x

template <typename F> 
struct caller; 

template <class R, class ... A> 
struct caller<R(A ...)>{ 
    template <R F(A ...)> 
    struct func{ 
     R operator()(A ... args){ 
     return F(args ...); 
     } 
    }; 
}; 

que se invoca y llama así:

int echoFunc(int i) {std::cout << "echo " << i << std::endl; return i;} 
... 
caller<int(int)>::func<echoFunc> f; 
f(1); 

Sin -O2 compila a dos llamadas de función inmediata anidados, con -O2 la llamada a f(1) reduce a una llamada inmediata a echoFunc.

Esto funciona como se espera para funciones miembro también, con gcc> = 4.5 y vs2008.

0

También es posible pasar una referencia de función a una plantilla. Esto compila en el último clang (3.2) e imprime "¡Hola mundo!" como era de esperar:

template<void(& f)()> struct test { 
    void operator()() { 
     f(); 
    } 
}; 
void foo() { 
    std::cout << "Hello World!\n"; 
} 
test<foo> bar; 
int main() { 
    bar(); 
} 

No estoy seguro de si esto realmente va a hacer la diferencia en comparación con el uso de un puntero de función, sin embargo.

Cuestiones relacionadas