Y a-t-il un moyen d'imposer une endiannesspécifique pour une structure C ou C++?

j'ai vu quelques questions et réponses concernant l'endianité des structures, mais elles concernaient la détection de l'endianité d'un système, ou la conversion de données entre les deux endianités différentes.

ce que je voudrais maintenant, cependant, s'il y a une façon d'imposer l'endianness spécifique d'une structure donnée . Y a-t-il de bonnes directives de compilateur ou d'autres solutions simples en plus de réécrire le tout à partir de beaucoup de macros? manipuler sur des bitfields?

Une solution générale serait bien, mais je serais heureux avec un gcc solution.

Edit:

je vous Remercie pour tous les commentaires soulignant pourquoi ce n'est pas une bonne idée d'appliquer l'endianness, mais dans mon cas, c'est exactement ce dont j'ai besoin.

Une grande quantité de données est généré par un processeur spécifique (qui ne changera jamais, c'est un système intégré avec un matériel personnalisé), et il doit être lu par un programme (sur lequel je travaille) tournant sur un processeur inconnu. L'évaluation Byte-sage des données serait horriblement gênante parce qu'elle se compose de centaines de différents types de structures, qui sont énormes, et profondes: la plupart d'entre eux ont de nombreuses couches d'autres énormes structures à l'intérieur.

changer le logiciel pour le processeur intégré est hors de question. La source est disponible, c'est pourquoi j'ai l'intention d'utiliser les structures de ce système au lieu de partir de zéro et d'évaluer toutes les données byte-wise.

C'est pourquoi j'ai besoin de dire au compilateur quel endianness il devrait utiliser, peu importe à quel point il sera efficace ou non.

Il n'a pas à être un réel changement dans l'endianness. Même si ce n'est qu'une interface, et physiquement tout est manipulé dans les processeurs parfaitement acceptable pour moi.

22
demandé sur vsz 2011-07-18 15:21:59

10 réponses

La façon dont j'ai l'habitude de gérer cela est de la sorte:

#include <arpa/inet.h> // for ntohs() etc.
#include <stdint.h>

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));

de même pour be_uint32_t .

, Alors vous pouvez définir votre structure comme ceci:

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

le fait est que le compilateur va très certainement présenter les champs dans l'ordre où vous les écrivez, donc tout ce qui vous inquiète vraiment c'est les entiers big-endians. L'objet be_uint16_t est une classe qui sait se convertir de manière transparente entre big-endian et machine-endian selon les besoins. Comme ceci:

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

en fait, si vous compilez cet extrait avec n'importe quel compilateur C++ raisonnablement bon, vous devriez trouver qu'il émet un" 13 " big-endian comme constante.

Avec ces objets, la représentation en mémoire est big-endian. Donc vous pouvez créer des tableaux d'eux, les mettre dans des structures, etc. Mais quand vous allez les opérer, ils moulent magiquement à machine-endian. C'est généralement un seul instruction sur x86, donc c'est très efficace. Il y a quelques contextes où vous devez lancer à la main:

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

...mais pour la plupart des codes, vous pouvez simplement les utiliser comme s'ils étaient des types intégrés.

18
répondu Nemo 2011-07-18 14:51:25

un peu en retard pour la partie mais avec le GCC actuel (testé le 6.2.1 où il fonctionne et le 4.9.2 où il n'est pas implémenté) il y a finalement un moyen de déclarer qu'une structure doit être conservée dans l'ordre des octets X-endian.

programme d'essai suivant:

#include <stdio.h>
#include <stdint.h>

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);
}

crée un fichier ".bin" que vous pouvez inspecter avec un éditeur hexadécimal (par exemple, hexdump-C.bac.) Si l'attribut scalar_storage_order est supporté, il contiendra la valeur attendue 0xaabbff0000aaabcdefaabbccddee dans cet ordre et sans trous. Malheureusement, ceci est bien sûr très spécifique au compilateur.

11
répondu Niklas Schnelle 2016-12-05 12:20:43

Non, Je ne pense pas.

Endianness est l'attribut de processeur qui indique si les entiers sont représentés de gauche à droite ou de droite à gauche, c'est pas un attribut du compilateur.

le mieux que vous pouvez faire est d'écrire du code qui est indépendant de toute commande byte.

3
répondu Alok Save 2011-07-18 11:35:02

Non, il n'y a pas une telle capacité. S'il existait, cela pourrait obliger les compilateurs à générer du code excessif/inefficace pour que C++ ne le supporte pas.

la façon C++ habituelle de traiter la sérialisation (ce que je suppose est ce que vous essayez de résoudre) c'est de laisser la structure rester en mémoire dans la disposition exacte désirée et faire la sérialisation de telle sorte que l'endianité soit préservée lors de la desérialisation.

2
répondu Mark B 2011-07-18 12:05:44

Je ne suis pas sûr que ce qui suit puisse être modifié pour répondre à vos besoins, mais lorsque je travaille, nous avons trouvé que ce qui suit est très utile dans de nombreux cas.

lorsque l'ennui est important, nous utilisons deux structures de données différentes. L'une représente la façon dont elle s'attendait à arriver. L'autre est la façon dont nous voulons qu'il soit représenté en mémoire. Les routines de Conversion sont alors développées pour passer d'un système à l'autre.

le flux de travail fonctionne ainsi ...

  1. lire les données dans la structure brute.
  2. de se Convertir à la "structure brute" à la "mémoire" version
  3. Fonctionnent uniquement sur la "mémoire" version
  4. quand il est fait opération sur elle, convertissez la" version en mémoire "de nouveau à la" structure brute " et écrivez-le.

nous trouvons ce découplage utile parce que (mais pas limité à) ...

  1. toutes les conversions sont situées en un seul endroit.
  2. moins de maux de tête au sujet des problèmes d'alignement de la mémoire quand on travaille avec la version "en mémoire".
  3. il rend le portage d'une arche à l'autre beaucoup plus facile (moins de problèmes d'endian).

espérons que ce découplage puisse être utile pour votre application aussi.

1
répondu Sparky 2011-07-18 14:35:36

une solution innovante serait d'utiliser un interpréteur C comme Ch et de forcer le codage endian à grand.

1
répondu Steve-o 2011-07-18 14:40:41

Boost fournit endian tampons pour cela.

par exemple:

#include <boost/endian/buffers.hpp>
#include <boost/static_assert.hpp>

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);
1
répondu Tobu 2015-10-01 09:42:18

peut-être pas une réponse directe, mais avoir une lecture à travers cette question peut, je l'espère, répondre à certaines de vos préoccupations.

0
répondu Nico Huysamen 2017-05-23 12:10:18

vous pourriez faire de la structure une classe avec des getters et des setters pour les membres de données. Les getters et les setters sont implémentés avec quelque chose comme:

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
}

nous le faisons parfois lorsque nous lisons une structure dans un fichier - nous la lisons dans une structure et l'utilisons sur les machines big-endian et little-endian pour accéder aux données correctement.

0
répondu Graeme Perrow 2011-07-18 14:36:04

il existe une représentation de données appelée XDR. Avoir un coup d'oeil. http://en.wikipedia.org/wiki/External_Data_Representation

même si c'est un peu trop pour votre système intégré. Essayez de rechercher une bibliothèque déjà implémentée que vous pouvez utiliser (vérifiez les restrictions de licence!).

XDR est généralement utilisé dans les systèmes de réseau, car ils ont besoin d'un moyen de déplacer des données d'une manière indépendante de L'ennui. Bien que rien ne dise qu'il ne peut pas être utilisé en dehors des réseaux.

0
répondu Outspoken 2011-07-18 15:51:01