导出编译时间常量,同时限制 class 可见性
Export a compile time constant, while limiting class visibility
我正在使用 C++11 之前的编译器,我正在尝试 "export" 一个常量,而不公开计算此常量的 类。
// A.hpp ----------------------
struct A{
...
};
// B.hpp ----------------------
struct B{
...
};
// Manager.hpp ------------------
#include "Manager.hpp"
template <size_t a, size_t b>
struct StaticMax
{
enum { value = a>b ? a : b };
}
class Manager
{
public:
static const size_t OverlayDataSize;
...
};
// manager.cpp ------------------
#include "A.hpp"
#include "B.hpp"
// I want the following constant to be available to anyone
// that includes Manager.hpp, but the classes A and B should not
// be visible to anyone else except class Manager
const size_t Manager::OverlayDataSize = StaticMax<sizeof(A),sizeof(B)>::value;
// otherfile.hpp -------------------
#include "Manager.hpp"
struct OverlayData
{
// shared state goes here
uint8 gameState;
uint8 specificState[Manager::OverlayDataSize];
};
class NvRam
{
void Write(OverlayData& serializedState);
....
}
以上代码无法编译并导致:
error: ‘Manager::OverlayDataSize' is not a valid template argument for type ‘unsigned
int’ because it is a non-constant expression
这已经很奇怪了,因为 Manager::OverlaySize
最肯定是 const
并且它的值是在编译时计算的。但是根据 this 问题,如果 const
声明和它的定义不在同一个位置,那么编译器就不能将它用作常量。即使您使用用 extern
声明的全局变量,错误仍然存在。我可以通过使用联合来计算不同的最大大小(但是结构 A
和 B
不允许有构造函数),但这不是问题,我仍然无法导出该常量在编译时可用,而无需向所有人公开结构 A
和 B
。当然,我可以通过使 DataOverlay 结构更复杂一些并在运行时使用 new uint8[Manager::OverlayDataSize];
来回避整个问题,并能够保持严格的分离。但我希望在编译时静态地完成这项工作。
那么如何 "export" 一个编译时常量,同时保持结构 A
和 B
与 Manager
的用户之间的严格分离?
这是一个(相当丑陋的)解决方案。
核心问题是您只需要 A
和 B
的大小,它们是常量,但您被迫包含整个定义。解决方案是手动计算大小并将其写入所需位置。
但是在修改A
和B
时很容易忘记更新值,所以我们应该以某种方式自动完成上述工作。
为此,您可以编写一个代码生成器,它将像这样的代码生成到头文件中:
const size_t OverlayDataSize = /* the calculated size */;
并在每次重建整个项目时调用该程序。 (例如,通过编写 Makefile。)
那个生成器可以包含A.hpp
和B.hpp
,计算max(sizeof(A), sizeof(B))
和运行一个printf
或类似的东西来编写生成的代码。其他源文件应该只 #include
生成的源。
由于 C++ 没有模块系统(这将使您能够隐藏一些内部实体)或完整的元编程工具(这使我们能够编写一些生成其他代码的代码),所以我只能想到这个实现这一目标的丑陋方式。但无论如何,它应该工作。
我正在使用 C++11 之前的编译器,我正在尝试 "export" 一个常量,而不公开计算此常量的 类。
// A.hpp ----------------------
struct A{
...
};
// B.hpp ----------------------
struct B{
...
};
// Manager.hpp ------------------
#include "Manager.hpp"
template <size_t a, size_t b>
struct StaticMax
{
enum { value = a>b ? a : b };
}
class Manager
{
public:
static const size_t OverlayDataSize;
...
};
// manager.cpp ------------------
#include "A.hpp"
#include "B.hpp"
// I want the following constant to be available to anyone
// that includes Manager.hpp, but the classes A and B should not
// be visible to anyone else except class Manager
const size_t Manager::OverlayDataSize = StaticMax<sizeof(A),sizeof(B)>::value;
// otherfile.hpp -------------------
#include "Manager.hpp"
struct OverlayData
{
// shared state goes here
uint8 gameState;
uint8 specificState[Manager::OverlayDataSize];
};
class NvRam
{
void Write(OverlayData& serializedState);
....
}
以上代码无法编译并导致:
error: ‘Manager::OverlayDataSize' is not a valid template argument for type ‘unsigned int’ because it is a non-constant expression
这已经很奇怪了,因为 Manager::OverlaySize
最肯定是 const
并且它的值是在编译时计算的。但是根据 this 问题,如果 const
声明和它的定义不在同一个位置,那么编译器就不能将它用作常量。即使您使用用 extern
声明的全局变量,错误仍然存在。我可以通过使用联合来计算不同的最大大小(但是结构 A
和 B
不允许有构造函数),但这不是问题,我仍然无法导出该常量在编译时可用,而无需向所有人公开结构 A
和 B
。当然,我可以通过使 DataOverlay 结构更复杂一些并在运行时使用 new uint8[Manager::OverlayDataSize];
来回避整个问题,并能够保持严格的分离。但我希望在编译时静态地完成这项工作。
那么如何 "export" 一个编译时常量,同时保持结构 A
和 B
与 Manager
的用户之间的严格分离?
这是一个(相当丑陋的)解决方案。
核心问题是您只需要 A
和 B
的大小,它们是常量,但您被迫包含整个定义。解决方案是手动计算大小并将其写入所需位置。
但是在修改A
和B
时很容易忘记更新值,所以我们应该以某种方式自动完成上述工作。
为此,您可以编写一个代码生成器,它将像这样的代码生成到头文件中:
const size_t OverlayDataSize = /* the calculated size */;
并在每次重建整个项目时调用该程序。 (例如,通过编写 Makefile。)
那个生成器可以包含A.hpp
和B.hpp
,计算max(sizeof(A), sizeof(B))
和运行一个printf
或类似的东西来编写生成的代码。其他源文件应该只 #include
生成的源。
由于 C++ 没有模块系统(这将使您能够隐藏一些内部实体)或完整的元编程工具(这使我们能够编写一些生成其他代码的代码),所以我只能想到这个实现这一目标的丑陋方式。但无论如何,它应该工作。