Hacer cumplir la verificación de tipo fuerte en C (estrictez de tipo para typedefs)

4 minutos de lectura

avatar de usuario
quinmars

¿Hay alguna manera de hacer cumplir el lanzamiento explícito para typedefs del mismo tipo? Tengo que lidiar con utf8 y, a veces, me confundo con los índices para el recuento de caracteres y el recuento de bytes. Así que sería bueno tener algunos typedefs:

typedef unsigned int char_idx_t;
typedef unsigned int byte_idx_t;

Con la adición de que necesita un elenco explícito entre ellos:

char_idx_t a = 0;
byte_idx_t b;

b = a; // compile warning
b = (byte_idx_t) a; // ok

Sé que tal función no existe en C, pero tal vez conozca un truco o una extensión del compilador (preferiblemente gcc) que lo haga.


EDITAR
Todavía no me gusta mucho la notación húngara en general. No pude usarlo para este problema debido a las convenciones de codificación del proyecto, pero lo usé ahora en otro caso similar, donde también los tipos son los mismos y los significados son muy similares. Y tengo que admitirlo: ayuda. Nunca iría y declararía cada número entero con una “i” inicial, pero como en el ejemplo de Joel para tipos superpuestos, puede salvar vidas.

  • Hay otro artículo muy bueno (aunque no estoy de acuerdo con prohibir goto :)) de Joel llamado Hacer que el código incorrecto parezca incorrecto. Creo que está muy relacionado con su pregunta, incluso si no hay una conexión directa.

    – Ilia

    18 de diciembre de 2008 a las 15:53


  • Lo más ‘interesante’ e importante en ese artículo, en mi humilde opinión, es un poco sobre la historia de la notación húngara. Muuuy interesante…

    – Noein

    21 de mayo de 2015 a las 6:52

Para los tipos de “control” (punteros opacos), Microsoft usa el truco de declarar estructuras y luego escribir un puntero a la estructura:

#define DECLARE_HANDLE(name) struct name##__ { int unused; }; \
                             typedef struct name##__ *name

Entonces en lugar de

typedef void* FOOHANDLE;
typedef void* BARHANDLE;

Ellas hacen:

DECLARE_HANDLE(FOOHANDLE);
DECLARE_HANDLE(BARHANDLE);

Así que ahora, esto funciona:

FOOHANDLE make_foo();
BARHANDLE make_bar();
void do_bar(BARHANDLE);

FOOHANDLE foo = make_foo();  /* ok */
BARHANDLE bar = foo;         /* won't work! */
do_bar(foo);                 /* won't work! */   

avatar de usuario
lilq

Podrías hacer algo como:

typedef struct {
    unsigned int c_idx;
} char_idx;

typedef struct {
    unsigned int b_idx;
} byte_idx;

Entonces verías cuando estás usando cada uno:

char_idx a;
byte_idx b;

b.b_idx = a.c_idx;  

Ahora está más claro que son tipos diferentes pero aún compilarían.

avatar de usuario
libeako

Lo que quiere se llama “typedef fuerte” o “typedef estricto”.

Algunos lenguajes de programación [Rust, D, Haskell, Ada, …] dar algo de apoyo para esto a nivel de idioma, C[++] no. Hubo una propuesta para incluirlo en el lenguaje con el nombre “opaque typedef”, pero no fue aceptada.

Sin embargo, la falta de soporte de idiomas no es realmente un problema. Simplemente envuelva el tipo para crear un alias en una nueva clase que tenga exactamente 1 miembro de datos, de tipo T. Gran parte de la repetición se puede eliminar mediante plantillas y macros. Esta sencilla técnica es tan cómoda como en los lenguajes de programación con soporte directo.

Usa una pelusa. Ver Férula: Tipos y verificación de tipo fuerte.

La verificación de tipo fuerte a menudo revela errores de programación. Splint puede verificar los tipos C primitivos de manera más estricta y flexible que los compiladores típicos (4.1) y proporciona soporte para un tipo booleano (4.2). Además, los usuarios pueden definir tipos abstractos que proporcionan ocultación de información (0).

En C, el único distinción entre tipos definidos por el usuario es decir implementado por el compilador es la distinción entre estructuras. Cualquier typedef que involucre estructuras distintas funcionará. Su principal pregunta de diseño es ¿Deberían los diferentes tipos de estructuras usar los mismos nombres de miembros? Si es así, puede simular algún código polimórfico usando macros y otros trucos de escorbuto. Si no, estás realmente comprometido con dos representaciones diferentes. Por ejemplo, ¿quieres poder

#define INCREMENT(s, k) ((s).n += (k))

y use INCREMENT en ambos byte_idx y char_idx? Luego nombre los campos de manera idéntica.

avatar de usuario
norman ramsey

Usted preguntó acerca de las extensiones. Jeff Foster’s CCalidad es muy agradable, y creo que podría hacer el trabajo que desea.

avatar de usuario
charles cooper

Con C ++ 11 puede usar una clase de enumeración, por ejemplo

enum class char_idx_t : unsigned int {};
enum class byte_idx_t : unsigned int {};

El compilador impondrá una conversión explícita entre los dos tipos; es como una clase de envoltura delgada. Desafortunadamente, no tendrá sobrecarga de operadores, por ejemplo, si desea agregar dos char_idx_t juntos, deberá convertirlos en int sin firmar.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad