¿Se considera una mala práctica typedef’ing un tipo de puntero? [duplicate]

10 minutos de lectura

avatar de usuario
Blagovest Büyükliev

Posible duplicado:

¿Los punteros Typedef son una buena idea?

He visto esta rareza en muchas API que he usado:

typedef type_t *TYPE;

Mi punto es que declarar una variable de tipo TYPE no dejará claro que, de hecho, se declara un puntero.

¿Tú, como yo, piensas que esto trae mucha confusión? ¿Esto está destinado a hacer cumplir la encapsulación, o también hay otras razones? ¿Consideras que esto es una mala práctica?

  • Solo una de las respuestas a “¿Los punteros Typedef son una buena idea?” alude a la inútil interacción con const – y esa respuesta tiene una calificación baja. en mi humilde opinión const problema es el problema más importante con este tipo de typedefs,… por lo tanto, no es realmente un duplicado.

    – Juan Marshall

    23 de septiembre de 2010 a las 20:48


  • @John: No estoy de acuerdo con tu conclusión. Ha notado que una respuesta agregada mucho después de que se formuló la pregunta no ha recibido mucha atención, pero las preguntas siguen siendo fundamentalmente las mismas.

    – dmckee — gatito ex-moderador

    23 de septiembre de 2010 a las 20:56


  • @dmckee: Es justo, supongo que mi punto real es “no debería cerrarse inmediatamente como un duplicado, ya que sus respuestas brindan información nueva e interesante”. Y realmente mi punto es que Blagovest debería haber esperado un poco más para aceptar mi responder 🙂

    – Juan Marshall

    23 de septiembre de 2010 a las 21:05

  • @John Marshall: acepté tu respuesta ahora porque tenía que dormir 🙂

    – Blagovest Büyükliev

    24 de septiembre de 2010 a las 7:27

  • Ves mucho de esto en la API de Windows. Lo detesto absolutamente. Oculta el tipo y eso genera más confusión. yo prefiero int* i over PINT = Supongo que la herencia BÁSICA de Windows los lleva a tratar de ocultar los punteros. En segundo lugar, todo en mayúsculas se ve horrible. De hecho, nunca me molesté con la API win32 porque es demasiado complicada.

    – WKS

    26 de junio de 2012 a las 5:41

En general, es una mala práctica. El problema importante es que no funciona bien con const:

typedef type_t *TYPE;
extern void set_type(TYPE t);

void foo(const TYPE mytype) {
  set_type(mytype);  // Error expected, but in fact compiles
}

Para que el autor de foo() para expresar lo que realmente significan, la biblioteca que proporciona TYPE también debe proporcionar CONST_TYPE:

typedef const type_t *CONST_TYPE;

así que eso foo() puede tener la firma void foo(CONST_TYPE mytype)y en este punto hemos descendido a la farsa.

De ahí una regla general:

Haga typedefs de estructuras (particularmente estructuras incompletas), no punteros a esas estructuras.

Si la definición de la estructura subyacente no debe estar disponible públicamente (lo que a menudo es loable), entonces esa encapsulación debe ser proporcionada por la estructura incompleta, en lugar de por inconvenientes typedefs:

struct type_t;
typedef struct type_t type_t;

void set_type(type_t *);
int get_type_field(const type_t *);

  • Es interesante que algunos tipos no parezcan necesitar una versión const. Por ejemplo, nunca he sentido ningún deseo de una const_pthread_t en el que puedo llamar pthread_getschedparam pero no setschedparam. const pthread_t no logra eso (por supuesto), al igual que el puntero typedefed no lo logra en su ejemplo. Y ftell toma FILE*no const FILE*, aunque supongo que debería ser const, para admitir el uso seguro de FILE*. Entonces creo que esto const problema es significativo/decisivo en algunos casos, pero no siempre. ¿El legado anterior a la construcción de C se muestra, tal vez? O simplemente pereza 🙂

    –Steve Jessop

    23 de septiembre de 2010 a las 22:25


  • Por supuesto. Miré para ver si algo en el estándar había sido actualizado con const FILE*, Pero no tanto. (Y ahí estaba yo esperando ayudarte a entender FILE*! :-)) Tal vez tenga que ver con si algo se siente como un tipo atómico — uno no escribe mucho const int cualquiera. O una especie de problema del huevo y la gallina: si ninguna de las funciones de consulta const FILE*¿por qué escribirías tu propio foo() función para tomar uno, ¿cómo usaría su parámetro?

    – Juan Marshall

    23 de septiembre de 2010 a las 22:37

  • ftell No puede tomar const FILE * porque (en POSIX o cualquier otra implementación con subprocesos) debe obtener un bloqueo mutex en el FILE objeto a menos que la posición actual se pueda obtener con una sola operación atómica. toda la idea de un const FILE * es más o menos una tontería; no se podía usar para nada.

    – R.. GitHub DEJA DE AYUDAR A ICE

    24 de septiembre de 2010 a las 2:26

  • +1 para “descendió a la farsa”

    – olovb

    4 de mayo de 2012 a las 19:08

  • Este ejemplo de una estructura incompleta (en lugar de un puntero a una estructura) para la encapsulación es importante: muchos ejemplos que he visto usan un typedef en un puntero y esto genera problemas que pueden ser difíciles de depurar para los programadores que no están acostumbrados a el idioma Sin embargo, sería útil saber cuáles son las desventajas de usar una estructura incompleta en lugar de un puntero definido por tipos.

    – davidA

    17 de agosto de 2015 a las 7:17


Un modismo común es sufijar el tipo con _p para indicar que es un puntero mientras conserva las cualidades de puntero.

