标准库和本地的包含顺序 headers
Order of includes with standard libraries and local headers
我正在生成位图文件。该程序每次都会编译,但在某些时候根据 #include
的顺序,它会给出好的或损坏的 .bmp 文件。
它来自一个教程,所有文件都是 here.
我的结构如下:
main.cpp
#include <iostream>
#include "Bitmap.h"
using namespace std;
int main() {
Bitmap bitmap(800, 600);
bitmap.write("test.bmp");
}
Bitmap.h
#include <string>
#include <cstdint>
#include <memory>
using namespace std;
class Bitmap {
private:
int m_width{0};
int m_height{0};
unique_ptr<uint8_t[]> m_pPixels{nullptr};
public:
Bitmap(int width, int height);
bool write(string filename);
};
BitmapFileHeader.h
#include <cstdint>
using namespace std;
#pragma pack(2)
struct BitmapFileHeader {
char header[2] { 'B', 'M' };
int32_t fileSize;
int32_t reserved { 0 };
int32_t dataOffset;
};
BitmapInfoHeader.h
#include <cstdint>
using namespace std;
#pragma pack(2)
struct BitmapInfoHeader {
int32_t headerSize{40};
int32_t width;
int32_t height;
int16_t planes{1};
int16_t bitsPerPixel{24};
int32_t compression{0};
int32_t dataSize{0};
int32_t horizontalResolution{2400};
int32_t verticalResolution{2400};
int32_t colors{0};
int32_t importantColors{0};
};
Bitmap.cpp
#include <fstream>
#include "Bitmap.h"
#include "BitmapInfoHeader.h"
#include "BitmapFileHeader.h"
using namespace std;
Bitmap::Bitmap(int width, int height): m_width(width), m_height(height), m_pPixels(new uint8_t[width * height * 3]{ }) {}
bool Bitmap::write(string filename) {
BitmapFileHeader fileHeader;
BitmapInfoHeader infoHeader;
fileHeader.fileSize = sizeof(BitmapFileHeader) + sizeof(BitmapInfoHeader) + m_width * m_height * 3;
fileHeader.dataOffset = sizeof(BitmapFileHeader) + sizeof(BitmapInfoHeader);
infoHeader.width = m_width;
infoHeader.height = m_height;
ofstream file;
file.open(filename, ios::out | ios::binary);
file.write((char *)&fileHeader, sizeof(fileHeader));
file.write((char *)&infoHeader, sizeof(infoHeader));
file.write((char *)m_pPixels.get(), m_width*m_height*3);
file.close();
return true;
}
现在在 Bitmap.cpp
中按此顺序一切正常。当我更改顺序以便 #include <fstream>
排在最后时,它仍然编译没有错误但生成损坏的文件。
BitmapInfoHeader.h
和 BitmapInfoHeader.h
只是持有一个结构,每个结构中都有一些 int32_t
变量,根本不使用 fstream
。
为什么会这样?
没有 mcve 就无法确定。但是很多时候,当包含顺序中断编译时,它是由至少一个 header 未能包含其所有依赖项引起的。在这种情况下,如果包含缺失依赖项的另一个 header 恰好包含在损坏的 header 之前,则会导致错误被隐藏。
编辑添加的代码:
如果将标准 header 移动到 #pragma pack(2)
下面,就会破坏标准 class 的定义。您应该始终在 header 秒内推送和弹出打包编译指示。
错误在BitmapInfoHeader.h
// issue #1: There is no guard to prevent re-entry into you header.
#pragma once // this should fix that, you can also place a classic
// #ifndef / #define / #endif guard, as advised by the
// C++ Core Guidelines
// https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines
#include <cstdint>
using namespace std; // Issue #2:
// NEVER place a "using namespace std;" in a header
// file. BTW, it is not even needed for this
// file to compile.
// And NEVER place a "using namespace ...;"
// in the global namespace in a header file.
// issue # 3. Any pack pragma MUST be preceded by a push, like this:
#pragma pack(2) // <-- This pack pragma breaks all include
// files that will follow this one.
#pragma pack(push)
#pragma pack(1) // use pack(1) to signify full packing,
// so when someone else reads your code,
// they won't need to read the whole
// struct definition, to figure out
// what it is you are trying to
// accomplish.
struct BitmapInfoHeader {
int32_t headerSize{40};
int32_t width;
int32_t height;
int16_t planes{1};
int16_t bitsPerPixel{24};
int32_t compression{0};
int32_t dataSize{0};
int32_t horizontalResolution{2400};
int32_t verticalResolution{2400};
int32_t colors{0};
int32_t importantColors{0};
};
// After a #pragma(push), don't forget to restore the original
// packing, otherwise, you are violating the One Definition Rule.
// If you do not restore, everything after including this file
// will exhibit undefined behavior.
#pragma pack(pop)
文件Bitmap.h也有一些相同的问题。
我正在生成位图文件。该程序每次都会编译,但在某些时候根据 #include
的顺序,它会给出好的或损坏的 .bmp 文件。
它来自一个教程,所有文件都是 here.
我的结构如下:
main.cpp
#include <iostream>
#include "Bitmap.h"
using namespace std;
int main() {
Bitmap bitmap(800, 600);
bitmap.write("test.bmp");
}
Bitmap.h
#include <string>
#include <cstdint>
#include <memory>
using namespace std;
class Bitmap {
private:
int m_width{0};
int m_height{0};
unique_ptr<uint8_t[]> m_pPixels{nullptr};
public:
Bitmap(int width, int height);
bool write(string filename);
};
BitmapFileHeader.h
#include <cstdint>
using namespace std;
#pragma pack(2)
struct BitmapFileHeader {
char header[2] { 'B', 'M' };
int32_t fileSize;
int32_t reserved { 0 };
int32_t dataOffset;
};
BitmapInfoHeader.h
#include <cstdint>
using namespace std;
#pragma pack(2)
struct BitmapInfoHeader {
int32_t headerSize{40};
int32_t width;
int32_t height;
int16_t planes{1};
int16_t bitsPerPixel{24};
int32_t compression{0};
int32_t dataSize{0};
int32_t horizontalResolution{2400};
int32_t verticalResolution{2400};
int32_t colors{0};
int32_t importantColors{0};
};
Bitmap.cpp
#include <fstream>
#include "Bitmap.h"
#include "BitmapInfoHeader.h"
#include "BitmapFileHeader.h"
using namespace std;
Bitmap::Bitmap(int width, int height): m_width(width), m_height(height), m_pPixels(new uint8_t[width * height * 3]{ }) {}
bool Bitmap::write(string filename) {
BitmapFileHeader fileHeader;
BitmapInfoHeader infoHeader;
fileHeader.fileSize = sizeof(BitmapFileHeader) + sizeof(BitmapInfoHeader) + m_width * m_height * 3;
fileHeader.dataOffset = sizeof(BitmapFileHeader) + sizeof(BitmapInfoHeader);
infoHeader.width = m_width;
infoHeader.height = m_height;
ofstream file;
file.open(filename, ios::out | ios::binary);
file.write((char *)&fileHeader, sizeof(fileHeader));
file.write((char *)&infoHeader, sizeof(infoHeader));
file.write((char *)m_pPixels.get(), m_width*m_height*3);
file.close();
return true;
}
现在在 Bitmap.cpp
中按此顺序一切正常。当我更改顺序以便 #include <fstream>
排在最后时,它仍然编译没有错误但生成损坏的文件。
BitmapInfoHeader.h
和 BitmapInfoHeader.h
只是持有一个结构,每个结构中都有一些 int32_t
变量,根本不使用 fstream
。
为什么会这样?
没有 mcve 就无法确定。但是很多时候,当包含顺序中断编译时,它是由至少一个 header 未能包含其所有依赖项引起的。在这种情况下,如果包含缺失依赖项的另一个 header 恰好包含在损坏的 header 之前,则会导致错误被隐藏。
编辑添加的代码:
如果将标准 header 移动到 #pragma pack(2)
下面,就会破坏标准 class 的定义。您应该始终在 header 秒内推送和弹出打包编译指示。
错误在BitmapInfoHeader.h
// issue #1: There is no guard to prevent re-entry into you header.
#pragma once // this should fix that, you can also place a classic
// #ifndef / #define / #endif guard, as advised by the
// C++ Core Guidelines
// https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines
#include <cstdint>
using namespace std; // Issue #2:
// NEVER place a "using namespace std;" in a header
// file. BTW, it is not even needed for this
// file to compile.
// And NEVER place a "using namespace ...;"
// in the global namespace in a header file.
// issue # 3. Any pack pragma MUST be preceded by a push, like this:
#pragma pack(2) // <-- This pack pragma breaks all include
// files that will follow this one.
#pragma pack(push)
#pragma pack(1) // use pack(1) to signify full packing,
// so when someone else reads your code,
// they won't need to read the whole
// struct definition, to figure out
// what it is you are trying to
// accomplish.
struct BitmapInfoHeader {
int32_t headerSize{40};
int32_t width;
int32_t height;
int16_t planes{1};
int16_t bitsPerPixel{24};
int32_t compression{0};
int32_t dataSize{0};
int32_t horizontalResolution{2400};
int32_t verticalResolution{2400};
int32_t colors{0};
int32_t importantColors{0};
};
// After a #pragma(push), don't forget to restore the original
// packing, otherwise, you are violating the One Definition Rule.
// If you do not restore, everything after including this file
// will exhibit undefined behavior.
#pragma pack(pop)
文件Bitmap.h也有一些相同的问题。