我有一个应用程序,它由多个使用共享内存共享公共数据的任务组成。到目前为止,共享内存中的数据看起来像这样:
struct Store = {
int id;
Array<Module, 5> modules;
};
其中Module
定义为
struct Module = {
uint32_t a;
char b[64];
Array<Component, 10> components;
};
此Store
结构具有固定大小,可以在共享内存中轻松使用。
但现在我必须支持其他模块,让我们说ModuleA
和ModuleB
。在普通 C ++上下文中,我将它们建模为:
struct ModuleBase {
// common informations
};
struct ModuleA : public ModuleBase {
// ...
};
struct ModuleB : public ModuleBase {
// ...
};
并将Module
替换为Module*
中的Store
。
但在共享内存中,这并不容易。 访问共享内存中的数据对于修复结构来说很容易,这就是使用编译时数组的原因。我想将这个属性与我的不同模块一起使用。
union Module {
ModuleType type;
ModuleA moduleA;
ModuleB moduleB;
};
问题是我的Module
类有构造函数,而union
中不允许这样做。使用type
然后Module.moduleX
轻松访问
修复:删除需要构造函数
使用评估给定类的最大大小的模板,例如
const size_t max_module_size = MaxTMP<ModuleA, ModuleB>::value;
这是我存储模块所需的缓冲区大小:
char ModuleBuffer[max_module_size];
(也许ModuleBuffer
必须封装在一个结构中,以便与Array
一起使用
访问非常棘手,ModuleBuffer
的内容必须根据ModuleBase
和type
投放到ModuleX
。我认为我需要一些reinterpret_cast
。而且我还需要以某种方式投射'ModuleX'以放入ModuleBuffer
。
我不喜欢这两种想法,但我无法想象另一种方法来处理这个问题。您有任何意见或 - 甚至更好 - 解决方案吗?
答案 0 :(得分:2)
实际上,你处在一块岩石和一块坚硬的地方之间。
我会尝试boost::variant
,因为它附带的设施,否则重建类似的东西并不太难,但它很长......
除了尺寸之外,您还需要注意对齐。这将有助于在这里使用C ++ 11,尽管可以使用几个库/扩展在C ++ 03中编写它。
请注意,union
并非如此特别,您可以在某种程度上轻松实现自己的,boost::variant
使其“标记为”。
一对情侣帮助很好:
/// Size and Alignment utilties
constexpr size_t max(size_t t) { return t; }
template <typename... U>
constexpr size_t max(size_t l, size_t r, U... tail) {
return l > max(r, tail...) ? l : max(r, tail...);
}
template <typename... T>
struct size { static size_t const value = max(sizeof(T)...); };
template <typename... T>
struct alignment { static size_t const value = max(alignof(T)...); };
/// Position of a type in the list
template <typename...> struct position;
template <typename T>
struct position<T> {
static size_t const value = 0;
};
template <typename T, typename Head, typename... Tail>
struct position<T, Head, Tail...> {
static size_t const value =
std::is_same<T, Head>::value ? 0 : 1 + position<T, Tail...>::value;
};
/// Type at a given position
template <size_t, typename...> struct at;
template <size_t N, typename T, typename... Tail>
struct at<N, T, Tail...> { typedef typename at<N-1, Tail..>::type type; };
template <typename T, typename... Tail>
struct at<0, T, Tail...> { typedef T type; };
现在开始真正的乐趣:如何以类型安全的方式应用函数,其类型可能在运行时更改:x?
/// Function application
template <typename...> struct Apply;
template <typename H, typename... Tail>
struct Apply<H, Tail...> {
// Mutable
template <typename Func>
static void Do(Func& f, void* storage, size_t tag) {
if (tag == 0) { f(*reinterpret_cast<H*>(storage)); }
else { Apply<Tail...>::Do(f, storage, tag-1); }
}
template <typename Func>
static void Do(Func const& f, void* storage, size_t tag) {
if (tag == 0) { f(*reinterpret_cast<H*>(storage)); }
else { Apply<Tail...>::Do(f, storage, tag-1); }
}
// Const
template <typename Func>
static void Do(Func& f, void const* storage, size_t tag) {
if (tag == 0) { f(*reinterpret_cast<H const*>(storage)); }
else { Apply<Tail...>::Do(f, storage, tag-1); }
}
template <typename Func>
static void Do(Func const& f, void const* storage, size_t tag) {
if (tag == 0) { f(*reinterpret_cast<H const*>(storage)); }
else { Apply<Tail...>::Do(f, storage, tag-1); }
}
}; // struct Apply
/// We need recursion to end quietly even though `tag` is a runtime argument
/// we place the precondition that `tag` should be a valid index in the type
/// list so this should never be reached.
template <>
struct Apply<> {
template <typename... T>
static void Do(T...&&) { abort(); }
}; // struct Apply
现在我们可以使用它以类型安全的方式动态调度。
/// Variant itself
template <typename... List>
class Variant {
public:
/// Constructor & co
Variant() {
typedef typename at<0, List...>::type First;
new (&_storage) First();
}
Variant(Variant const& other) {
this->initialize(other);
}
Variant& operator=(Variant const& other) {
this->destroy();
this->initialize(other);
return *this;
}
~Variant() { this->destroy(); }
/// Conversions
template <typename T>
explicit Variant(T const& t) {
_tag = position<T, List...>::value;
new (&_storage) T(t);
}
template <typename T>
Variant& operator=(T const& t) {
_tag = position<T, List...>::value;
this->destroy();
new (&_storage) T(t);
return *this;
}
/// Applying a func
template <typename Func>
void apply(Func& f) { Apply<List...>::Do(f, &_storage, _tag); }
template <typename Func>
void apply(Func& f) const { Apply<List...>::Do(f, &_storage, _tag); }
template <typename Func>
void apply(Func const& f) { Apply<List...>::Do(f, &_storage, _tag); }
template <typename Func>
void apply(Func const& f) const { Apply<List...>::Do(f, &_storage, _tag); }
private:
void initialize(Variant const& v) {
struct {
template <typename T>
void operator()(T& t) const { new (_storage) T(t); }
void* _storage;
} copier = { &_storage };
v.apply(copier);
_tag = v._tag;
}
void destroy() {
struct {
template <typename T>
void operator()(T& t) const { t.~T(); }
} eraser;
this->apply(eraser);
}
std::aligned_storage<size<List...>::value,
alignment<List...>::value> _storage;
size_t _tag;
}; // class Variant
我说容易吗?
嗯,还有一个微妙的问题:operator=
实现不是异常安全的。在您的情况下,它不应该是一个问题,因为您没有在这些类型中分配动态内存。
参考文献: