C++でJavaの静的初期化ブロックをシミュレートする【翻訳】

5162 ワード

静的初期化ブロックとは?
Javaには、静的初期化ブロックと呼ばれる構造がある.静的初期化ブロックは、Javaの最初の実行時にロードされます.たとえば、次のコードクリップを考慮します.
class Foo {
    static {
        // initialization code goes here
        // called only once, when the class is loaded by the runtime
        System.out.println("I'm the static initialization block of Foo
"); } public Foo() { System.out.println("I'm the constructor of Foo
"); } public static void Main(String[] args) { Foo foo1 = new Foo(); Foo foo2 = new Foo(); } }

これにより、次のような出力が得られます.
I'm the static initialization block of Foo
I'm the constructor of Foo
I'm the constructor of Foo

この構造は様々な場合に有用である.Javaでは、クラスの静的メンバーのマルチロー初期化を許可する例が期待されます.また、スクリプトバインドなどのログ・レコードや任意の種類の登録コードやサブスクリプション・コードにも使用できます.
Javaの静的初期化ブロックの詳細については、この記事を参照してください.http://www.jianshu.com/p/e10871b7cbd4
C++はこのような構造方法に欠けているが,巧みに設計することで,そのいくつかの態様の特性を模倣することができる.
シミュレーションできるものは何ですか?
まず,C++でライブラリを実行するようなものはなく,少なくともJavaの意味でのライブラリではない.これは、上記の定義がC++で完全にコピーできないことを意味します.そのため、ターゲットは次の構造です.
  • 最小限のテンプレート
  • を使用
  • わかりやすい(つまり文法は簡単)
  • は静的関数としてクラスの範囲内で
  • を実行する.
  • は決定点でコードを実行し、mainの開始
  • であることが望ましい.
    構文
    上記の要件(およびいくつかの制限)から、静的初期化ブロックの予想構文を以下に示す.
    In foo.h:
    #pragma once
    #include "static_init.h"
    
    class Foo
    {
    public:
        Foo();
        DECLARE_STATIC_INIT(Foo);
    };
    

    In foo.cc:
    #include 
    #include "foo.h"
    
    Foo::Foo()
    {
        std::cout << "I'm the constructor of Foon";
    }
    
    STATIC_INIT(Foo)
    {
        std::cout << "I'm the static initialization block of Foon";
    }
    

    In main.cc:
    #include 
    #include "static_init.h"
    #include "foo.h"
    
    int main()
    {
        static_init::execute();
        Foo foo1, foo2;
    }
    

    このコードは,上記のJavaコードフラグメントと同じ出力を生成することを期待する.
    上記の考えを実現する.
    ではstatic_に入りますinit.hは?主なコードから,実装の一部が何らかのレジストリであることが推測される.私たちは間違っていません.それはこのように見えます.
    typedef void (*init_func_type)();
    
    class static_init
    {
    public:
        static static_init& instance()
        {
            static static_init inst;
            return inst;
        }
    
        void add_init_func(init_func_type f) { funcs_.push_back(f); }
    
        static void execute()
        {
            auto& inst = instance();
            for (auto& c : inst.funcs_) c();
        }
    
    private:
        static_init() {}
    
        std::vector funcs_;
    };
    

    static_Initはvoid関数を管理および実行する単一のクラスです.init_func_typeは、パラメータを持たない関数を指す関数ポインタのtypedefです.静的メンバー関数を指すために使用されます.std::functionはここでも作業できますが、効率は低いです.実行メンバー関数は静的に設計されており,呼び出しの面で便利であるため,他の意図はない.
    add_init_funcはどこで呼び出されますか?コツは、静的メンバーのコンストラクション関数で呼び出すことです.このコンストラクション関数はinit関数をパラメータとしてadd_に渡すinit_func. 理論的には、そこで関数を呼び出すことができます(「init関数レジストリ」は必要ありません).しかし、これにより、私たちのコードは「静的初期化順序の失敗」の影響を受けます.私たちはライブラリのお客様に制限を加えたくないので、この方法を採用しています.
    登録コードにこっそり導入された静的メンバーには、アシスタントクラスを定義する必要があります.これは非常に簡単です.
    class static_init_helper
    {
    public:
       static_init_helper(init_func_type f)
       {
           static_init::instance().add_init_func(f);
       }
    };
    

    この点では、私たちに必要なすべての機能があります.Foo.hには次のようなコードがあります.
    #pragma once
    #include "static_init.h"
    
    class Foo
    {
    public:
        Foo();
        static void static_init_func();
        static static_init_helper Foo_static_init_helper;
    };
    

    foo.ccで:
    #include 
    #include "foo.h"
    
    Foo::Foo()
    {
        std::cout << "I'm the constructor of Foon";
    }
    
    // This is where the registration code (i.e. the constructor of the helper class) gets called:
    static_init_helper Foo::Foo_static_init_helper(&Foo::static_init_func);
    
    // And this is the implementation of the static init function,
    // an actual static member function of the class.
    void Foo::static_init_func()
    {
        std::cout << "I'm the static initialization block of Foon";
    }
    

    正常に動作しますが、少しもきれいで優雅ではありません.
    もっときれいにして
    上記のコードを意図的に手配することで、マクロがどれだけ役に立つかを簡単に見ることができます.宣言セクションは非常に簡単です.
    #define DECLARE_STATIC_INIT(ClassName)
       static void static_init_func();
       static static_init_helper ClassName##_static_init_helper
    

    最後にセミコロンが欠けていることに注意してください.これは作者の個人的な好みにすぎず、私たちは自分の習慣に基づいて追加することができます.
    同様に、実装部分:
    #define STATIC_INIT(ClassName)
       static_init_helper ClassName::ClassName##_static_init_helper(&ClassName::static_init_func);
       void ClassName::static_init_func()
    

    ただし、ここでは、マクロが書き込むメンバー関数の署名で終了するため、最後にセミコロンを追加することはありません.これが、マクロが関数定義の一部として機能することを許可する理由です.
    欠点
    静的初期化関数は、「静的初期化順序の失敗」の影響を直接受けない(すなわち、任意のクラスの静的メンバーが完全に構築されることを期待できる)が、定義されていない順序と呼ばれるため、相互に依存しない可能性があるという問題がある.
    このソリューションはC++の分野で少し見慣れない概念を導入した.これは、プログラマがコードを読むとオーバーヘッドが増加することを意味します.しかし、正しいコードで命名すると、私たちの意図は簡単に推測できるはずです.
    また、クラスにデータ・メンバーを追加します.このデータ・メンバーは0サイズですが、正しくリンクするにはソース・ファイルに存在する必要があります.これは静的init関数がinlineであるはずがないことを意味する.
    ソースコード
    本明細書のソースコード(およびいくつかのサンプルコード)は、Github repositoryで参照できます.
    本文は文章から訳す:
    http://szelei.me/cpp-static-init-block/
    原文の作者に感謝します