C++ 导出编译时常量,同时限制类的可见性

C++ 导出编译时常量,同时限制类的可见性,c++,constants,visibility,encapsulation,C++,Constants,Visibility,Encapsulation,我使用的是C++11之前的编译器,我试图“导出”一个常量,而不公开从中计算该常量的类 // A.hpp ---------------------- struct A{ ... }; // B.hpp ---------------------- struct B{ ... }; // Manager.hpp ------------------ #include "Manager.hpp" template <size_t a, size_t b> struct StaticMa

我使用的是C++11之前的编译器,我试图“导出”一个常量,而不公开从中计算该常量的类

// 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);
    ....
}
//A.hpp----------------------
结构A{
...
};
//水电站----------------------
结构B{
...
};
//Manager.hpp------------------
#包括“Manager.hpp”
样板
结构静态最大
{
枚举{value=a>b?a:b};
}
班级经理
{
公众:
静态常量大小\u t覆盖数据大小;
...
};
//manager.cpp------------------
#包括“A.hpp”
#包括“B.hpp”
//我希望以下常量对任何人都可用
//这包括Manager.hpp,但A类和B类不应包括在内
//对除类经理以外的任何其他人都可见
const size\u t Manager::overlydatasize=StaticMax::value;
//otherfile.hpp-------------------
#包括“Manager.hpp”
结构覆盖数据
{
//共享状态在这里
uint8配子状态;
uint8 specificState[Manager::overlydatasize];
};
类NvRam
{
无效写入(覆盖数据和序列化数据);
....
}
上述代码将不会编译并导致:

错误:“Manager::OverlyDataSize”不是类型“unsigned”的有效模板参数 int'因为它是一个非常量表达式

这已经很奇怪了,因为
Manager::overlysize
最确定的是
const
,它的值是在编译时计算的。但根据问题,如果
const
声明及其定义不在同一位置,编译器就不能将其用作常量。即使使用用
extern
声明的全局变量,错误仍然存在。我可以通过使用一个并集来计算不同的最大大小(但是结构
a
B
不允许有构造函数),但这不是问题,我仍然不能在编译时导出该常量,而不向所有人公开结构
a
B
。当然,我可以通过使DataOverlay结构更加复杂并使用
newUINT8[Manager::OverlayDataSize]来回避整个问题在运行时,并能够保持严格的分离。但我希望这是在编译时静态完成的

因此,如何“导出”编译时常量,同时保持结构
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
或类似的程序来编写生成的代码。其他源文件应仅包含生成的源文件


<>因为C++没有一个模块系统(它可以让你隐藏一些内部实体)或者一个完整的元编程工具(它允许我们编写一些代码生成其他代码),所以我只能想到这个实现这个的相当丑陋的方法。但无论如何,它应该是有效的

使用编译时常量时,编译器应该已经知道它的值。因此,为了使用您的
overlydatasize
,您必须在范围内定义它,因此需要在范围内定义
A
B
。因此基本上无法实现我的目标。。。这似乎是C头文件系统的遗留问题,或者这与编译器的工作方式有什么更具体的原因吗?由于
OverlyDataSize
是在编译时计算的,因此我认为原则上没有理由不这样做,但是,这可能需要为常量输入占位符,并在编译器进行必要的计算后重新检查编译后的代码并替换占位符……我担心编译器就是不能这样工作。也就是说,我确实有一个解决方案,但它相当丑陋。无论如何,我会为这个丑陋的解决方案写一个答案。
A
B
是否用于其他用途?考虑到代码的设置方式,我希望它们是这样的,但在这种情况下,我不确定您为什么要隐藏它们,因为无论包含什么
“Manager.hpp”
都可能只包含
“A.hpp”
“B.hpp”
。如果不是,您可以将它们设置为
管理器
的内部成员类。也许只是把它们放在一个名称空间中,并称之为好的。啊,聪明!这确实实现了完全的分离。。。我曾经在一个项目中使用过类似的生成程序来生成版本号的想法。