在什么平台上会崩溃,我该如何改进呢?

Jon*_*rdy 5 c++ struct dynamic data-structures

我已经编写了一个用于在C++中创建动态结构的类的基础知识.动态结构成员与(在我的测试中指示)连续存储编译器将在等效静态结构中插入的相同填充.因此,动态结构可以隐式转换为静态结构,以便与现有API互操作.

最重要的是,我不相信自己能够编写可以在或多或少任何平台上编译和工作的Boost质量代码.这段代码的哪些部分是危险的需要修改?

我还有一个与设计相关的问题:模板化get访问器是为编译器提供类型安全代码所需的静态类型信息的唯一方法吗?实际上,用户dynamic_struct必须在访问时指定他们正在访问的成员的类型.如果该类型应该更改,则所有访问将变为无效,并且会导致严重的崩溃 - 或者更糟糕的是,无声地失败.并且它无法在编译时捕获.这是一个巨大的风险,我想补救一个.

用法示例:

struct Test {

    char a, b, c;
    int i;
    Foo object;

};

void bar(const Test&);

int main(int argc, char** argv) {

    dynamic_struct<std::string> ds(sizeof(Test));

    ds.append<char>("a") = 'A';
    ds.append<char>("b") = '2';
    ds.append<char>("c") = 'D';
    ds.append<int>("i") = 123;
    ds.append<Foo>("object");
    bar(ds);

}
Run Code Online (Sandbox Code Playgroud)

代码如下:

//
// dynamic_struct.h
//
// Much omitted for brevity.
//


/**
 * For any type, determines the alignment imposed by the compiler.
 */
template<class T>
class alignment_of {
private:

    struct alignment {

        char a;
        T b;

    }; // struct alignment

public:

    enum { value = sizeof(alignment) - sizeof(T) };

}; // class alignment_of


/**
 * A dynamically-created structure, whose fields are indexed by keys of
 * some type K, which can be substituted at runtime for any structure
 * with identical members and packing.
 */
template<class K>
class dynamic_struct {
public:


    // Default maximum structure size.
    static const int DEFAULT_SIZE = 32;


    /**
     * Create a structure with normal inter-element padding.
     */
    dynamic_struct(int size = DEFAULT_SIZE) : max(size) {

        data.reserve(max);

    } // dynamic_struct()


    /**
     * Copy structure from another structure with the same key type.
     */
    dynamic_struct(const dynamic_struct& structure) :
        members(structure.members), max(structure.max) {

        data.reserve(max);

        for (iterator i = members.begin(); i != members.end(); ++i)
            i->second.copy(&data[0] + i->second.offset,
                &structure.data[0] + i->second.offset);

    } // dynamic_struct()


    /**
     * Destroy all members of the structure.
     */
    ~dynamic_struct() {

        for (iterator i = members.begin(); i != members.end(); ++i)
            i->second.destroy(&data[0] + i->second.offset);

    } // ~dynamic_struct()


    /**
     * Get a value from the structure by its key.
     */
    template<class T>
    T& get(const K& key) {

        iterator i = members.find(key);

        if (i == members.end()) {

            std::ostringstream message;
            message << "Read of nonexistent member \"" << key << "\".";
            throw dynamic_struct_access_error(message.str());

        } // if

        return *reinterpret_cast<T*>(&data[0] + i->second.offset.offset);

    } // get()


    /**
     * Append a member to the structure.
     */
    template<class T>
    T& append(const K& key, int alignment = alignment_of<T>::value) {

        iterator i = members.find(key);

        if (i != members.end()) {

            std::ostringstream message;
            message << "Add of already existing member \"" << key << "\".";
            throw dynamic_struct_access_error(message.str());

        } // if

        const int modulus = data.size() % alignment;
        const int delta = modulus == 0 ? 0 : sizeof(T) - modulus;

        if (data.size() + delta + sizeof(T) > max) {

            std::ostringstream message;

            message << "Attempt to add " << delta + sizeof(T)
                << " bytes to struct, exceeding maximum size of "
                << max << ".";

            throw dynamic_struct_size_error(message.str());

        } // if

        data.resize(data.size() + delta + sizeof(T));

        new (static_cast<void*>(&data[0] + data.size() - sizeof(T))) T;

        std::pair<iterator, bool> j = members.insert
            ({key, member(data.size() - sizeof(T), destroy<T>, copy<T>)});

        if (j.second) {

            return *reinterpret_cast<T*>(&data[0] + j.first->second.offset);

        } else {

            std::ostringstream message;
            message << "Unable to add member \"" << key << "\".";
            throw dynamic_struct_access_error(message.str());

        } // if

    } // append()


    /**
     * Implicit checked conversion operator.
     */
    template<class T>
    operator T&() { return as<T>(); }


    /**
     * Convert from structure to real structure.
     */
    template<class T>
    T& as() {

        // This naturally fails more frequently if changed to "!=".
        if (sizeof(T) < data.size()) {

            std::ostringstream message;

            message << "Attempt to cast dynamic struct of size "
                << data.size() << " to type of size " << sizeof(T) << ".";

            throw dynamic_struct_size_error(message.str());

        } // if

        return *reinterpret_cast<T*>(&data[0]);

    } // as()


private:


    // Map from keys to member offsets.
    map_type members;

    // Data buffer.
    std::vector<unsigned char> data;

    // Maximum allowed size.
    const unsigned int max;


}; // class dynamic_struct
Run Code Online (Sandbox Code Playgroud)

Pup*_*ppy 1

这种代码本身并没有什么问题。将类型检查延迟到运行时是完全有效的,尽管您必须努力击败编译时类型系统。我编写了一个同质堆栈类,您可以在其中插入任何类型,其功能类似。

但是,您必须问自己 - 您实际上要使用它做什么?我编写了一个同构堆栈来替换解释语言的 C++ 堆栈,这对于任何特定类来说都是一个相当高的要求。如果你不做一些激烈的事情,这可能不是正确的做法。

简而言之,你可以做到,而且它不是非法的、不好的或未定义的,你可以让它工作——但只有当你非常迫切需要做正常语言范围之外的事情时,你才应该这样做。另外,当 C++0x 成为标准时,您的代码将可怕地死亡,现在您需要移动以及所有其他内容。

考虑代码的最简单方法实际上是一个小型的托管堆。您放置在各种类型的对象上......它们是连续存储的,等等。

编辑:等等,您也没有设法在运行时强制执行类型安全?您只是破坏了编译时类型安全性但没有替换它?让我发布一些更高级的代码(可能会慢一些)。

编辑:哦等等。您想在运行时将您的动态结构作为整体转换为任意未知的其他结构吗?哦。天啊。哦,说真的。什么。就是不行。只是不要。真的,真的,不要。这是非常错误的,令人难以置信。如果你有反思,你就可以做到这一点,但 C++ 不提供这一点。您可以使用dynamic_cast 和继承的类型擦除在运行时为每个单独的成员强制执行类型安全。不适用于整个结构,因为给定类型 T,您无法分辨类型或二进制布局是什么。