导出编译时间常数,同时限制类的可见性

时间:2019-01-24 01:23:31

标签: c++ const 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 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);
    ....
}

上面的代码无法编译并导致:

  

错误:“ Manager :: OverlayDataSize”不是类型为“ unsigned”的有效模板参数   int”,因为它是非恒定表达式

已经很奇怪了,因为Manager::OverlaySize最肯定是const,并且它的值是在编译时计算的。但是根据this的问题,如果const声明及其定义不在同一位置,则编译器不能将其用作常量。即使使用extern声明的全局变量,该错误仍然存​​在。我可以通过使用联合来计算最大大小(但然后不允许结构AB具有构造函数),但这不是问题,我仍然无法导出该常数可以在编译时使用,而不会将结构AB暴露给所有人。当然,我可以通过使DataOverlay结构更复杂并在运行时使用new uint8[Manager::OverlayDataSize];并能够保持严格的分隔来避免整个问题。但是我正在努力在编译时将其静态地完成。

那么如何在保持结构ABManager的用户之间严格分隔的同时“导出”编译时常量呢?

1 个答案:

答案 0 :(得分:1)

这是一个(非常丑陋的)解决方案。

核心问题是,您只需要AB的大小,它们是常量,但是您必须包括整个定义。解决方案是手动计算尺寸并将其写入所需的位置。

但是修改AB时很容易忘记更新值,因此我们应该以某种方式自动完成上述工作。

要实现此目的,您可以编写一个代码生成器,该代码生成器将这样的代码生成到头文件中:

const size_t OverlayDataSize = /* the calculated size */;

,并在每次重建整个项目时调用该程序。 (例如,通过编写一个Makefile。)

该生成器可以包含A.hppB.hpp,计算max(sizeof(A), sizeof(B))并运行printf或类似的东西来编写生成的代码。其他源文件应仅#include生成的源。

由于C ++没有模块系统(可让您隐藏一些内部实体)或完整的元编程工具(可让我们编写一些生成其他代码的代码),所以我只能想到这种相当丑陋的方式为达到这个。但是无论如何,它应该起作用。