1

当我浏览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 实例上调用析构函数,而对于大值类型,使用 'delete'。

有什么诀窍吗?提前致谢。

4

3 回答 3

2

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

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

于 2011-10-31T15:10:56.527 回答
2

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

从代码来看,小对象的接口与大对象的接口不同;为小对象传递的指针是指向对象本身的指针,而为大对象传递的指针是指向对象的指针。

然而,作者似乎不太了解 C++(我会避免使用任何这样的代码)。例如,在 中move,他显式地破坏了对象,然后分配给它:这是保证未定义的行为,并且除了最简单的内置类型之外,可能无法可靠地工作。此外,小物体与大物体的区别在很大程度上是无关紧要的;一些“小”对象复制起来可能非常昂贵。当然,鉴于这里的一切都是模板,绝对没有理由使用void*任何东西。

于 2011-10-31T15:16:50.580 回答
1

我已经编辑了您的问题以包含指向原始源文件的链接,因为显然大多数在这里回答的人都没有阅读它以了解实际情况。我承认这可能是 FireBreath 中最令人困惑的代码之一。当时,我试图避免使用 boost,而且效果非常好。

从那以后,我考虑切换到 boost::any(对于那些渴望建议它的人,不,boost::variant 不起作用,我不会在这里解释为什么;如果你真的在乎,问另一个问题)但是我们已经对这个类进行了相当多的定制,以使其完全符合我们的需要,并且 boost::any 很难以类似的方式定制。最重要的是,我们一直在遵循古老的格言:如果它没有坏,就不要修复它!

首先,您应该知道有几位 C++ 专家已经检查过这段代码;是的,它使用了一些许多人认为可疑的做法,但经过仔细考虑,它们在 FireBreath 支持的编译器上是一致且可靠的。我们已经使用 valgrind、视觉泄漏检测器、LeakFinder 和 Rational Purify 进行了广泛的测试,并且从未在此代码中发现任何泄漏。这有点令人困惑。令我惊讶的是,不懂代码的人认为作者不懂 C++。在这种情况下,Christopher Diggins(他编写了您引用的代码和原始 cdiggins::any 类)似乎非常了解 C++,他能够编写此代码的事实证明了这一点。该代码在内部使用并且经过高度优化——实际上可能超出了 FireBreath 的需要。

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

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

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

无论如何,综上所述,cdiggins 今年实际上推出了他的 any 课程的新版本;我需要看一下它,并且可能会尝试将其拉入以替换当前的。诀窍是我已经定制了当前版本(主要是添加一个比较运算符,以便可以将其放入 STL 容器中并添加 convert_cast),因此我需要确保我对新版本的理解足以安全地做到这一点。

希望有帮助;我从中得到的文章在这里: http: //www.codeproject.com/KB/cpp/dynamic_typing.aspx

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

编辑

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

于 2011-10-31T16:34:10.327 回答