2011-11-10 24 views
6

Quiero escribir una macro para escribir una cadena, utilizando la optimización en tiempo de compilación de conocer la longitud de una cadena literal. Pero necesito detectar el uso incorrecto, usando punteros.Cómo distinguir cadena constante de char * en C macro

Esto es lo que quiero decir:

void transmit(const char *data, int length); 
#define tx_string(x) transmit((x), sizeof(x) -1) 
void func(char *badmsg) { 
    tx_string("GO"); // ok 
    tx_string(badmsg); // not OK 
} 

Con la segunda llamada, el tamaño será sin sentido (sizeof un puntero).

Quiero generar un error de tiempo de compilación si intento usar tx_string en cualquier cosa que no sea una cadena literal. Esto está usando gcc; ¿Hay algo de gcc que pueda usar para hacer esto?

Editar: Trabajo con almacenamientos intermedios de datos que pueden contener nulos o no ser terminados por nulos. REALMENTE quiero evitar el uso de punteros para esto y evitar el uso en tiempo de ejecución strlen().

Edición 2:

He aquí un ejemplo que podría causar un problema. Voy a inventar un protocolo imaginario donde tengo que decirle a un microcontrolador de 16 bits una dirección usando el comando GO seguido de una dirección como raw de 16 bits (dos caracteres de 8 bits), y quiero ir desde la dirección 0.

#define GOSEQ "GO\000\000" 

void func(void) { 
    char *bad = GOSEQ; 
    tx_string(GOSEQ); // ok, sends 4 bytes: GO and two zero bytes 
    tx_string(bad); // bad, using runtime strlen sends two characters "GO" 
} 

Estoy seguro de que debe haber algún tipo de control integrado de gcc para esto. Veo mucho las fuentes del kernel de Linux usando trucos de distinción en tiempo de compilación como este ... pero no puedo echar mano de uno específico rápidamente.

Hasta ahora la idea del "programador de Windows" parece buena, pero un error de tiempo de compilación más significativo sería una ventaja.

Respuesta

9

En general, ya que no puede utilizar la concatenación de cadenas con los punteros, etc., tal vez se puede utilizar:

#define STRLIT(x) x "" 

Si el argumento a STRLIT no es una cadena literal, obtendrá un error de compilación.

La adaptación general a la macro específica:

#define tx_string(x) transmit((x ""), sizeof(x) - 1) 
+0

¡Esto parece un ganador! Esperaré un poco en caso de que alguien presente la prueba integrada perfecta o algo así. – blueshift

4

usted puede hacer esto:

#define tx_string(x) transmit(x, strlen(x)) // No need for extra parentheses 

GCC optimizará el llamado strlen, y estoy seguro de que otros compiladores también lo hará. No pierdas tu tiempo en esto.

Archivo de prueba:

#include <string.h> 
void transmit(const char *, size_t); 
#define tx_string(x) transmit(x, strlen(x)) 
void func(void) 
{ 
    tx_string("abcdef"); 
} 

resultante de montaje:

I recortado ruido fuera del conjunto, se trata de las cosas importantes. Note que nunca llama strlen, sino que sólo utiliza el número 6:

.LC0: 
    .string "abcdef" 

func: 
    movl $6, %esi 
    movl $.LC0, %edi 
    jmp  transmit 
+0

bien, pero trabajar con búfer de datos que pueden contener valores nulos o no ser terminado por nulos. REALMENTE deseo evitar el uso de punteros para esto y evitar el uso de Strlen() en tiempo de ejecución. – blueshift

+1

@blueshift: en C, los punteros y matrices son intercambiables como argumentos para las funciones. ¿Por qué es necesario evitar que se pasen los indicadores? No tiene sentido para mí. Tampoco estoy seguro de por qué necesita evitar el uso de 'strlen' en tiempo de ejecución. ¿Podrías explicar eso? –

+0

@Deitrich Epp: mi comentario anterior debería explicarlo. Editaré la pregunta para que quede más clara. – blueshift

1
#define tx_string(x) ("i came, i concatenated, i discarded " x, transmit((x), sizeof(x) - 1)) 

no es perfecto porque algún gracioso podría llamar tx_string (1)

+0

Veo lo que hiciste allí. Furtivo. Me sorprende que sizeof (+1) compile. En realidad, si la cadena de descarte se mueve a la derecha de x, se ve bastante bien ... no se puede pensar inmediatamente en nada inválido que se compile. – blueshift

+0

Tiene razón, la respuesta de Jonathan Leffler resuelve mi defecto menor. Mientras tanto, no debería ser realmente sorprendente que el tamaño de algunas compilaciones int y su valor sea el tamaño de una int. –