C ++中不同大小的不同行为(Firebreath源代码)

时间:2011-10-31 14:59:48

标签: c++ templates firebreath

当我浏览firebreath的源代码(src/ScriptingCore/Variant.h)时遇到一个疑惑的问题

    // function pointer table
    struct fxn_ptr_table {
        const std::type_info& (*get_type)();
        void (*static_delete)(void**);
        void (*clone)(void* const*, void**);
        void (*move)(void* const*,void**);
        bool (*less)(void* const*, void* const*);
    };

    // static functions for small value-types 
    template<bool is_small>
    struct fxns
    {
        template<typename T>
        struct type {
            static const std::type_info& get_type() { 
                return typeid(T); 
            }
            static void static_delete(void** x) { 
                reinterpret_cast<T*>(x)->~T(); 
            }
            static void clone(void* const* src, void** dest) { 
                new(dest) T(*reinterpret_cast<T const*>(src)); 
            }
            static void move(void* const* src, void** dest) { 
                reinterpret_cast<T*>(dest)->~T(); 
                *reinterpret_cast<T*>(dest) = *reinterpret_cast<T const*>(src); 
            }
            static bool lessthan(void* const* left, void* const* right) {
                T l(*reinterpret_cast<T const*>(left));
                T r(*reinterpret_cast<T const*>(right));

                return l < r;
            }
        };
    };

    // static functions for big value-types (bigger than a void*)
    template<>
    struct fxns<false>
    {
        template<typename T>
        struct type {
            static const std::type_info& get_type() { 
                return typeid(T); 
            }
            static void static_delete(void** x) { 
                delete(*reinterpret_cast<T**>(x)); 
            }
            static void clone(void* const* src, void** dest) { 
                *dest = new T(**reinterpret_cast<T* const*>(src)); 
            }
            static void move(void* const* src, void** dest) { 
                (*reinterpret_cast<T**>(dest))->~T(); 
                **reinterpret_cast<T**>(dest) = **reinterpret_cast<T* const*>(src); 
            }
            static bool lessthan(void* const* left, void* const* right) {
                return **reinterpret_cast<T* const*>(left) < **reinterpret_cast<T* const*>(right);
            }
        };
    };

    template<typename T>
    struct get_table 
    {
        static const bool is_small = sizeof(T) <= sizeof(void*);

        static fxn_ptr_table* get()
        {
            static fxn_ptr_table static_table = {
                fxns<is_small>::template type<T>::get_type
                , fxns<is_small>::template type<T>::static_delete
                , fxns<is_small>::template type<T>::clone
                , fxns<is_small>::template type<T>::move
                , fxns<is_small>::template type<T>::lessthan
            };
            return &static_table;
        }
    };

问题是为什么大值类型(大于void *)的静态函数的实现与小的类型不同。

例如,小值类型的static_delete只是在T实例上调用析构函数,而对于大值类型则是使用“删除”。

有诀窍吗?提前谢谢。

3 个答案:

答案 0 :(得分:2)

看起来Firebreath为其小对象使用专用内存池,而大型对象通常在堆中分配。因此,不同的行为。请注意new中的小clone()小位置对象,例如:这会在指定的内存位置创建新对象而不分配它。使用placement new创建对象时,必须在释放内存之前显式调用析构函数,这就是static_delete()所做的。

内存实际上并没有被释放,因为正如我所说,它看起来像是一个专用的内存池正在使用中。内存管理必须在其他地方执行。这种内存池是小对象的常见优化。

答案 1 :(得分:2)

内部文件说什么?如果作者没有 记录下来,他可能不了解自己。

从代码来看,与小对象的接口不同 对大型物体;你为一个小对象传递的指针是一个 指向对象本身的指针,作为传递给大对象的指针 object是指向对象的指针。

然而,作者似乎并不太了解C ++(而且我会 避免使用任何这样的代码)。例如,在move中,他明确表示 破坏对象,然后分配给它:这是保证未定义的 行为,除了最简单的事情,可能无法可靠地工作 内置类型。小型与大型物体的区别也很大 无关紧要的;一些“小”物体可能相当昂贵 复制。当然,鉴于此处的所有内容都是模板, 绝对没有理由将void*用于任何事情。

答案 2 :(得分:1)

我已经编辑了你的问题以包含原始源文件的链接,因为很明显,大多数在这里回答的人都没有阅读它以查看实际发生的情况。我承认这可能是FireBreath中最令人困惑的代码之一;当时,我试图避免使用增强功能,而且这种功能非常好。

从那时起我就考虑过切换到boost :: any(对于那些想要它的人来说,不,boost :: variant不会起作用,我不会在这里解释原因;如果你真的那么问另一个问题但是我们已经为这个类定制了相当多的东西,使它完全符合我们的需要和提升::任何都难以以类似的方式定制。最重要的是,我们一直在遵循旧的方法:如果没有破坏,请不要修复它!

首先,你应该知道几个C ++专家已经完成了这个代码;是的,它使用了一些许多人认为可疑的做法,但它们经过仔细考虑,并且在FireBreath支持的编译器上保持一致和可靠。我们已经使用valgrind,可视化泄漏检测器,LeakFinder和Rational Purify进行了大量测试,并且从未在此代码中发现任何泄漏。这有点令人困惑;令我惊讶的是,那些不懂代码的人认为作者不懂C ++。在这种情况下,Christopher Diggins(编写你引用的代码和原始的cdiggins ::任何来自这个类的类)似乎非常了解C ++,因为他能够编写这段代码。代码在内部使用并且经过高度优化 - 实际上可能比FireBreath需要更多。但是,它对我们有好处。

我会尽力解释你的问题的答案;请记住,我没有太多的时间,因为我真的深入挖掘它已经有一段时间了。使用不同静态类的“小”类型的主要原因是“小”类型几乎是内置类型;一个int,一个char,一个long等等。任何大于void *的东西都被认为是某种对象。这是一种优化,允许它尽可能重用内存,而不是删除和重新分配。

如果你并排查看代码,那就更加清晰了。如果你看一下删除和克隆,你会在“大”对象上看到它动态分配内存;它在删除中称为“删除”,在克隆中它使用常规的“新”。在“小”版本中,它只是在内部存储内存并重用它;它永远不会“删除”内存,它只是在内部的内存中调用析构函数或正确类型的构造函数。同样,这只是为了提高效率。在两种类型的移动中,它调用旧对象的析构函数,然后分配新的对象数据。

对象本身存储为void *,因为我们实际上并不知道对象的类型;要将对象取回,您必须指定类型。这是允许容器保存绝对任何类型数据的部分原因。这就是那里有那么多reinterpret_cast呼叫的原因 - 许多人看到并说“哦,不!作者一定是无能为力!”但是,如果您有一个需要取消引用的void *,那么这就是您要使用的运算符。

无论如何,所有这些都说,今年cdiggins实际上推出了他的任何一个班级的新版本;我需要看看它,可能会尝试将其拉入以替换当前的那个。诀窍是我已经定制了当前的一个(主要是为了添加一个比较运算符,所以它可以放在一个STL容器中并添加convert_cast)所以我需要确保我能够很好地理解新版本,以便安全地做到这一点。 / p>

希望有所帮助;我得到的文章是:http://www.codeproject.com/KB/cpp/dynamic_typing.aspx

请注意,该文章已更新,似乎无法再使用原始文章到旧版本。

修改

自从我写这篇文章以来,我们已经确认了旧版本类的一些问题,并且已经更新并替换为使用boost :: any的版本。感谢dougma对此的大部分工作。 FireBreath 1.7(截至本文撰写时的当前主分支)包含该修复。