代码之家  ›  专栏  ›  技术社区  ›  Jon Purdy

这将在什么平台上崩溃,我如何改进它?

  •  5
  • Jon Purdy  · 技术社区  · 14 年前

    最重要的是,我不相信自己能够编写出高质量的代码,这些代码可以在任何平台上编译和工作。此代码的哪些部分需要进行危险的修改?

    我还有一个与设计相关的问题:模板 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);
    
    }
    

    代码如下:

    //
    // 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
    
    3 回复  |  直到 14 年前
        1
  •  1
  •   Puppy    14 年前

    简而言之,你可以这样做,它不是非法的,不好的或未定义的,你可以让它工作-但你应该只有当你有一个非常迫切的需要做的事情以外的正常语言范围。另外,当C++ 0x变成标准,现在你需要移动,剩下的所有代码都会死掉。

    等等,你也没有在运行时执行类型安全?你刚刚破坏了编译时类型安全,但没有替换它?让我发布一些更高级的代码(可能会慢一些)。

    编辑:哦,等等。你想在运行时将动态结构转换成任意未知的其他结构吗?哦。哦,伙计。哦,说真的。什么。只是不。只是不要。真的,真的,不要。那太错了,难以置信。如果你有反射,你可以做这个工作,但是C++不提供这个。您可以使用带有继承的动态类型转换和类型擦除,在运行时为每个成员强制执行类型安全性。不是针对整个结构,因为给定一个类型T,您无法判断类型或二进制布局是什么。

        2
  •  1
  •   Potatoswatter R. Martinho Fernandes    14 年前

    reinterpret_cast 它本身可以是任何大小相同的类型。

    #define REGISTER_DYNAMIC_STRUCT_CLIENT( STRUCT, MEMBER ) \
        do dynamic_struct::registry< STRUCT >() // one registry obj per client type \
            .add( # MEMBER, &STRUCT::MEMBER, offsetof( STRUCT, MEMBER ) ) while(0)
            //    ^ name as str ^ ptr to memb ^ check against dynamic offset
    
        3
  •  0
  •   Matthieu M.    14 年前

    我有一个问题:你从中得到了什么?

    我是说这是一段很聪明的代码,但是:

    我真正想知道的是你到底想要什么。。。

    例如,使用 Boost.Fusion

    struct a_key { typedef char type; };
    struct object_key { typedef Foo type; };
    
    typedef boost::fusion<
      std::pair<a_key, a_key::type>,
      std::pair<object_key, object_key::type>
    > data_type;
    
    int main(int argc, char* argv[])
    {
      data_type data;
      boost::fusion::at_key<a_key>(data) = 'a'; // compile time checked
    }
    

    char Foo ).

    DECLARE_ATTRIBUTES(
      mData,
      (char, a)
      (char, b)
      (char, c)
      (int, i)
      (Foo, object)
    )
    

    不过,这并不像一个典型的声明那么冗长 a , b 等等。。。将是内部类型而不是属性名称。

    这比您的解决方案有几个优点:

    • 完全符合默认生成的构造函数/复制构造函数/等等。。。
    • 更紧凑的表示