如何根据来自另一个翻译单元的类型信息静态分配内存
How to statically allocate memory based upon type information from another translation unit
我在一个翻译单元中有一堆复杂的 类,其中涉及一堆 header 依赖项。另外,翻译单元提供了工厂功能。
// MyClass.h
#include "Interface.h"
// lots of other includes
class MyClass : public Interface {
// lots of members
}:
// Creates an instance of MyClass using placement new
Interface* createMyClassAt(uint8_t* location);
在另一个翻译单元中,我想使用从 Interface
派生的不同 类 的多个实例,我想将它们分配到静态内存中。这是一个没有堆的小型嵌入式系统。我想避免包含 MyClass.h
因为它的某些依赖项是内部的。
// somefile.cpp
#include "Interface.h"
extern Interface* createMyClassAt(uint8_t* location);
uint8_t myClassContainer[sizeofMyClass];
int main() {
createMyClassAt(myClassContainer);
// more stoff
}
我的理解是,如果没有 MyClass
的实际类型信息,就不可能确定 sizeofMyClass
。不管我做什么。我无法跨翻译单元获取此信息。
那怎么实现我的目标呢?我是否需要通过构建系统并以某种方式从 object 文件中提取大小并从中生成 header?毕竟那可能没问题。
编辑 1:一些说明:
- 所有从 Interface.h 派生的 类 都在末尾的预链接 self-contained 静态库中定义。
- 我所说的“内部依赖项”是指我不想泄露给库使用者的其他 header 文件和类型
- 库的使用者可以创建各种 类.
的多个实例
创建第二个函数,returns 大小为 class:
extern size_t sizeofMyClass();
size_t sizeofMyClass() { return sizeof(MyClass); }
如果你在编译时想要它,那么constexpr
它。
这是第二次尝试回答这个问题。
你不能为所欲为。隐藏实现 (PIMPL) 的唯一原因是允许您完全独立地编译模块。
您不能这样做,同时注入依赖项。你要的是self-contradictory.
要么在header中声明class,以便声明依赖关系,要么将数组放入包含class的模块中。如果它是一个静态数组,为什么在哪个模块中声明它很重要。
换句话说,使用 singleton,可通过 free-function
访问
您可以在“someFile”翻译单元中声明myClassContainer
,但实际上在“myClass”翻译单元中定义它。使用通用头文件可以使这更容易:
// globaldefs.h
#include <cstdint>
extern std::uint8_t myClassContainer[];
#ifdef MY_CLASS_DEFINED
alignas(MyClass) std::uint8_t myClassContainer[sizeof(MyClass)];
#endif
// MyClass.h
class MyClass : public Interface {
// lots of members
};
#define MY_CLASS_DEFINED
#include "globaldefs.h"
// somefile.cpp
#include "globaldefs.h"
extern Interface* createMyClassAt(uint8_t* location);
int main() {
createMyClassAt(myClassContainer);
// more stuff
}
这样,带有somefile.cpp
的翻译单元只看到std::uint8_t myClassContainer[];
,不需要大小。
How to achieve my goal then?
方法 1:静态断言和“猜测”大小。 interface.h
和 class 之间没有依赖关系,但是您必须 手动 更新每个更改的 header (或者,更好的是,生成header 来自构建系统)。
// interface.h
using Interface_storage = std::aligned_storage<20, 16>;
// ^^^^^^ - size and alignment
// They are _hard-coded_ here and _need_ to be _manually_ updated each time
// MyClass changes.
Interface* createMyClassAt(Interface_storage& location);
// interface.c
Interface* createMyClassAt(Interface_storage& location) {
// static assertion check
static_assert(sizeof(MyClass) == sizeof(Interface_storage) &&
alignof(MyClass) == alignof(Interface_storage),
" Go and fix the header file");
// use placement new on location
}
// main.c
int main() {
Interface_storage storage; // nice&clean
Interface *i = createMyClassAt(storage);
destroyMyClassAt(i, storage);
}
方法 2:Unix 系统自古以来就使用文件描述符。一个文件描述符很简单——它只是一个数组中的一个索引……一些东西。实现起来很简单,您可以将 一切 隐藏在单个整数值后面。这基本上意味着,您必须使用动态内存,或者您必须提前知道您需要多少 objects 并为所有这些内存分配内存。
下面的伪代码实现只是 returns 指向接口的指针,但它非常类似于返回数组中的索引,就像文件描述符一样。
// interface.h
Interface *new_MyClass();
destroy_MyClass(Interface *);
// interface.c
#define MAX 5
std::array<std::aligned_storage<sizeof(MyClass), alignof(MyClass)>, MAX> arr;
std::array<bool, MAX> used;
Interface *new_MyClass() {
// find the fist not used and return it.
for (size_t i = 0; i < used.size(); ++i) {
if (!used[i]) {
used[i] = true;
return new(arr[i]) MyClass();
}
}
return nullptr;
}
void destroy_MyClass(Interface *i) {
// update used array and destroy
}
我在一个翻译单元中有一堆复杂的 类,其中涉及一堆 header 依赖项。另外,翻译单元提供了工厂功能。
// MyClass.h
#include "Interface.h"
// lots of other includes
class MyClass : public Interface {
// lots of members
}:
// Creates an instance of MyClass using placement new
Interface* createMyClassAt(uint8_t* location);
在另一个翻译单元中,我想使用从 Interface
派生的不同 类 的多个实例,我想将它们分配到静态内存中。这是一个没有堆的小型嵌入式系统。我想避免包含 MyClass.h
因为它的某些依赖项是内部的。
// somefile.cpp
#include "Interface.h"
extern Interface* createMyClassAt(uint8_t* location);
uint8_t myClassContainer[sizeofMyClass];
int main() {
createMyClassAt(myClassContainer);
// more stoff
}
我的理解是,如果没有 MyClass
的实际类型信息,就不可能确定 sizeofMyClass
。不管我做什么。我无法跨翻译单元获取此信息。
那怎么实现我的目标呢?我是否需要通过构建系统并以某种方式从 object 文件中提取大小并从中生成 header?毕竟那可能没问题。
编辑 1:一些说明:
- 所有从 Interface.h 派生的 类 都在末尾的预链接 self-contained 静态库中定义。
- 我所说的“内部依赖项”是指我不想泄露给库使用者的其他 header 文件和类型
- 库的使用者可以创建各种 类. 的多个实例
创建第二个函数,returns 大小为 class:
extern size_t sizeofMyClass();
size_t sizeofMyClass() { return sizeof(MyClass); }
如果你在编译时想要它,那么constexpr
它。
这是第二次尝试回答这个问题。
你不能为所欲为。隐藏实现 (PIMPL) 的唯一原因是允许您完全独立地编译模块。 您不能这样做,同时注入依赖项。你要的是self-contradictory.
要么在header中声明class,以便声明依赖关系,要么将数组放入包含class的模块中。如果它是一个静态数组,为什么在哪个模块中声明它很重要。
换句话说,使用 singleton,可通过 free-function
访问您可以在“someFile”翻译单元中声明myClassContainer
,但实际上在“myClass”翻译单元中定义它。使用通用头文件可以使这更容易:
// globaldefs.h
#include <cstdint>
extern std::uint8_t myClassContainer[];
#ifdef MY_CLASS_DEFINED
alignas(MyClass) std::uint8_t myClassContainer[sizeof(MyClass)];
#endif
// MyClass.h
class MyClass : public Interface {
// lots of members
};
#define MY_CLASS_DEFINED
#include "globaldefs.h"
// somefile.cpp
#include "globaldefs.h"
extern Interface* createMyClassAt(uint8_t* location);
int main() {
createMyClassAt(myClassContainer);
// more stuff
}
这样,带有somefile.cpp
的翻译单元只看到std::uint8_t myClassContainer[];
,不需要大小。
How to achieve my goal then?
方法 1:静态断言和“猜测”大小。 interface.h
和 class 之间没有依赖关系,但是您必须 手动 更新每个更改的 header (或者,更好的是,生成header 来自构建系统)。
// interface.h
using Interface_storage = std::aligned_storage<20, 16>;
// ^^^^^^ - size and alignment
// They are _hard-coded_ here and _need_ to be _manually_ updated each time
// MyClass changes.
Interface* createMyClassAt(Interface_storage& location);
// interface.c
Interface* createMyClassAt(Interface_storage& location) {
// static assertion check
static_assert(sizeof(MyClass) == sizeof(Interface_storage) &&
alignof(MyClass) == alignof(Interface_storage),
" Go and fix the header file");
// use placement new on location
}
// main.c
int main() {
Interface_storage storage; // nice&clean
Interface *i = createMyClassAt(storage);
destroyMyClassAt(i, storage);
}
方法 2:Unix 系统自古以来就使用文件描述符。一个文件描述符很简单——它只是一个数组中的一个索引……一些东西。实现起来很简单,您可以将 一切 隐藏在单个整数值后面。这基本上意味着,您必须使用动态内存,或者您必须提前知道您需要多少 objects 并为所有这些内存分配内存。
下面的伪代码实现只是 returns 指向接口的指针,但它非常类似于返回数组中的索引,就像文件描述符一样。
// interface.h
Interface *new_MyClass();
destroy_MyClass(Interface *);
// interface.c
#define MAX 5
std::array<std::aligned_storage<sizeof(MyClass), alignof(MyClass)>, MAX> arr;
std::array<bool, MAX> used;
Interface *new_MyClass() {
// find the fist not used and return it.
for (size_t i = 0; i < used.size(); ++i) {
if (!used[i]) {
used[i] = true;
return new(arr[i]) MyClass();
}
}
return nullptr;
}
void destroy_MyClass(Interface *i) {
// update used array and destroy
}