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インスタンスでデストラクタを呼び出すだけですが、大きい値型のstatic_deleteは、「delete」を使用します。

トリックはありますか?前もって感謝します。

4

3 に答える 3

2

内部文書には何と記載されていますか? 作成者がそれを文書化していない場合、彼はおそらく自分自身を知りません。

コードから判断すると、小さなオブジェクトへのインターフェイスは大きなオブジェクトへのインターフェイスとは異なります。小さなオブジェクトに渡すポインターはオブジェクト自体へのポインターですが、大きなオブジェクトに渡すポインターはオブジェクトへのポインターへのポインターです。

ただし、作成者は C++ をよく知らないようです (このようなコードを使用することは避けたいと思います)。たとえば、 ではmove、彼は明示的にオブジェクトを破棄してから代入します。これは未定義の動作が保証されており、おそらく最も単純な組み込み型以外では確実に機能しません。また、小さなオブジェクトと大きなオブジェクトの区別もほとんど意味がありません。一部の「小さな」オブジェクトは、コピーするのに非常にコストがかかる場合があります。そしてもちろん、ここにあるものはすべてテンプレートであるため、何かに使用する理由はまったくありませんvoid*

于 2011-10-31T15:16:50.580 に答える
2

Firebreath は小さなオブジェクトに専用のメモリ プールを使用しているように見えますが、大きなオブジェクトは通常ヒープに割り当てられます。したがって、異なる動作。たとえば、小さなオブジェクトの配置newに注意してください。これにより、割り当てずに、指定されたメモリ位置に新しいオブジェクトが作成されます。clone()配置を使用してオブジェクトを作成する場合new、メモリの割り当てを解除する前に、デストラクタを明示的に呼び出す必要がありますstatic_delete()

私が言うように、専用のメモリプールが使用されているように見えるため、メモリは実際には割り当て解除されていません。メモリ管理は別の場所で実行する必要があります。この種のメモリ プールは、小さなオブジェクトの一般的な最適化です。

于 2011-10-31T15:10:56.527 に答える
1

元のソース ファイルへのリンクを含めるように質問を編集しました。これは、おそらく FireBreath で最も紛らわしいコードの 1 つであることは認めます。当時、私はブーストの使用を避けようとしていましたが、これは非常にうまく機能しました.

それ以来、私は boost::any に切り替えることを検討しました (それを提案したい人のために、いいえ、boost::variant は機能しません。ここで理由を説明するつもりはありません。本当に気にする場合は別の質問をしてください)。このクラスをかなりの量カスタマイズして、必要なものを正確に作成しましたが、boost::any を同様の方法でカスタマイズするのは困難です。何よりも、私たちは古い原則に従っています。壊れていない場合は、修正しないでください。

まず第一に、何人かの C++ 専門家がこのコードを調べたことを知っておく必要があります。はい、多くの人が疑わしいと考えるいくつかのプラクティスを使用していますが、それらは非常に慎重に検討されており、FireBreath がサポートするコンパイラで一貫性があり信頼性があります。valgrind、ビジュアル リーク ディテクター、LeakFinder、および Rational Purify を使用して広範なテストを実施しましたが、このコードにリークは見つかりませんでした。少し混乱します。コードを理解していない人が、作成者が C++ を知らないと想定していることは、私にとって驚くべきことです。この場合、Christopher Diggins (あなたが引用したコードと元の cdiggins::any クラスを書いた人) は、このコードを書くことができたという事実によって証明されるように、C++ を非常によく知っているようです。このコードは内部で使用され、高度に最適化されています。実際、おそらく FireBreath が必要とする以上のものです。

私が覚えている限り、あなたの質問に対する答えを説明しようと思います。あまり時間がないことを覚えておいてください。これについて深く掘り下げてからしばらく経ちました。異なる静的クラスを使用する「小さい」型の主な理由は、「小さい」型がほとんど組み込み型であるためです。int、char、long など。void* より大きいものは、何らかのオブジェクトであると見なされます。これは、メモリを削除して再割り当てするのではなく、可能な限りメモリを再利用できるようにするための最適化です。

コードを並べて見ると、より明確になります。delete と clone を見ると、「大きな」オブジェクトではメモリが動的に割り当てられていることがわかります。削除では「削除」を呼び出し、クローンでは通常の「新規」を使用します。「小さい」バージョンでは、メモリを内部に保存して再利用するだけです。メモリを「削除」することはなく、内部にあるメモリ上の正しいタイプのデストラクタまたはコンストラクタを呼び出すだけです。繰り返しますが、これは効率のために行われます。両方のタイプの移動では、古いオブジェクトのデストラクタを呼び出してから、新しいオブジェクト データを割り当てます。

オブジェクト自体は void* として格納されます。これは、オブジェクトがどのような型になるか実際にはわからないためです。実際、オブジェクトを元に戻すには、タイプを指定する必要があります。これは、コンテナが絶対にあらゆるタイプのデータを保持できるようにするための一部です。これが、そこに非常に多くの reinterpret_cast 呼び出しがある理由です。多くの人がそれを見て、「ああ、いや、作者は無知に違いない!」と言います。ただし、逆参照する必要がある void* がある場合は、まさにその演算子を使用します。

とはいえ、cdiggins は今年、彼の any クラスの新しいバージョンを発表しました。私はそれを見てみる必要があり、おそらく現在のものを置き換えるためにそれを引き込もうとします. 秘訣は、現在のものをカスタマイズしたことです (主に、比較演算子を追加して STL コンテナーに配置できるようにするためと、convert_cast を追加するためです)。そのため、新しいバージョンを安全に行うために十分に理解していることを確認する必要があります。

お役に立てば幸いです。私が入手した記事はここにあります:http://www.codeproject.com/KB/cpp/dynamic_typing.aspx

記事は更新されており、元の記事で古い記事にアクセスすることはできなくなっているようです。

編集

これを書いて以来、古いバリアント クラスにいくつかの問題があることが確認されており、更新され、boost::any を利用するクラスに置き換えられました。これに関するほとんどの作業について dougma に感謝します。FireBreath 1.7 (この記事の執筆時点での現在の master ブランチ) には、その修正が含まれています。

于 2011-10-31T16:34:10.327 に答える