2009-12-03 27 views
32

Estoy tratando de usar una biblioteca de C en una aplicación de C++ y me he encontrado en la siguiente situación (conozco mi C, pero soy bastante nuevo en C++). En el lado C, tengo una colección de funciones que toma un puntero a la función como argumento. En el lado de C++, tengo objetos con un funtor que tiene la misma firma que el puntero de función que necesita la función C. ¿Hay alguna forma de utilizar el functor de C++ como un puntero de función para pasar a la función C?función de pase como puntero de función

Respuesta

46

No se puede pasar directamente un puntero a un objeto functor C++ como un puntero a la función código C (o incluso al código C++).

Además, para pasar portable una devolución de llamada a código C tiene que ser al menos declarado como una función extern "C" no miembro. Al menos, debido a que algunos API requieren convenciones de llamada de función específicas y por lo tanto modificadores de declaración adicionales.

En muchos entornos de C y C++ tienen las mismas convenciones de llamada y sólo se diferencian en renombrado de nombres, por lo que cualquier función global o miembro estático funcionarán. pero aún necesita para envolver la llamada a operator() en una función normal.

  • Si el funtor no tiene un estado (que es un objeto sólo para satisfacer algunas requisitos formales, etc.):

    class MyFunctor { 
        // no state 
    public: 
        MyFunctor(); 
        int operator()(SomeType &param) const; 
    } 
    

    puede escribir una función normal extern "C", que crea el funtor y ejecuta su operador ().

    extern "C" int MyFunctorInC(SomeType *param) 
    { 
        static MyFunctor my_functor; 
        return my_functor(*param); 
    } 
    
  • Si el funtor tiene estado, por ejemplo:

    class MyFunctor { 
        // Some fields here; 
    public: 
        MyFunctor(/* some parameters to set state */); 
        int operator()(SomeType &param) const; 
        // + some methods to retrieve result. 
    } 
    

    y la función C de devolución de llamada toma algún tipo de parámetro de estado de usuario (por lo general void *):

    void MyAlgorithmInC(SomeType *arr, 
            int (*fun)(SomeType *, void *), 
            void *user_state); 
    

    se puede escribir una función normal extern "C", que proyecta su parámetro de estado a su objeto funtor:

    extern "C" int MyFunctorInC(SomeType *param, void *user_state) 
    { 
        MyFunctor *my_functor = (MyFunctor *)user_state; 
        return (*my_functor)(*param); 
    } 
    

    y utilizar de esta manera:

    MyFunctor my_functor(/* setup parameters */); 
    MyAlgorithmInC(input_data, MyFunctorInC, &my_functor); 
    
  • lo contrario, la única manera normal a hacerlo (normal como en "sin generar código de máquina en tiempo de ejecución", etc.) es utilizar algún almacenamiento estático (global) o de subproceso local para pasar el funtor a una función externa "C". Esto limita lo que puede hacer con su código y es feo, pero funcionará.

+0

+1, una descripción detallada de posibles casos. –

+0

+1, aunque la firma de devolución de llamada definitivamente no es C :) – quinmars

+0

oops, nvm, de alguna manera mezclé las declaraciones y pensé que estás usando referencias en C aquí. – quinmars

2

No creo que pueda: operator() en un objeto de función es realmente una función miembro, y C no sabe nada de eso.

Lo que debería poder usar son funciones C++ gratuitas o funciones estáticas de clases.

0

Depende si este es un método estático o de instancia, si es estático, puede pasar a través de la función como className :: functionName, si es un método de instancia es justo más complicado, porque obviamente necesita atar en cierta instancia, pero no puede hacerlo de la misma manera que lo haría con los delegados en C# etc.

La mejor manera que he encontrado de hacer esto es crear una clase de retención que se instancia con la instancia de tanto el objeto como el puntero a la función, la clase de retención puede invocar la función directamente.

3

No, por supuesto. La firma de su función C toma un argumento como función.

void f(void (*func)()) 
{ 
    func(); // Only void f1(), void F2(), .... 
} 

Todos los trucos con funtores son utilizados por plantilla funciones:

template<class Func> 
void f (Func func) 
{ 
    func(); // Any functor 
} 
0

yo diría que no, porque un C++ funtor tiene un operador sobrecargado () que es una función miembro de, y se por lo tanto, requieren un puntero de función miembro. Este es un tipo de datos totalmente diferente de un puntero de función C normal, ya que no se puede invocar sin una instancia de la clase. Tendría que pasar una función normal o una función miembro estática a la biblioteca C. Como un operador () sobrecargado no puede ser estático, no puede hacerlo. Tendría que pasarle a la biblioteca C una función normal, función no miembro o función miembro estática, desde la cual puede invocar el functor C++.

0

Hm, tal vez podría escribir una función de plantilla gratuita que envuelva sus objetos funcionales. Si todos tienen la misma firma, esto debería funcionar. Me gusta esto (no probado):

template<class T> 
int function_wrapper(int a, int b) { 
    T function_object_instance; 

    return funcion_object_instance(a, b); 
} 

Esto haría para todas las funciones que toman dos ints y devuelve un int.

7

Encontré este "gem" usando google. Aparentemente posible, pero seguro que no lo recomendaría. Directo link al código fuente de ejemplo.

+0

No lo use. Debe ser trivial para envolver su functor en una función C normal y no requiere un hack feo. –

+3

yuck, esto es de hecho "magia negra" ya que el nombre de categoría bajo el cual se archivó sugiere – 0xC0000022L

+0

Pure gem Creo que esto es solo así, porque c llamador no sabe cómo presionar parámetros adicionales. La función se crea en RAM que tiene esta llamada "const". –

3

Una función de devolución de llamada C escrita en C++ debe declararse como una función extern "C", por lo que el uso de un functor está directamente fuera. Tendrá que escribir algún tipo de función de envoltura para usar como devolución de llamada y hacer que ese envoltorio llame al funtor. Por supuesto, el protocolo de devolución de llamada deberá tener alguna forma de pasar el contexto a la función para que pueda llegar al functor, o la tarea se vuelve bastante complicada. La mayoría de los esquemas de devolución de llamada tienen una forma de pasar el contexto, pero he trabajado con algunos de muerte cerebral que no lo hacen.

Ver esta respuesta para algunos detalles más (y busque en los comentarios de la evidencia anecdótica de que la devolución de llamada debe ser extern "C" y no sólo una función miembro estática):

0

Muchos C Las API que toman devoluciones de llamada de puntero a función tienen un parámetro void * para el estado del usuario. Si usted tiene uno de esos, estás de suerte - se puede utilizar una función exterm C que trata los datos del usuario como una especie de referencia o clave para buscar el funtor, a continuación, ejecutarlo.

De lo contrario, no.

1

GCC le permite convertir los punteros de función miembro de punteros a funciones de civil (el primer argumento de la función llamada por el puntero de función es claro entonces this).

Revisa el respective link in the manual.

Esto requiere el indicador -Wno-pmf-conversions para silenciar la advertencia respectiva para la característica decididamente no estándar. Muy conveniente para interconectar bibliotecas de estilo C con programación de estilo C++. Cuando el puntero de la función miembro es una constante, esto ni siquiera necesita generar ningún código: la API usaría ese orden de argumento de todos modos.

Si ya tiene un funtor, aplanar el funtor de esa manera probablemente signifique aplanamiento de su operator(), proporcionándole una función que debe invocarse con un puntero de clase funtor como su primer argumento. Lo cual no necesariamente ayuda mucho, pero al menos tiene un enlace C.

Pero al menos cuando no está pasando por funtores esto es útil y proporciona un reemplazo de enlace C sin sentido para std::mem_fn de <functional>.

Cuestiones relacionadas