不同结构的静态大小数组

时间:2012-04-14 12:15:23

标签: c++ struct shared-memory

我有一个应用程序,它由多个使用共享内存共享公共数据的任务组成。到目前为止,共享内存中的数据看起来像这样:

struct Store = {
    int id;
    Array<Module, 5> modules;
};

其中Module定义为

struct Module = {
    uint32_t a;
    char b[64];
    Array<Component, 10> components;
};

Store结构具有固定大小,可以在共享内存中轻松使用。

但现在我必须支持其他模块,让我们说ModuleAModuleB。在普通 C ++上下文中,我将它们建模为:

struct ModuleBase {
    // common informations
};
struct ModuleA : public ModuleBase {
    // ...
};
struct ModuleB : public ModuleBase {
    // ...
};

并将Module替换为Module*中的Store

但在共享内存中,这并不容易。 访问共享内存中的数据对于修复结构来说很容易,这就是使用编译时数组的原因。我想将这个属性与我的不同模块一起使用。

想法1

union Module {
    ModuleType type;
    ModuleA moduleA;
    ModuleB moduleB;
};

问题是我的Module类有构造函数,而union中不允许这样做。使用type然后Module.moduleX轻松访问 修复:删除需要构造函数

创意2

使用评估给定类的最大大小的模板,例如

const size_t max_module_size = MaxTMP<ModuleA, ModuleB>::value;

这是我存储模块所需的缓冲区大小:

char ModuleBuffer[max_module_size];

(也许ModuleBuffer必须封装在一个结构中,以便与Array一起使用

访问非常棘手,ModuleBuffer的内容必须根据ModuleBasetype投放到ModuleX。我认为我需要一些reinterpret_cast。而且我还需要以某种方式投射'ModuleX'以放入ModuleBuffer

问题

我不喜欢这两种想法,但我无法想象另一种方法来处理这个问题。您有任何意见或 - 甚至更好 - 解决方案吗?

1 个答案:

答案 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=实现不是异常安全的。在您的情况下,它不应该是一个问题,因为您没有在这些类型中分配动态内存。

参考文献: