将C ++结构填充为2的幂

时间:2009-08-06 15:55:00

标签: c++ struct bit-manipulation padding

我正在为嵌入式系统开发一些C ++代码。代码使用的I / O接口要求每条消息的大小(以字节为单位)是2的幂。现在,代码执行类似的操作(在几个地方):

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

我正在尝试首次在64位Fedora上编译代码,其中long是64位。在这种情况下,sizeof(internal_)大于64,数组大小表达式下溢,编译器抱怨数组太大。

理想情况下,我希望能够编写一个宏来获取结构的大小,并在编译时评估填充数组所需的大小,以便将结构的大小舍入为2。

我查看了Bit Twiddling Hacks页面,但我不知道是否有任何技术可以在宏中实现,以便在编译时进行评估。

此问题的其他任何解决方案?或者我应该将问题永久化,只需将神奇的64改为神奇的128?

11 个答案:

答案 0 :(得分:15)

使用模板元程序。 (编辑回应评论)。

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

答案 1 :(得分:6)

可能最明显的方法是使用三元运算符:

#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

答案 2 :(得分:5)

为什么不使用工会?

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

或者更好的是使用匿名结构

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

所以你可以访问这样的成员:Message.member1;

编辑:显然这并不能解决64个以上的问题,但提供了一种更简洁的填充方式。

答案 3 :(得分:4)

解决这个问题的一种方法是用大小(长)的倍数替换硬编码的64,将填充变成这样的:

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

它很难看但它应该可以移植到64位。

也就是说,要求消息大小为2的幂的API听起来有点奇怪,就像设计问题一样。要求大小为偶数是有意义的,因为在某些处理器上为访问奇数地址上的数据而要付出相当大的代价,但是#pragma pack几乎使其不可避免。

答案 4 :(得分:4)

如何在发送和接收消息函数周围编写一个小包装器来处理任何大小的消息,他们只需分配一个更大的缓冲区(下一个2的幂)并记住它,将结构复制到开头并发送它。

答案 5 :(得分:2)

你已经在使用#pragma pack了,我不知道你具体使用什么编译器,但是你应该看看它们是否支持控制对齐/填充的包的参数然后你就可以得到完全摆脱填充场。我知道pragma pack版{{1}}支持此版本,MSVC's也是如此。

答案 6 :(得分:2)

您可以按如下方式对this进行宏观化(对于32位架构):

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

然后你可以使用联合技巧或其他方法来应用它。在您的示例中:

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

答案 7 :(得分:1)

您可以使用模板获取结构大小的编译时常量,其舍入为2的幂:

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

填充值应该很容易。

答案 8 :(得分:0)

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

会更清洁。

答案 9 :(得分:0)

我喜欢Niki's answer,特别是有匿名结构的部分。

答案没有解决的一件事是大于64字节的问题,但如果 sizeof(有条件地声明 char [128] 结构成员,则可以解决这个问题)长)== 8 并声明 char [64]

答案 10 :(得分:0)

还有另一个模板解决方案(从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;
}

我的方法是简单地将填充大小加倍,直到它至少与类型大小一样大。

相关问题