Pad a C++ structure to a power of two

2019-04-05 16:54发布

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?

11条回答
闹够了就滚
2楼-- · 2019-04-05 16:55

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
查看更多
相关推荐>>
3楼-- · 2019-04-05 16:57

And yet another template solution (robbing hugely from fizzer):

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

My approach is simply to keep doubling the padding size until it's at least as big as the type size.

查看更多
叛逆
4楼-- · 2019-04-05 16:58

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.

查看更多
小情绪 Triste *
5楼-- · 2019-04-05 17:04
union Message
{
    struct
    {
        unsigned long member1;
        unsigned long member2; //...
    };
    char pad[1 << 5]; //change 5 to whatever size you need...
};

Would be a little cleaner.

查看更多
贼婆χ
6楼-- · 2019-04-05 17:06

You're already using #pragma pack, I don't know what compiler(s) your using specifically, but you should see if they support arguments for pack that control the alignment/padding and then you can just get rid of the padding field altogether. I know MSVC's version of pragma pack supports this, as does GCC's.

查看更多
▲ chillily
7楼-- · 2019-04-05 17:07

I like Niki's answer, especially the part with anonymous structs.

One thing that answer didn't solve was the greater-than-64-bytes problem, but that can be solved by conditionally declaring a char[128] struct member if sizeof(long)==8 and declaring char[64] otherwise.

查看更多
登录 后发表回答