2

我有一个应用程序,它由多个使用共享内存共享公共数据的任务组成。到目前为止,共享内存中的数据如下所示:

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 {
    // ...
};

并替换ModuleModule*.Store

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

理念一

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投射到ModuleBase并根据typeto ModuleX。那是因为我认为我需要一些reinterpret_cast. 而且我还需要以某种方式将“ModuleX”投射到ModuleBuffer.

问题

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

4

1 回答 1

2

实际上,您介于岩石和坚硬的地方之间。

我会尝试一下boost::variant,因为它附带的设施,否则重新创建类似的东西并不太难,但是它很长......


除了尺寸之外,您还需要注意对齐。在这里使用 C++11 会有所帮助,尽管可以在 C++03 中使用几个库/扩展来编写它。

请注意, aunion并不是什么特别的东西,您可以轻松地以某种方式实现您自己的,并且喜欢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=实现不是异常安全的。在您的情况下,这应该不是问题,因为您在这些类型中没有动态内存分配。

参考:

于 2012-04-14T15:59:28.583 回答