2010-08-04 29 views
7

Hacía tiempo que no usaba C. Y estoy en un proyecto en el que estoy trabajando en una API en C++. La mayoría de estos métodos son solo C de todos modos, y todos los valores devueltos son estructuras C. Excepto uno. Un método que necesito para devolver un vector<string>. Ahora esta es mi pregunta. ¿Los métodos/bibliotecas/C++ de C++ son accesibles desde C? Lo pregunto porque no sé si las personas que usan la API van a escribir en C o C++, y creo que debería devolver solo las estructuras en C. Eso requeriría que devuelva un char**, ¿verdad?llamando a los métodos C++ desde C

espero que tenía sentido, si no:

tl; versión dr - ¿Puedo llamar a un C método ++ de C si se devuelve una estructura C, y si es así es la mejor (sólo?) Valor de retorno equivalente de vector<string> ->char**?

Actualización: Los métodos C++ son simplemente métodos globales. No hay clases u objetos orientados a objetos en ellos. Lo ÚNICO que es específico de C++ además de mi pregunta de vector es unos pocos stringstreams

Respuesta

6

No, C no puede usar las funciones de C++ que tampoco están disponibles en C. Sin embargo, el código C puede hacer uso del código C++ indirectamente. Por ejemplo, puede implementar una función C usando C++, y puede usar tipos opacos en la interfaz para que la firma use void*, pero la implementación usa una clase C++.

El equivalente de vectores <cadena> en C es probablemente más cercano a:

typedef const char* c_string_type; 
typedef struct c_string_array { 
    c_string_type* c_strings; 
    int c_strings_count; 
    } c_string_array_t; 

Con tipos opacos, que tendría algo en la línea de:

typedef void* c_string_array_t; 
int c_string_array_length(c_string_array_t array); 
const char* c_string_array_get(c_string_array_t array, int index); 

A continuación, podría secreto (en la implementación de C++) eche std :: vector * a void *.

+0

¿Por qué no utilizar una estructura para mantener la longitud de la cuerda, también? Salvará al usuario C, que aplica todas las cuerdas cuando ya se conoce la longitud. – Puppy

+0

@DeadMG: No presumas saber qué hará el usuario con sus datos. Puede incurrir en penalidad de rendimiento sin ningún motivo. Es posible que ni siquiera sean cadenas de texto. –

+0

@Michael: No use _t, esto reservado por POSIX. –

5

Ver this FAQ. Básicamente, no puede llamar a los métodos de C++ (funciones de miembro), pero puede llamar a funciones autónomas si se declaran con C. externa char ** no es la única posibilidad, pero es probablemente la más directa. Puede devolver una matriz dinámicamente asignada de char *. Deberá usar un parámetro de salida para proporcionar la longitud a la persona que llama (podría terminarla NULA, pero probablemente no sea la ideal). P.ej.

char **get_string_list(size_t *len) 
{ 
    char **array; 
    size_t actual_len; 
    // ... 
    *len = actual_len; 
    array = (char **) malloc(sizeof(char *) * actual_len); 
    // ... 
    return array; 
} 

Debe de alguna manera liberar la memoria. Proporcione una función o documente cómo la persona que llama debería hacerlo. No olvides liberar las cadenas individuales si están dinámicamente asignadas.

+0

Supongo que en una nota relacionada, ¿puede liberar memoria en C que se asignó con 'new' en C++? – Falmarri

+8

No. Casos simples podrían funcionar casualmente en algunos compiladores (ya que 'new' a menudo se implementa con' malloc'), pero definitivamente es incorrecto. –

0

Leí algunas líneas sobre esto hace algún tiempo, y iirc puede usar código/estructuras de C++ que se compilarían en C. Pero hay algo sobre la inicialización estática, si entendí correcto, debe escribir su función principal en C++ para garantizar que este está hecho, C main no funciona.

+0

No estamos escribiendo nada en main, ni nada de eso. Estamos escribiendo una API para interactuar con nuestro servidor de socket – Falmarri

6

Puede llamar técnicamente cualquier cosa desde C, siempre que se proporcione un nombre de función C-visible (los prototipos, etc. se ignoran en el nivel ABI). Por supuesto, no puede esperar resultados correctos si C no puede generar los parámetros de la manera esperada. En general, la solución obvia es simplificar la interfaz hasta el nivel C. char ** es una opción excelente para el mayor denominador común con vector<string>. No solo eso, si sabes lo que piensas hacer con él, posiblemente más rápido (y en mi humilde opinión).

Con respecto a la visibilidad C: El nombre de la función no se puede compartir con ninguna otra función visible de C. Si desea que su C++ función sea exigible de C, esto podría ser un buen ejemplo del prototipo:

extern "C" char **lots_of_strings(); 

Si la firma parámetro difiere, C++ le permitirá sobrecarga con funciones visibles sólo desde C++, y permitirles coexistir con la versión C:

vector<string> lots_of_strings(int); 
extern "C" char **lots_of_strings(); 

Si desea proporcionar varias formas de llamarlo, apropiado para el idioma, pidiendo, puede probar esto (ignorando el mal en los últimos tiempos de inicialización, y el hecho de que bool existe en C):

bool lots_of_strings(vector<string> &); 
extern "C" int lots_of_strings(char ***); 
Whatever lots_of_strings(SomeArrayType &); 

Teniendo en cuenta que en cada caso, C++ elegirá la definición con la mejor firma coincidente para el sitio de la llamada, y C tomará todo lo que pueda (que siempre es una sola función con un nombre coincidente).

Le resultará útil ocultar los isósforos de C++ de C combinando #ifdef con la macro __cplusplus.

+0

+1 por ofrecer diferentes interfaces a C y C++, incluso si las interfaces de muestra no fueran mi primera opción. –

+0

Me preocupa cuál sería tu primera opción en ese momento. Teniendo en cuenta los requisitos de OP, cualquier cosa más que un 'vector ' o 'char **' son overkill _immediately_ overkill. –

0

Si tiene la intención de ofrecer la misma funcionalidad tanto para C como para C++, trataría de ofrecer dos puntos de entrada para que pueda adaptar uno al otro. Tenga en cuenta que si bien puede conformarse con una interfaz que puede usarse tanto de C como de C++, en la mayoría de los casos, la interfaz C no será ideal para C++ (usar char** podría ser una buena solución para C, pero que los usuarios la conviertan nuevamente en Los tipos de C++ y realizar la limpieza pueden saturar el código de usuario) y viceversa.

0

Si tienes la posibilidad de modificar el código que está llamando Yo sugeriría cambiar a la función de devolución de un vector a algo como:


unsigned int MyFunction(char* buff, unsigned int numLines, unsigned int stride) 

Dónde numLines es la cantidad de cuerdas asignados y zancada es el tamaño de las cuerdas. De esta forma, su función no necesita asignar memoria de la que deba preocuparse más adelante. Todo es manejado por la persona que llama. El valor de retorno de la función es la cantidad de cadenas que se utilizó.

Cuestiones relacionadas