lenguaje funciones estructuras ejemplo con arreglo array anidadas c struct private-members

funciones - Ocultar miembros en una estructura C



typedef struct c (13)

Algo similar al método que ha propuesto se usa de vez en cuando (por ejemplo, vea las diferentes variables de struct sockaddr* en la API de sockets BSD), pero es casi imposible de usar sin violar las estrictas reglas de aliasing de C99.

Sin embargo, puede hacerlo de manera segura :

somestruct.h :

struct SomeStructPrivate; /* Opaque type */ typedef struct { int _public_member; struct SomeStructPrivate *private; } SomeStruct;

somestruct.c :

#include "somestruct.h" struct SomeStructPrivate { int _member; }; SomeStruct *SomeStruct_Create() { SomeStruct *p = malloc(sizeof *p); p->private = malloc(sizeof *p->private); p->private->_member = 0xWHATEVER; return p; }

He estado leyendo sobre OOP en C, pero nunca me ha gustado cómo no puedes tener miembros de datos privados como tú en C ++. Pero luego me vino a la mente que podrías crear 2 estructuras. Uno se define en el archivo de encabezado y el otro se define en el archivo de origen.

// ========================================= // in somestruct.h typedef struct { int _public_member; } SomeStruct; // ========================================= // in somestruct.c #include "somestruct.h" typedef struct { int _public_member; int _private_member; } SomeStructSource; SomeStruct *SomeStruct_Create() { SomeStructSource *p = (SomeStructSource *)malloc(sizeof(SomeStructSource)); p->_private_member = 42; return (SomeStruct *)p; }

Desde aquí puedes simplemente lanzar una estructura a la otra. ¿Esto se considera una mala práctica? ¿O se hace a menudo?


Casi lo tienes, pero no has ido lo suficientemente lejos.

En el encabezado:

struct SomeStruct; typedef struct SomeStruct *SomeThing; SomeThing create_some_thing(); destroy_some_thing(SomeThing thing); int get_public_member_some_thing(SomeThing thing); void set_public_member_some_thing(SomeThing thing, int value);

En la C:

struct SomeStruct { int public_member; int private_member; }; SomeThing create_some_thing() { SomeThing thing = malloc(sizeof(*thing)); thing->public_member = 0; thing->private_member = 0; return thing; } ... etc ...

El punto es que ahora los consumidores no tienen conocimiento de los aspectos internos de SomeStruct y puedes cambiarlo impunemente, agregando y eliminando miembros a voluntad, incluso sin que los consumidores necesiten recompilarlo. Tampoco pueden "accidentalmente" enviar miembros directamente, ni asignar SomeStruct en la pila. Esto, por supuesto, también puede verse como una desventaja.


Escribía una estructura oculta y la referenciaba usando un puntero en la estructura pública. Por ejemplo, tu .h podría tener:

typedef struct { int a, b; void *private; } public_t;

Y tu .c:

typedef struct { int c, d; } private_t;

Obviamente no protege contra la aritmética del puntero, y agrega un poco de sobrecarga para la asignación / desasignación, pero supongo que está más allá del alcance de la pregunta.


Este enfoque es válido, útil, estándar C.

Un enfoque ligeramente diferente, utilizado por Sockets API, que fue definido por BSD Unix, es el estilo utilizado para struct sockaddr .


Hay mejores formas de hacerlo, como usar un puntero void * a una estructura privada en la estructura pública. La forma en que lo haces engaña al compilador.


Mi solución sería proporcionar solo el prototipo de la estructura interna y luego declarar la definición en el archivo .c. Muy útil para mostrar la interfaz C y usar C ++ detrás.

.h:

struct internal; struct foo { int public_field; struct internal *_internal; };

.c:

