Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pad a C++ structure to a power of two

I'm working on some C++ code for an embedded system. The I/O interface the code uses requires that the size of each message (in bytes) is a power of two. Right now, the code does something like this (in several places):

#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()

I'm trying to compile the code on a 64-bit Fedora for the first time, where long is 64-bits. In this case, sizeof(internal_) is greater than 64, the array size expression underflows, and the compiler complains that the array is too large.

Ideally, I'd like to be able to write a macro that will take the size of the structure and evaluate at compile time the required size of the padding array in order to round the size of the structure out to a power of two.

I've looked at the Bit Twiddling Hacks page, but I don't know if any of the techniques there can really be implemented in a macro to be evaluated at compile time.

Any other solutions to this problem? Or should I perpetuate the problem and just change the magical 64 to a magical 128?

like image 497
Nick Meyer Avatar asked Aug 06 '09 15:08

Nick Meyer


People also ask

What is padding in structure in C?

Structure padding is a concept in C that adds the one or more empty bytes between the memory addresses to align the data in memory.

How is structure padding done?

The structure padding is automatically done by the compiler to make sure all its members are byte aligned. Here 'char' is only 1 byte but after 3 byte padding, the number starts at 4 byte boundary. For 'int' and 'double', it takes up 4 and 8 bytes respectively.

Why do we need padding in C?

The answer to that lies in how a CPU accesses memory. Typically a CPU has alignment constraints, e.g. a CPU will access one word at a time, or a CPU will require data to be 16byte aligned, etc. So to make sure that data is aligned according to the constraints of the CPU, padding is required.

Why do structs have padding?

Padding aligns structure members to "natural" address boundaries - say, int members would have offsets, which are mod(4) == 0 on 32-bit platform. Padding is on by default.


3 Answers

Use a template metaprogram. (Edited in response to comment).

#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;
}
like image 153
fizzer Avatar answered Sep 23 '22 13:09

fizzer


Probably the most obvious way would be to just use the ternary operator:

#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
like image 24
bdonlan Avatar answered Sep 23 '22 13:09

bdonlan


Why not use a union?

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

or better yet use anonymous structs

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

So you can access members like this: Message.member1;

Edit: obviously this doesn't solve your greater than 64 problem, but provides a cleaner way of padding.

like image 32
Niki Yoshiuchi Avatar answered Sep 19 '22 13:09

Niki Yoshiuchi