A veces es necesario usar solo el tipo de puntero si la estructura a la que apunta no está disponible públicamente. Esto ayuda a facilitar la ocultación de datos. Es decir

typedef struct hidden_secret_object * object;
void change_object(object foo);

esto le permite cambiar la forma en que hidden_secret_object está estructurado sin romper el código externo.

  • ¿Por qué necesita el puntero en el typedef para ocultar datos? typedef struct h_s_o object; void change_object(object *foo); lograría lo mismo. El ejemplo parece irrelevante para la pregunta.

    – tiro

    23 de septiembre de 2010 a las 20:12


  • A veces, se pretende que un tipo sea opaco y, en diferentes sistemas, se usaría un tipo diferente. En algunos sistemas, este tipo puede ser grande, por lo que pasarlo por puntero es más eficiente, mientras que en otros sistemas puede ser simplemente un token pasado como un número entero.

    – ganso nate

    23 de septiembre de 2010 a las 20:26

Yo tampoco lo veo claro. Tampoco me gustan los tipos con mayúscula completa (intento reservarlos para #defines).

De esta manera, es más fácil engañarse pensando que, de hecho, es un tipo de valor, mientras que estamos hablando de un tipo de puntero. El tipo de puntero se puede abstraer por completo con punteros inteligentes, pero esa no es una práctica común en C.

El sufijo con (como se mencionó anteriormente) _p, _ptr, Pointer o cualquier cosa similar crea claridad; aumenta la escritura, eso es cierto, pero evitará errores tontos (como usar ‘.’ en lugar de ‘->’, …) que le costarán un valioso tiempo de desarrollo.

avatar de usuario
Hormiga

Depende de lo que estés tratando de lograr. No hay una respuesta significativa de “sí o no” a su pregunta de la forma en que está formulada.

  • Si está tratando de crear un mango abstracto tipo de tipo, lo que implica que se supone que el usuario no debe saber o preocuparse por lo que se esconde detrás del tipo, entonces tipear un tipo de puntero está perfectamente bien. El punto es que hoy podría ser un tipo puntero, y mañana podría convertirse en un tipo entero, y luego podría convertirse en otra cosa. Esto es exactamente para lo que se usan normalmente los typedefs de tipo de puntero en la mayoría de las interfaces de biblioteca.

Usted está diciendo que a veces “no está claro que se declare un puntero”. ¡Pero bajo este modelo de uso, ese es exactamente el punto! Está supuesto ser “no claro”. El hecho de que el tipo sea un puntero ofuscado no es asunto tuyo. Es algo que no necesita saber y en lo que no debe confiar.

Un ejemplo clásico de este modelo de uso es el va_list escriba en la biblioteca estándar. En alguna implementación, podría ser fácilmente un typedef para un tipo de puntero. Pero eso es algo que se supone que no debes saber o en lo que no debes confiar.

Otro ejemplo sería la definición de HWND escriba la API de Windows. También es un typedef para el tipo de puntero, pero eso no es asunto tuyo.

  • Una situación completamente diferente es cuando está tipeando un tipo de puntero como una forma de taquigrafía, solo para hacer las declaraciones más cortas para no tener que escribir el * personaje cada vez. En este caso, el hecho de que el typedef está (y siempre estará) representando un tipo de puntero está expuesto al usuario. Normalmente este uso no es una buena práctica de programación. Si los usuarios querrán crear un alias para evitar escribir * cada vez, pueden hacerlo por sí mismos.

Este modelo de uso generalmente conduce a un código más ofuscado por las razones que ya mencionó en su OP.

También se puede encontrar un ejemplo de este mal uso de typedefs en la API de Windows. Typedef nombres como PINT siga exactamente ese modelo de uso defectuoso.

No creo que sea una mala práctica si es un puntero a un tipo incompleto, o si por cualquier otra razón no se espera que el usuario elimine la referencia. Nunca entendí FILE*.

Tampoco creo que sea una mala práctica si lo haces porque tienes varios niveles de direccionamiento indirecto y quieres usarlo en situaciones en las que algunos de ellos son irrelevantes. typedef char **argarrayo algo.

Si se espera que el usuario elimine la referencia en C, creo que probablemente sea mejor conservar el *. En C++, las personas están acostumbradas a los tipos definidos por el usuario con sobrecarga operator*, como iteradores. En C eso no es normal.

avatar de usuario
Super gato

Los calificadores de clase de almacenamiento como ‘const’ funcionarán de manera diferente con los punteros definidos por tipos que con los ‘naturales’. Si bien esto no suele ser bueno con ‘const’, puede ser muy útil con clases de almacenamiento específicas del compilador como “xdata”. Una declaración como:

xdata WOKKA *foo;

declarará “foo” como un puntero, almacenado en la clase de almacenamiento predeterminada, a un WOKKA en xdata. Una declaración:

xdata WOKKA_PTR bar;

declararía “bar” como un puntero, almacenado en xdata, a un WOKKA en cualquier clase de almacenamiento especificada en WOKKA_PTR. Si las rutinas de la biblioteca van a esperar punteros a cosas con una clase de almacenamiento en particular, puede ser útil definir esas clases de almacenamiento dentro de los tipos de puntero.

Me ha mordido el culo en alguna ocasión:

for (vector<typedef_name_that_doesnt_indicate_pointerness_at_all>::iterator it;
    it != v.end(); ++it)
{
   it->foo(); // should have been written (*it)->foo();
}

La única vez que es aceptable es si el tipo debe ser verdaderamente opaco y no se accede directamente en absoluto. IOW, si alguien va a tener que eliminar la referencia fuera de una API, entonces el puntero no debe ocultarse detrás de un typedef.

¿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