Pregunta

Estoy trabajando en algo de código C ++ para un sistema embebido. La interfaz I / O utiliza el código requiere que el tamaño de cada mensaje (en bytes) es una potencia de dos. En este momento, el código hace algo como esto (en varios lugares):

#pragma pack(1)
struct Message
{
   struct internal_
   {
      unsigned long member1;
      unsigned long member2;
      unsigned long member3;
      /* more members */
   } internal;
   char pad[64-sizeof(internal_)];
};
#pragma pack()

Estoy intentando compilar el código en una de 64 bits de Fedora, por primera vez, donde long es de 64-bits. En este caso, sizeof(internal_) es mayor que 64, la expresión tamaño de la matriz subdesbordamientos, y el compilador se queja de que la matriz es demasiado grande.

Idealmente, me gustaría ser capaz de escribir una macro que tomará el tamaño de la estructura y evaluar en tiempo de compilación el tamaño requerido de la matriz de relleno con el fin de redondear el tamaño de la estructura a una potencia de dos.

He mirado en la página del Bit haciendo girar Hacks , pero yo no' sé si cualquiera de las técnicas que realmente se puede implementar en una macro para ser evaluados en tiempo de compilación.

Las demás soluciones a este problema? O debería perpetuar el problema y sólo cambiar el mágico 64 a un 128 mágica?

¿Fue útil?

Solución

Utilice un metaprograma plantilla. (Editado en respuesta a comentar).

#include <iostream>
#include <ostream>
using namespace std;

template <int N>
struct P
{
    enum { val = P<N/2>::val * 2 };
};

template <>
struct P<0>
{
    enum { val = 1 };
};

template <class T>
struct PadSize
{
    enum { val = P<sizeof (T) - 1>::val - sizeof (T) }; 
};

template <class T, int N>
struct PossiblyPadded
{
    T       payload;
    char    pad[N]; 
};

template <class T>
struct PossiblyPadded<T, 0>
{
    T       payload;
};

template <class T>
struct Holder : public PossiblyPadded<T, PadSize<T>::val>
{
};


int main()
{
    typedef char Arr[6];

    Holder<Arr> holder;
    cout << sizeof holder.payload << endl;

    // Next line fails to compile if sizeof (Arr) is a power of 2
    // but holder.payload always exists
    cout << sizeof holder.pad << endl;
}

Otros consejos

Probablemente la forma más obvia sería sólo tiene que utilizar el operador ternario:

#define LOG2_CONST(n) ((n) <= 1 ? 0 :
                      ((n) <= 2 ? 1 :
                      ((n) <= 4 ? 2 :
                      /* ... */
                      ))))))))))))))))))))))))))))))
#define PADDED_STRUCT(ResultName, BaseName) \
  typedef union { BaseName data; char pad[1 << LOG2_CONST(sizeof(BaseName))]; } ResultName

¿Por qué no utilizar una unión?

union Message
{
    struct internal_
    {
        unsigned long member1;
        /* more members */
    };
    char[64];
};

o mejor aún, utilizar estructuras anónimos

union Message
{
    struct
    {
        unsigned long member1;
        /* more members */
    };
    char[64];
};

Así se puede acceder a los miembros de esta manera: Message.member1;

Editar: obviamente, esto no soluciona el problema mayor que 64, pero ofrece una forma más limpia de acolchado

.

Una forma de trabajar alrededor del problema sería la de sustituir el 64 codificado con un múltiplo del tamaño (largo), girando el relleno en algo como esto:

char pad[4*sizeof(unsigned long) - sizeof(internal_)];

Es feo, pero debe ser portátil para 64 bits.

Dicho esto, una API que requiere el tamaño del mensaje a ser una potencia de 2 suena un poco extraño y, como un problema de diseño. Que requiere que el tamaño es un número par tiene sentido ya que en algunos procesadores que pagar una penalización bastante para acceder a datos en direcciones extrañas, pero su paquete pragma casi hace que inevitable.

¿Qué hay de sólo escribir un pequeño envoltorio sobre el envío y recepción función de mensaje que manejar cualquier mensaje de tamaño y que acaba de asignar un búfer más grande (siguiente potencia de 2) y memclear que, copie la estructura al principio y enviarlo junto.

Ya está usando #pragma pack, no sé qué compilador (s) usando su específicamente, pero debería ver si son compatibles con argumentos a favor de paquete que controlan la alineación / relleno y entonces sólo puede deshacerse del acolchado campo completo. Sé versión de MSVC de pragma pack apoya esta , al igual que de GCC.

Puede macroize este de la siguiente manera (para la arquitectura de 32 bits ):

#define align_step(N, shift) ((N) | ((N) >> shift))
#define align_up(N) (align_step(align_step(align_step(align_step(align_step((N)-1, 1), 2), 4), 8), 16) + 1)
#define alignment_padding(N) (align_up((N)) - (N))

A continuación, puede solicitar que el uso de la unión truco o algún otro medio. En su ejemplo:

#pragma pack(1)
struct Message
{
   struct internal_
   {
      unsigned long member1;
      unsigned long member2;
      unsigned long member3;
      /* more members */
   } internal;
   char pad[alignment_padding(sizeof(internal_))];
};
#pragma pack()

Usted puede obtener una constante de la dimensión de la estructura en tiempo de compilación redondeado a una potencia de dos que utilizan plantillas:

template<int N, int C = 1>
struct Recurse
{
    enum {result = Recurse<N/2, C*2>::result};
};

template<int C>
struct Recurse<0, C>
{
    enum {result = C};
};

template<typename T>
struct Calc
{
    enum {size = Recurse<sizeof(Test)-1>::result};
};

struct Test
{
    int a;
    double b;
    double c;
};

int main()
{
    std::cout << sizeof(Test) << " -> " << Calc<Test>::size << std::endl;
    return 0;
}

El valor de relleno debe entonces ser fácil.

union Message
{
    struct
    {
        unsigned long member1;
        unsigned long member2; //...
    };
    char pad[1 << 5]; //change 5 to whatever size you need...
};

Sería un poco más limpio.

de Niki respuesta , especialmente la parte con estructuras anónimas.

Una cosa que la respuesta no resolvió fue la mayor que la 64-bytes problema, pero que puede ser resuelto por la que se declara condicionalmente una char [128] miembro de estructura si sizeof ( de largo) == 8 y declarando char [64] lo contrario.

Y sin embargo, otra solución plantilla (robar enormemente de Efervescencia):

#include <iostream>
#include <ostream>
using namespace std;

template <int TSize, int PSize = 1, bool = false>
struct PadSize
{
  static const int val =
    ( PadSize <    TSize, (PSize*2), (TSize <= (PSize*2)  )    > :: val );
};

template < int TSize, int PSize>
struct PadSize <TSize, PSize, true>  // As soon as TSize is <= to PSize
{
  static const int val = PSize;
};

int main()
{
    typedef char Arr[8];
    char pad[ PadSize <sizeof(Arr)>::val  ];

    cout << sizeof pad << endl;
}

Mi enfoque es simplemente para mantener duplicar el tamaño de relleno hasta que sea al menos tan grande como el tamaño de letra.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top