struct internal { int private_field; // could be a C++ class };

Nota: En ese caso, la variable debe ser un puntero porque el compilador no puede conocer el tamaño de la estructura interna.


No es muy privado, dado que el código de llamada puede (SomeStructSource *) a a (SomeStructSource *) . Además, ¿qué sucede cuando quieres agregar otro miembro público? Tendrás que romper la compatibilidad binaria.

EDITAR: me olvidé de que estaba en un archivo .c, pero realmente no hay nada que impida que un cliente lo copie, o posiblemente incluso #include el archivo .c directamente.


No recomiendo usar el patrón de estructura pública. El patrón de diseño correcto, para OOP en C, es proporcionar funciones para acceder a todos los datos, nunca permitiendo el acceso público a los datos. Los datos de la clase se deben declarar en la fuente, para que sean privados, y se debe hacer referencia a ellos de manera anticipada, donde Create y Destroy hace la asignación y libre de los datos. De esa manera, el dilema público / privado ya no existirá.

/*********** header.h ***********/ typedef struct sModuleData module_t'' module_t *Module_Create(); void Module_Destroy(module_t *); /* Only getters and Setters to access data */ void Module_SetSomething(module_t *); void Module_GetSomething(module_t *); /*********** source.c ***********/ struct sModuleData { /* private data */ }; module_t *Module_Create() { module_t *inst = (module_t *)malloc(sizeof(struct sModuleData)); /* ... */ return inst; } void Module_Destroy(module_t *inst) { /* ... */ free(inst); } /* Other functions implementation */

En el otro lado, si no quiere usar Malloc / Free (que puede ser una sobrecarga innecesaria para algunas situaciones), le sugiero que oculte la estructura en un archivo privado. Se podrá acceder a los miembros privados, pero eso será responsabilidad del usuario.

/*********** privateTypes.h ***********/ /* All private, non forward, datatypes goes here */ struct sModuleData { /* private data */ }; /*********** header.h ***********/ #include "privateTypes.h" typedef struct sModuleData module_t; void Module_Init(module_t *); void Module_Deinit(module_t *); /* Only getters and Setters to access data */ void Module_SetSomething(module_t *); void Module_GetSomething(module_t *); /*********** source.c ***********/ void Module_Init(module_t *inst) { /* perform initialization on the instance */ } void Module_Deinit(module_t *inst) { /* perform deinitialization on the instance */ } /*********** main.c ***********/ int main() { module_t mod_instance; module_Init(&mod_instance); /* and so on */ }


Nunca hagas eso. Si su API admite cualquier cosa que tome SomeStruct como parámetro (que espero que lo haga), entonces podrían asignar uno en una pila y pasarlo. Obtendría errores importantes al intentar acceder al miembro privado desde el que el compilador asigna para la clase de cliente no contiene espacio para ello.

La forma clásica de ocultar miembros en una estructura es hacer que sea un vacío *. Básicamente es un identificador / cookie que solo conocen sus archivos de implementación. Casi todas las bibliotecas de C hacen esto para datos privados.


Personalmente, me gustaría más esto:

typedef struct { int _public_member; /*I know you wont listen, but don''t ever touch this member.*/ int _private_member; } SomeStructSource;

Es C, después de todo, si la gente quiere arruinarse, se les debería permitir: no es necesario ocultar nada, excepto:

Si lo que necesita es mantener compatible el ABI / API, hay dos enfoques que son más comunes de lo que he visto.

  • No dé acceso a sus clientes a la estructura, déles un identificador opaco (un vacío * con un nombre bonito), proporcione funciones de inicio / destrucción y acceso para todo. Esto asegura que puede cambiar la estructura sin siquiera recompilar a los clientes si está escribiendo una biblioteca.

  • proporcione un identificador opaco como parte de su estructura, que puede asignar de la manera que desee. Este enfoque incluso se usa en C ++ para proporcionar compatibilidad ABI.

p.ej

struct SomeStruct { int member; void* internals; //allocate this to your private struct };


Relacionado, aunque no exactamente oculto.

Es descalificar condicionalmente a los miembros.

Tenga en cuenta que esto funciona para GCC / Clang, pero MSVC y otros compiladores también pueden degradarse, por lo que es posible crear una versión más portátil.

Si construye con advertencias bastante estrictas, o advertencias como errores, esto al menos evita el uso accidental.

// ========================================= // in somestruct.h #ifdef _IS_SOMESTRUCT_C # if defined(__GNUC__) # define HIDE_MEMBER __attribute__((deprecated)) # else # define HIDE_MEMBER /* no hiding! */ # endif #else # define HIDE_MEMBER #endif typedef struct { int _public_member; int _private_member HIDE_MEMBER; } SomeStruct; #undef HIDE_MEMBER // ========================================= // in somestruct.c #define _IS_SOMESTRUCT_C #include "somestruct.h" SomeStruct *SomeStruct_Create() { SomeStructSource *p = (SomeStructSource *)malloc(sizeof(SomeStructSource)); p->_private_member = 42; return (SomeStruct *)p; }


Use la siguiente solución alternativa:

#include <stdio.h> #define C_PRIVATE(T) struct T##private { #define C_PRIVATE_END } private; #define C_PRIV(x) ((x).private) #define C_PRIV_REF(x) (&(x)->private) struct T { int a; C_PRIVATE(T) int x; C_PRIVATE_END }; int main() { struct T t; struct T *tref = &t; t.a = 1; C_PRIV(t).x = 2; printf("t.a = %d/nt.x = %d/n", t.a, C_PRIV(t).x); tref->a = 3; C_PRIV_REF(tref)->x = 4; printf("tref->a = %d/ntref->x = %d/n", tref->a, C_PRIV_REF(tref)->x); return 0; }

El resultado es:

t.a = 1 t.x = 2 tref->a = 3 tref->x = 4


sizeof(SomeStruct) != sizeof(SomeStructSource) . Esto hará que alguien te encuentre y te mate algún día.