我的编译器是否将我的枚举转换为位域?
Is my compiler turning my enum into bitfields?
我的结构如下所示:
struct UploadConfig {
private:
const void * m_data; // 4 bytes (32bit)
glm::u16vec4 m_size; // 12 bytes (uint16 * 4 = +8)
uint8 m_mipmapsCount;
TextureDim::Type m_depthType;
TextureInternalFormat::Type m_internalFormat;
TextureUploadFormat::Type m_uploadFormat; // 16 bytes
TextureUploadComp::Type m_uploadCompType;
uint8 m_swizzleR :3;
uint8 m_swizzleG :3;
uint8 m_swizzleB :3;
uint8 m_swizzleA :3;
bool m_minFilter :1;
bool m_minmipFilterUsed :1;
bool m_minmipFilter :1;
bool m_maxFilter :1;
bool m_edgeClampX :1;
bool m_edgeMirrorX :1;
bool m_edgeClampY :1;
bool m_edgeMirrorY :1;
bool m_edgeClampZ :1;
bool m_edgeMirrorZ :1; // 20 bytes probably
};
const int iogwhgakfj = sizeof UploadConfig; // this is reported as 16 (???)
其中枚举定义为 uint8
,如下所示:
struct TextureDim {
enum Type : uint8 {
dim2D,
dim3D,
dimCubic
};
};
但是这个结构的大小对我来说真的很奇怪,有 16 个字节,我预计它会更大,达到 20 甚至 24 个字节。编译器是否在背后将我的枚举转换为位域?我的意思是......很好,但看起来也很奇怪它会用枚举类型而不是一系列布尔值来做到这一点。 (没有指定位域,这个结构的大小是 28)
编辑
我尝试添加一些曲线球来混淆编译器,但它仍然在 IDE 中报告大小为 16(将鼠标悬停在值 iogwhgakfj
上)
我的推介是:
void UploadConfig::setMinFilter() {
// there's no way the compiler can predict this
m_minFilter = (uintptr(&m_internalFormat) & 7) == (uintptr(&m_uploadCompType) & 7);
}
同样奇怪的是,它拒绝计算 IDE 中的 offsetof(TextureUploadConfig, m_uploadCompType);
,但会为 m_mipmapsCount
的值和出现在它之前的成员执行此操作。
结论
这是因为尚未定义枚举。在使用它们之前定义您的枚举类型,因为 Visual Studio 会很好地突出显示语法,就好像它是一样。也不要把你的代码搞得一团糟,以至于你连续一周都无法编译。
这不是一个正确的答案。而是带有格式的扩展注释。
在我的机器(有 8 字节指针)上,我看到了这个:
m_data 0
m_size 8
m_mipmapsCount 16
m_depthType 17
m_internalFormat 18
m_uploadFormat 19
m_uploadCompType 20
m_swizzleR 21[.....210]
m_swizzleG 21[..543...]
m_swizzleB 22[.....210]
m_swizzleA 22[..543...]
m_minFilter 22[.6......]
m_minmipFilterUsed 22[7.......]
m_minmipFilter 23[.......0]
m_maxFilter 23[......1.]
m_edgeClampX 23[.....2..]
m_edgeMirrorX 23[....3...]
m_edgeClampY 23[...4....]
m_edgeMirrorY 23[..5.....]
m_edgeClampZ 23[.6......]
m_edgeMirrorZ 23[7.......]
该输出的快速代码是:
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <cstring>
using std::cout;
using std::memset;
using std::ostringstream;
using std::setw;
using std::string;
using uint8 = unsigned char;
using uint16 = unsigned short;
namespace glm {
struct u16vec4 { uint16 data[4]; };
}
struct TextureDim { enum Type : uint8 { }; };
struct TextureInternalFormat { enum Type : uint8 { }; };
struct TextureUploadFormat { enum Type : uint8 { }; };
struct TextureUploadComp { enum Type : uint8 { }; };
struct UploadConfig {
const void * m_data; // 4 bytes (32bit)
glm::u16vec4 m_size; // 12 bytes (uint16 * 4 = +8)
uint8 m_mipmapsCount;
TextureDim::Type m_depthType;
TextureInternalFormat::Type m_internalFormat;
TextureUploadFormat::Type m_uploadFormat; // 16 bytes
TextureUploadComp::Type m_uploadCompType;
uint8 m_swizzleR :3;
uint8 m_swizzleG :3;
uint8 m_swizzleB :3;
uint8 m_swizzleA :3;
bool m_minFilter :1;
bool m_minmipFilterUsed :1;
bool m_minmipFilter :1;
bool m_maxFilter :1;
bool m_edgeClampX :1;
bool m_edgeMirrorX :1;
bool m_edgeClampY :1;
bool m_edgeMirrorY :1;
bool m_edgeClampZ :1;
bool m_edgeMirrorZ :1;
};
const int iogwhgakfj = sizeof(UploadConfig);
static int diff(void* b, void* m) {
auto bb = static_cast<char*>(b);
auto mm = static_cast<char*>(m);
return static_cast<int>(mm - bb);
}
static string zrange(void* b, size_t len) {
auto bb = static_cast<unsigned char*>(b);
ostringstream ss;
auto sep = "";
for (size_t pos = 0; pos < len; ++pos) {
if (bb[pos] == 0xFF) continue;
ss << sep << pos << "[";
auto u = bb[pos];
if ((u & 0b1000'0000) == 0) ss << "7"; else ss << ".";
if ((u & 0b0100'0000) == 0) ss << "6"; else ss << ".";
if ((u & 0b0010'0000) == 0) ss << "5"; else ss << ".";
if ((u & 0b0001'0000) == 0) ss << "4"; else ss << ".";
if ((u & 0b0000'1000) == 0) ss << "3"; else ss << ".";
if ((u & 0b0000'0100) == 0) ss << "2"; else ss << ".";
if ((u & 0b0000'0010) == 0) ss << "1"; else ss << ".";
if ((u & 0b0000'0001) == 0) ss << "0"; else ss << ".";
ss << "]";
sep = " ";
}
return ss.str();
}
int main() {
cout << "Size of UploadConfig: " << iogwhgakfj << "\n";
UploadConfig u;
#define DIFF(member) cout << setw(20) << #member << " " << diff(&u, &u.member) << "\n"
#define DIFFX(bitmember) memset(&u, 0xFF, sizeof u); u.bitmember = 0; cout << setw(20) << #bitmember << " " << zrange(&u, sizeof u) << "\n"
DIFF(m_data);
DIFF(m_size);
DIFF(m_mipmapsCount);
DIFF(m_depthType);
DIFF(m_internalFormat);
DIFF(m_uploadFormat);
DIFF(m_uploadCompType);
DIFFX(m_swizzleR);
DIFFX(m_swizzleG);
DIFFX(m_swizzleB);
DIFFX(m_swizzleA);
DIFFX(m_minFilter);
DIFFX(m_minmipFilterUsed);
DIFFX(m_minmipFilter);
DIFFX(m_maxFilter);
DIFFX(m_edgeClampX);
DIFFX(m_edgeMirrorX);
DIFFX(m_edgeClampY);
DIFFX(m_edgeMirrorY);
DIFFX(m_edgeClampZ);
DIFFX(m_edgeMirrorZ);
#undef DIFF
#undef DIFFX
}
我的结构如下所示:
struct UploadConfig {
private:
const void * m_data; // 4 bytes (32bit)
glm::u16vec4 m_size; // 12 bytes (uint16 * 4 = +8)
uint8 m_mipmapsCount;
TextureDim::Type m_depthType;
TextureInternalFormat::Type m_internalFormat;
TextureUploadFormat::Type m_uploadFormat; // 16 bytes
TextureUploadComp::Type m_uploadCompType;
uint8 m_swizzleR :3;
uint8 m_swizzleG :3;
uint8 m_swizzleB :3;
uint8 m_swizzleA :3;
bool m_minFilter :1;
bool m_minmipFilterUsed :1;
bool m_minmipFilter :1;
bool m_maxFilter :1;
bool m_edgeClampX :1;
bool m_edgeMirrorX :1;
bool m_edgeClampY :1;
bool m_edgeMirrorY :1;
bool m_edgeClampZ :1;
bool m_edgeMirrorZ :1; // 20 bytes probably
};
const int iogwhgakfj = sizeof UploadConfig; // this is reported as 16 (???)
其中枚举定义为 uint8
,如下所示:
struct TextureDim {
enum Type : uint8 {
dim2D,
dim3D,
dimCubic
};
};
但是这个结构的大小对我来说真的很奇怪,有 16 个字节,我预计它会更大,达到 20 甚至 24 个字节。编译器是否在背后将我的枚举转换为位域?我的意思是......很好,但看起来也很奇怪它会用枚举类型而不是一系列布尔值来做到这一点。 (没有指定位域,这个结构的大小是 28)
编辑
我尝试添加一些曲线球来混淆编译器,但它仍然在 IDE 中报告大小为 16(将鼠标悬停在值 iogwhgakfj
上)
我的推介是:
void UploadConfig::setMinFilter() {
// there's no way the compiler can predict this
m_minFilter = (uintptr(&m_internalFormat) & 7) == (uintptr(&m_uploadCompType) & 7);
}
同样奇怪的是,它拒绝计算 IDE 中的 offsetof(TextureUploadConfig, m_uploadCompType);
,但会为 m_mipmapsCount
的值和出现在它之前的成员执行此操作。
结论
这是因为尚未定义枚举。在使用它们之前定义您的枚举类型,因为 Visual Studio 会很好地突出显示语法,就好像它是一样。也不要把你的代码搞得一团糟,以至于你连续一周都无法编译。
这不是一个正确的答案。而是带有格式的扩展注释。
在我的机器(有 8 字节指针)上,我看到了这个:
m_data 0
m_size 8
m_mipmapsCount 16
m_depthType 17
m_internalFormat 18
m_uploadFormat 19
m_uploadCompType 20
m_swizzleR 21[.....210]
m_swizzleG 21[..543...]
m_swizzleB 22[.....210]
m_swizzleA 22[..543...]
m_minFilter 22[.6......]
m_minmipFilterUsed 22[7.......]
m_minmipFilter 23[.......0]
m_maxFilter 23[......1.]
m_edgeClampX 23[.....2..]
m_edgeMirrorX 23[....3...]
m_edgeClampY 23[...4....]
m_edgeMirrorY 23[..5.....]
m_edgeClampZ 23[.6......]
m_edgeMirrorZ 23[7.......]
该输出的快速代码是:
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <cstring>
using std::cout;
using std::memset;
using std::ostringstream;
using std::setw;
using std::string;
using uint8 = unsigned char;
using uint16 = unsigned short;
namespace glm {
struct u16vec4 { uint16 data[4]; };
}
struct TextureDim { enum Type : uint8 { }; };
struct TextureInternalFormat { enum Type : uint8 { }; };
struct TextureUploadFormat { enum Type : uint8 { }; };
struct TextureUploadComp { enum Type : uint8 { }; };
struct UploadConfig {
const void * m_data; // 4 bytes (32bit)
glm::u16vec4 m_size; // 12 bytes (uint16 * 4 = +8)
uint8 m_mipmapsCount;
TextureDim::Type m_depthType;
TextureInternalFormat::Type m_internalFormat;
TextureUploadFormat::Type m_uploadFormat; // 16 bytes
TextureUploadComp::Type m_uploadCompType;
uint8 m_swizzleR :3;
uint8 m_swizzleG :3;
uint8 m_swizzleB :3;
uint8 m_swizzleA :3;
bool m_minFilter :1;
bool m_minmipFilterUsed :1;
bool m_minmipFilter :1;
bool m_maxFilter :1;
bool m_edgeClampX :1;
bool m_edgeMirrorX :1;
bool m_edgeClampY :1;
bool m_edgeMirrorY :1;
bool m_edgeClampZ :1;
bool m_edgeMirrorZ :1;
};
const int iogwhgakfj = sizeof(UploadConfig);
static int diff(void* b, void* m) {
auto bb = static_cast<char*>(b);
auto mm = static_cast<char*>(m);
return static_cast<int>(mm - bb);
}
static string zrange(void* b, size_t len) {
auto bb = static_cast<unsigned char*>(b);
ostringstream ss;
auto sep = "";
for (size_t pos = 0; pos < len; ++pos) {
if (bb[pos] == 0xFF) continue;
ss << sep << pos << "[";
auto u = bb[pos];
if ((u & 0b1000'0000) == 0) ss << "7"; else ss << ".";
if ((u & 0b0100'0000) == 0) ss << "6"; else ss << ".";
if ((u & 0b0010'0000) == 0) ss << "5"; else ss << ".";
if ((u & 0b0001'0000) == 0) ss << "4"; else ss << ".";
if ((u & 0b0000'1000) == 0) ss << "3"; else ss << ".";
if ((u & 0b0000'0100) == 0) ss << "2"; else ss << ".";
if ((u & 0b0000'0010) == 0) ss << "1"; else ss << ".";
if ((u & 0b0000'0001) == 0) ss << "0"; else ss << ".";
ss << "]";
sep = " ";
}
return ss.str();
}
int main() {
cout << "Size of UploadConfig: " << iogwhgakfj << "\n";
UploadConfig u;
#define DIFF(member) cout << setw(20) << #member << " " << diff(&u, &u.member) << "\n"
#define DIFFX(bitmember) memset(&u, 0xFF, sizeof u); u.bitmember = 0; cout << setw(20) << #bitmember << " " << zrange(&u, sizeof u) << "\n"
DIFF(m_data);
DIFF(m_size);
DIFF(m_mipmapsCount);
DIFF(m_depthType);
DIFF(m_internalFormat);
DIFF(m_uploadFormat);
DIFF(m_uploadCompType);
DIFFX(m_swizzleR);
DIFFX(m_swizzleG);
DIFFX(m_swizzleB);
DIFFX(m_swizzleA);
DIFFX(m_minFilter);
DIFFX(m_minmipFilterUsed);
DIFFX(m_minmipFilter);
DIFFX(m_maxFilter);
DIFFX(m_edgeClampX);
DIFFX(m_edgeMirrorX);
DIFFX(m_edgeClampY);
DIFFX(m_edgeMirrorY);
DIFFX(m_edgeClampZ);
DIFFX(m_edgeMirrorZ);
#undef DIFF
#undef DIFFX
}