2011-03-04 13 views
23

Soy consciente de que en C no se puede convertir implícitamente, por ejemplo, char**-const char** (compárese con C-Faq, SO question 1, SO Question 2).C idiomática para la const dobles triples

Por otro lado, si veo una función declarada como tal:.

void foo(char** ppData); 

debo asumir la función, puede cambiar los datos pasados ​​en Por lo tanto, si estoy escribiendo una función que no cambiar los datos, es mejor, en mi opinión, a declarar:

void foo(const char** ppData); 

o incluso:

void foo(const char * const * ppData); 

Pero eso pone a los usuarios de la función en una posición incómoda. Puede ser que tengan :

int main(int argc, char** argv) 
{ 
    foo(argv); // Oh no, compiler error (or warning) 
    ... 
} 

Y con el fin de llamar limpiamente mi función, por lo que necesitaría para insertar un yeso.

Vengo de un fondo en su mayoría C++, donde esto es un problema menor debido a las reglas de const más profundas de C++.

¿Cuál es la solución idiomática en C?

  1. Declarar foo como tomar una char**, y acaba de documentar el hecho de que no va a cambiar sus entradas? Eso parece un poco asqueroso, esp. ya que castiga a los usuarios que podrían tener un const char** que quieren pasarlo (ahora tienen que emitir distancia const-ness)

  2. usuarios de la Fuerza de emitir su entrada, añadiendo const-dad.

  3. ¿Algo más?

+1

Supongo que 2) está bien. – Andrey

+2

Creo que 2) es apropiado. –

+0

1) 'const' es una anotación de compilador inútil en C. –

Respuesta

6

2 es mejor que 1. 1 es bastante común, ya que los grandes volúmenes de código C no usan const en absoluto. Entonces, si está escribiendo un código nuevo para un nuevo sistema, use 2. Si está escribiendo un código de mantenimiento para un sistema existente donde const es una rareza, use 1.

2

Vaya con la opción 2. La opción 1 tiene la desventaja que mencionó y es menos seguro.

Si vi una función que toma un argumento char ** y tengo un char *const * o similar, haría una copia y la pasaría, por las dudas.

+0

Buenas respuestas: Les di una botera y arrojé una moneda por la respuesta (: Parece que no hay una bala de plata. – jwd

+0

nmichaels fue el primero, así que es justo que acepte. –

7

Aunque ya has aceptado una respuesta, me gustaría ir por 3) a saber, macros. Puede escribir estos de una manera que el usuario de su función simplemente escriba una llamada foo(x); donde x puede ser const -calificado o no.La idea sería tener una macro CASTIT que hace el reparto y comprueba si el argumento es de un tipo válido, y otra que es la interfaz de usuario:

void totoFunc(char const*const* x);  
#define CASTIT(T, X) (    \ 
    (void)sizeof((T const*){ (X)[0] }), \ 
    (T const*const*)(X)     \ 
) 
#define toto(X) totoFunc(CASTIT(char, X)) 

int main(void) { 
    char  *  * a0 = 0; 
    char const*  * b0 = 0; 
    char  *const* c0 = 0; 
    char const*const* d0 = 0; 
    int  *  * a1 = 0; 
    int const*  * b1 = 0; 
    int  *const* c1 = 0; 
    int const*const* d1 = 0; 

    toto(a0); 
    toto(b0); 
    toto(c0); 
    toto(d0); 
    toto(a1); // warning: initialization from incompatible pointer type 
    toto(b1); // warning: initialization from incompatible pointer type 
    toto(c1); // warning: initialization from incompatible pointer type 
    toto(d1); // warning: initialization from incompatible pointer type 
} 

La macro CASTIT parece un poco complicado, pero todo lo que hace es comprobar primero si X[0] es asignación compatible con char const*. Utiliza un compuesto literal para eso. Esto está oculto dentro de un sizeof para asegurar que en realidad el compuesto literal nunca se crea y que esa prueba no evalúa X.

Luego sigue un molde simple, pero que por sí mismo sería demasiado peligroso.

Como puede ver en los ejemplos en el main, esto detecta exactamente los casos erróneos.

Mucho de eso es posible con las macros. Recientemente cociné un ejemplo complicado with const-qualified arrays.

+0

Este es un ejemplo de cómo C se ofusca. Un mantenedor posterior va a tener dificultades para entender lo que está sucediendo aquí. A menos que el nombre sea todo mayúsculas, supondría que 'toto()' era una llamada de función, a menos que hubiera un comentario en todas partes que se usó. El mantenedor posterior recibió una advertencia, tomaría un tiempo incómodamente largo para averiguar por qué, ya que el compilador no le informa acerca de la macro. – atlpeg

+0

@atlpeg: Si todo lo que le molesta es que uno debe usar todas las letras mayúsculas, esto no es grandes ofertas, tómalo como 'TOTO' si quieres. Por lo demás, veo tu punto, pero no estoy totalmente de acuerdo. La falta de posibilidad de convertir 'char **' a 'char const * const *' es claramente una defecto del lenguaje. C++ obtiene un camino con eso mucho mejor. Obligar a los usuarios con las soluciones 1 o 2 a introducir mucho lanzamiento es una pesadilla para mantener y realmente peligroso, porque C solo usa un tipo de yeso. Entonces, si un usuario cambia el tipo de 'char' a' int', el elenco simplemente lo aceptará. –

+0

Definitivamente interesante. No estoy seguro de usarlo, pero interesante (: – jwd