none
構造体メンバのアライメント

    質問

  • お世話になります。

    MyApp(exe) アライメント:1
    OtherDLL(DLL) アライメント:8

    MyApp側で、OtherDLLに定義されているクラスのインスタンスを生成するのですが、
    そのインスタンスのメソッドをコールすると実行時エラーとなります。

    MyApp側の構造体メンバのアライメントを「8」にすることで、
    メソッドも正常に実行できるようになったのですが、
    MyApp側の構造体メンバのアライメントは、「1」である必要があり、
    OtherDLL側の構造体メンバ
    のアライメントを変更にするには、費用が掛かるといわれています。

    そこで、OtherDLL側から提供されたヘッダーファイルをインクルードしているところで、
    次のようにしたところ、メソッドは正常に起動されることが確認できました。

    MyAppSource.cpp

    #pragma push (8)
    #include otherdll.h"
    #pragma pop
    
    


    質問です。

    この場合、otherdll.h内に定義されているクラス、構造体のみが、
    構造体メンバのアライメントが「8」に変更されるという認識であっているでしょうか?
    それ以外は、プロジェクト設定で定義されている構造体メンバのアライメントの値(1)のままの認識であってますでしょうか?
    ※ただこれで問題ないなら、DLLを提供する側が、このオプションを実装したヘッダーファイルに記述してくれてもいいそうですが。。。

    また後学のための質問となりますが、ヘッダーファイルに記述するとしたら、次のように記述であっていますでしょうか?

    otherdll.h

    #pragma push (8)
    
    struct Abc{};
    class Ced{};
    
    #pragma pop

    また#pragma popが無い場合、次のようなインクルードをした場合、
    "myapp_data.h"で定義されている構造体クラスも、構造体メンバのアライメントは、「8」となってしまいますか?

    #include "otherdll.h"
    #include "myapp_data.h"

    ご教授ください。



    2018年2月7日 7:28

回答

  • #pragma push (8) と #pragma pop は Visual Studio では不明なプラグマとして認識されるようですので、それぞれ #pragma push (8) を #pragma pack(push, 8) と #pragma pop を #pragma pack(pop) と読み替えて回答いたします。
    この場合、otherdll.h内に定義されているクラス、構造体のみが、構造体メンバのアライメントが「8」に変更されるという認識であっているでしょうか?
    それ以外は、プロジェクト設定で定義されている構造体メンバのアライメントの値(1)のままの認識であってますでしょうか?

    基本的な認識はあっていると思います。ただ、otherdll.h 内で別のヘッダーを include している場合はそのヘッダーに定義されているのアライメントにも影響があり得ます。また、別のソースどこかで otherdll.h がアライメント設定なしで既にインクルードされしまっている場合は、アライメントの設定が効かないことは考えられます。

    また後学のための質問となりますが、ヘッダーファイルに記述するとしたら、次のように記述であっていますでしょうか?

    はい。あっています。

    また#pragma popが無い場合、次のようなインクルードをした場合、 "myapp_data.h"で定義されている構造体クラスも、構造体メンバのアライメントは、「8」となってしまいますか?
    はい。pragma で アライメントを 8 と設定した後、pop で戻されなかった場合は、それ以降 include 内の構造体、およびそれ以降のソースコードの構造体のアライメントも 8 となります。
    • 回答としてマーク Brillia 2018年2月8日 1:49
    2018年2月7日 8:34
  • 別の観点から・・・、

    1.コンパイラのアライメントの「規定値」は8です。何も指定しなかった場合は8になります。
    2.アライメントはビルド時に指定された値で算定され、以後は変更することはできません。
    3.同じ構造体を参照する場合には、同じアライメント値で参照しなければなりません。

    従って、

    >MyApp側の構造体メンバのアライメントは、「1」である必要があり、

    の場合は、自身の設定は「規定値」ではないので

    4.その規定値では無い構造体(群)だけアライメントを1にする。その他は「規定値」を採用する。

    が問題を起こしにくい設定の仕方だと断言できます。例えば

    #pragma pack( push, 1)
    typedef struct MyStruct{
      :
    }MyStruct;
    #pragma pack( pop)

    といった感じですね。
    このように普通の方法をとることにより、otherdll.hについてすべきことは何もなくなるわけです。

    • 回答としてマーク Brillia 2018年2月8日 1:49
    2018年2月7日 10:10
  • 構造体メンバのアライメントが「8」に変更されるという認識であっているでしょうか?
    それ以外は、プロジェクト設定で定義されている構造体メンバのアライメントの値(1)のままの認識であってますでしょうか?
    ※ただこれで問題ないなら、DLLを提供する側が、このオプションを実装したヘッダーファイルに記述してくれてもいいそうですが。。。

    みなさんも答えられていますが、合ってます。ただし、Visual C++のアラインメントはデフォルトで8byteです。混乱を避けるためにもMyApp全体でなく1byteを必要と構造体だけに対してアラインメントを変更することをお勧めします。

    なお、#pragma pack(push, 8) は従来からの書式ですが、Visual C++ 2015からC++11標準にある alignas(8) に対応しているため可能であればこちらの書式をお勧めします。
    ただし#includeファイルをまたいでの指定はできません。

    • 回答としてマーク Brillia 2018年2月8日 1:49
    2018年2月7日 13:35

すべての返信

  • #pragma push (8) と #pragma pop は Visual Studio では不明なプラグマとして認識されるようですので、それぞれ #pragma push (8) を #pragma pack(push, 8) と #pragma pop を #pragma pack(pop) と読み替えて回答いたします。
    この場合、otherdll.h内に定義されているクラス、構造体のみが、構造体メンバのアライメントが「8」に変更されるという認識であっているでしょうか?
    それ以外は、プロジェクト設定で定義されている構造体メンバのアライメントの値(1)のままの認識であってますでしょうか?

    基本的な認識はあっていると思います。ただ、otherdll.h 内で別のヘッダーを include している場合はそのヘッダーに定義されているのアライメントにも影響があり得ます。また、別のソースどこかで otherdll.h がアライメント設定なしで既にインクルードされしまっている場合は、アライメントの設定が効かないことは考えられます。

    また後学のための質問となりますが、ヘッダーファイルに記述するとしたら、次のように記述であっていますでしょうか?

    はい。あっています。

    また#pragma popが無い場合、次のようなインクルードをした場合、 "myapp_data.h"で定義されている構造体クラスも、構造体メンバのアライメントは、「8」となってしまいますか?
    はい。pragma で アライメントを 8 と設定した後、pop で戻されなかった場合は、それ以降 include 内の構造体、およびそれ以降のソースコードの構造体のアライメントも 8 となります。
    • 回答としてマーク Brillia 2018年2月8日 1:49
    2018年2月7日 8:34
  • 別の観点から・・・、

    1.コンパイラのアライメントの「規定値」は8です。何も指定しなかった場合は8になります。
    2.アライメントはビルド時に指定された値で算定され、以後は変更することはできません。
    3.同じ構造体を参照する場合には、同じアライメント値で参照しなければなりません。

    従って、

    >MyApp側の構造体メンバのアライメントは、「1」である必要があり、

    の場合は、自身の設定は「規定値」ではないので

    4.その規定値では無い構造体(群)だけアライメントを1にする。その他は「規定値」を採用する。

    が問題を起こしにくい設定の仕方だと断言できます。例えば

    #pragma pack( push, 1)
    typedef struct MyStruct{
      :
    }MyStruct;
    #pragma pack( pop)

    といった感じですね。
    このように普通の方法をとることにより、otherdll.hについてすべきことは何もなくなるわけです。

    • 回答としてマーク Brillia 2018年2月8日 1:49
    2018年2月7日 10:10
  • 構造体メンバのアライメントが「8」に変更されるという認識であっているでしょうか?
    それ以外は、プロジェクト設定で定義されている構造体メンバのアライメントの値(1)のままの認識であってますでしょうか?
    ※ただこれで問題ないなら、DLLを提供する側が、このオプションを実装したヘッダーファイルに記述してくれてもいいそうですが。。。

    みなさんも答えられていますが、合ってます。ただし、Visual C++のアラインメントはデフォルトで8byteです。混乱を避けるためにもMyApp全体でなく1byteを必要と構造体だけに対してアラインメントを変更することをお勧めします。

    なお、#pragma pack(push, 8) は従来からの書式ですが、Visual C++ 2015からC++11標準にある alignas(8) に対応しているため可能であればこちらの書式をお勧めします。
    ただし#includeファイルをまたいでの指定はできません。

    • 回答としてマーク Brillia 2018年2月8日 1:49
    2018年2月7日 13:35