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

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

问题描述:

我正在研究用于嵌入式系统的一些C++代码。代码使用的I/O接口要求每个消息的大小(以字节为单位)是2的幂。眼下,代码做这样的事情(在几个地方):将C++结构填充为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,数组大小表达式会下溢,并且编译器会抱怨数组太大。

理想情况下,我希望能够编写一个宏,该宏将采用该结构的大小,并在编译时评估所需的填充数组大小,以便将结构的大小舍入到功率两个。

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

对此问题的其他解决方案?或者,我是否应该将问题永久化并将魔法64变成魔法128?

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

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

我喜欢这个,但是当它的内容是2的偶数时失败。pad的声明得到了 错误C2466:不能分配一个常数大小的数组0 – 2009-08-06 16:39:44

+0

好点。现在修复。 – fizzer 2009-08-06 17:01:46

+1

我想你可以从T派生PossiblyPadded而不是T成员(对于合适的T),并且保存恼人的成员访问语法。 – fizzer 2009-08-06 17:18:17

为什么不使用联合?

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

或更好,但使用匿名结构

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

所以,你可以访问的成员如下:Message.member1;

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

+4

一个很好的建议,但不回答这个问题...... – bdonlan 2009-08-06 16:00:31

+0

其实这是很危险的,因为的member1和member2将不是在不同的平台相同的地址一致。由于这是IO代码,这会导致错误。 – 2010-09-28 20:16:31

也许最明显的方法是只使用三元运算:

解决该问题的
#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 

一种方法是用大小的倍数来代替硬编码的64(长),转动填充到这样的东西:

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

这是丑陋的,但它应该是可移植到64位。

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

如何在处理任何大小消息的发送和接收消息函数周围编写一个小包装,然后只分配一个更大的缓冲区(下一个2的幂次)并对其进行memclear,将结构复制到起始处并将其发送。

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

会更清洁。

我喜欢Niki's answer,尤其是带有匿名结构的部分。

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

你可以得到一个编译时间常数结构的大小四舍五入到两个使用模板电源:然后

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

填充值应该很容易。

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

我的做法就是保持,直到它至少一样大的类型规模扩大一倍填充大小。

您已经在使用#pragma pack,我不知道您使用哪种编译器,但应该看看它们是否支持用于控制对齐/填充的pack参数,然后您可以摆脱填充字段。我知道MSVC's版本的pragma pack支持这一点,正如GCC's一样。

可以macroize 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() 
+0

我喜欢这个,我没有想过用一个宏来处理重复的n | n >> 1,2,4,8 ...等 – 2009-08-06 21:55:09