¿Hay alguna manera de imponer un endianness específico para una estructura C o C ++?

He visto algunas preguntas y respuestas sobre el endianness de las estructuras, pero se trataba de detectar el endianness de un sistema o de convertir datos entre los dos endianness diferentes.

Lo que me gustaría ahora, sin embargo, si hay una manera de imponer la endianidad específica de una estructura dada . ¿Hay algunas buenas directivas de comstackción u otras soluciones simples además de reescribir todo de una gran cantidad de macros que manipulan en campos de bits?

Una solución general estaría bien, pero también estaría feliz con una solución específica de gcc.

Editar:

Gracias por todos los comentarios que indican por qué no es una buena idea imponer el endianness, pero en mi caso eso es exactamente lo que necesito.

Un procesador específico genera una gran cantidad de datos (que nunca cambiará, es un sistema integrado con un hardware personalizado), y un progtwig (en el que estoy trabajando) que se ejecuta en un procesador desconocido debe leerlo. La evaluación por byte de los datos sería terriblemente problemática porque consiste en cientos de diferentes tipos de estructuras, que son enormes y profundas: la mayoría de ellas tienen muchas capas de otras estructuras enormes en su interior.

Cambiar el software para el procesador integrado está fuera de discusión. La fuente está disponible, esta es la razón por la que pretendo usar las estructuras de ese sistema en lugar de empezar de cero y evaluar todos los datos por bytes.

Es por esto que necesito decirle al comstackdor qué endianness debe usar, no importa cuán eficiente o no sea.

No tiene que ser un cambio real en la endianidad. Incluso si es solo una interfaz, y físicamente todo se maneja en los procesadores propios, es perfectamente aceptable para mí.

La forma en que normalmente manejo esto es así:

 #include  // for ntohs() etc. #include  class be_uint16_t { public: be_uint16_t() : be_val_(0) { } // Transparently cast from uint16_t be_uint16_t(const uint16_t &val) : be_val_(htons(val)) { } // Transparently cast to uint16_t operator uint16_t() const { return ntohs(be_val_); } private: uint16_t be_val_; } __attribute__((packed)); 

Del mismo modo para be_uint32_t .

Entonces puedes definir tu estructura así:

 struct be_fixed64_t { be_uint32_t int_part; be_uint32_t frac_part; } __attribute__((packed)); 

El punto es que es casi seguro que el comstackdor disponga los campos en el orden en que los escribes, por lo que todo lo que realmente te preocupa son los enteros de big-endian. El objeto be_uint16_t es una clase que sabe cómo convertirse de forma transparente entre big-endian y machine-endian según sea necesario. Me gusta esto:

 be_uint16_t x = 12; x = x + 1; // Yes, this actually works write(fd, &x, sizeof(x)); // writes 13 to file in big-endian form 

De hecho, si comstacks ese fragmento de código con cualquier comstackdor de C ++ razonablemente bueno, deberías encontrar que emite un big-endian “13” como una constante.

Con estos objetos, la representación en memoria es big-endian. Así que puedes crear matrices de ellas, colocarlas en estructuras, etc. Pero cuando vas a operar en ellas, se lanzan mágicamente a la máquina-endiana. Esto suele ser una sola instrucción en x86, por lo que es muy eficiente. Hay algunos contextos donde tienes que lanzar a mano:

 be_uint16_t x = 37; printf("x == %u\n", (unsigned)x); // Fails to compile without the cast 

… pero para la mayoría de los códigos, puedes usarlos como si fueran tipos integrados.

Un poco tarde para la fiesta pero con el GCC actual (probado en 6.2.1 donde funciona y 4.9.2 donde no está implementado), finalmente hay una manera de declarar que una estructura debe mantenerse en orden de bytes X-endian.

El siguiente progtwig de prueba:

 #include  #include  struct __attribute__((packed, scalar_storage_order("big-endian"))) mystruct { uint16_t a; uint32_t b; uint64_t c; }; int main(int argc, char** argv) { struct mystruct bar = {.a = 0xaabb, .b = 0xff0000aa, .c = 0xabcdefaabbccddee}; FILE *f = fopen("out.bin", "wb"); size_t written = fwrite(&bar, sizeof(struct mystruct), 1, f); fclose(f); } 

crea un archivo “out.bin” que puede inspeccionar con un editor hexadecimal (por ejemplo, hexdump -C out.bin). Si se admite el atributo scalar_storage_order, contendrá el 0xaabbff0000aaabcdefaabbccddee esperado en este orden y sin agujeros. Lamentablemente esto es, por supuesto, muy específico del comstackdor.

No, no lo creo.

Endianness es el atributo del procesador que indica si los enteros se representan de izquierda a derecha o de derecha a izquierda, no es un atributo del comstackdor.

Lo mejor que puede hacer es escribir código que sea independiente de cualquier orden de bytes.

No, no hay tal capacidad. Si existiera, eso podría hacer que los comstackdores tuvieran que generar código excesivo / ineficiente, por lo que C ++ simplemente no lo admite.

La forma habitual de C ++ de lidiar con la serialización (que supongo que es lo que estás tratando de resolver) es dejar que la estructura permanezca en la memoria en el diseño exacto deseado y realizar la serialización de manera tal que la endianidad se mantenga en la deserialización.

No estoy seguro de si lo siguiente puede modificarse para adaptarse a sus propósitos, pero donde trabajo, hemos encontrado que lo siguiente es bastante útil en muchos casos.

Cuando el endianness es importante, usamos dos estructuras de datos diferentes. Uno está hecho para representar cómo esperaba llegar. La otra es cómo queremos que se represente en la memoria. Las rutinas de conversión se desarrollan para cambiar entre las dos.

El flujo de trabajo funciona así …

  1. Lea los datos en la estructura en bruto.
  2. Convertir a la “estructura en bruto” a la “en versión de memoria”
  3. Operar solo en la “versión en memoria”
  4. Cuando termine de operar en él, convierta la “versión en memoria” de nuevo a la “estructura en bruto” y escríbala.

Encontramos útil este desacoplamiento porque (pero no limitado a) …

  1. Todas las conversiones se encuentran en un solo lugar.
  2. Menos dolores de cabeza sobre los problemas de alineación de la memoria cuando se trabaja con la “versión en memoria”.
  3. Facilita mucho la transferencia de un arco a otro (menos problemas endianos).

Con suerte, este desacoplamiento también puede ser útil para su aplicación.

Una posible solución innovadora sería usar un intérprete de C como Ch y forzar la encoding endiana a grande.

Boost proporciona buffers endian para esto.

Por ejemplo:

 #include  #include  using namespace boost::endian; struct header { big_int32_buf_t file_code; big_int32_buf_t file_length; little_int32_buf_t version; little_int32_buf_t shape_type; }; BOOST_STATIC_ASSERT(sizeof(h) == 16U); 

Tal vez no sea una respuesta directa, pero al leer esta pregunta esperamos poder responder algunas de sus inquietudes.

Podría hacer que la estructura sea una clase con captadores y definidores para los miembros de datos. Los captadores y definidores se implementan con algo como:

 int getSomeValue( void ) const { #if defined( BIG_ENDIAN ) return _value; #else return convert_to_little_endian( _value ); #endif } void setSomeValue( int newValue) { #if defined( BIG_ENDIAN ) _value = newValue; #else _value = convert_to_big_endian( newValue ); #endif } 

Hacemos esto a veces cuando leemos una estructura desde un archivo: la leemos en una estructura y la usamos en las máquinas big-endian y little-endian para acceder a los datos correctamente.

Hay una representación de datos para este llamado XDR. Échale un vistazo. http://en.wikipedia.org/wiki/External_Data_Representation

Aunque puede ser demasiado para su sistema integrado. Intente buscar una biblioteca ya implementada que pueda usar (¡verifique las restricciones de licencia!).

XDR se usa generalmente en sistemas de red, ya que necesitan una forma de mover datos de una manera independiente de Endianness. Aunque nada dice que no puede ser usado fuera de las redes.