Notes C API & C++11+ & ReactiveX & Qt #4 ~ main.cppとアラインメント

前回に引き続き、以下のソースコードについて説明していきます。

github.com

前回のQMakeのプロジェクトファイルに関するトピックはまだまだ尽きません。言語的な要素も持っていて、ビルトインの変数、関数の他に、カスタム変数、関数を定義することもできます。Windows/Mac/Linuxや他のOS、組み込みデバイススマートフォンなどに対応するための仕組みもあります。もちろん、Qt独自の機能(例えば、Qt翻訳システムやリソースシステムなど)を定義するのもこのファイルです(今後もQMakeプロジェクトについては、関連するトピックを取り上げる際に触れていきます)。

これらQMakeとプロジェクトファイルについては、日本語で情報を紹介してくれている方々も多くいらっしゃいますが、やはり全体的には英語が主流です。QMakeの英語マニュアルで基礎から学びたい方で、私のように英語に自信のない方は、翻訳サイトをうまく活用してみてはいかがでしょうか。

それでは、C++ソースコードの説明に移ります。このv0.0.1では、ハードコードしたサーバ名でそのサーバと通信し、待ち時間とサーバのバージョンを取得して表示します。この機能は1つのAPI関数「NSFGetServerLatency」だけで実行することができます。

// main.cpp

#include <QCoreApplication>
#include <QTranslator>
#include <QLocale>
#include <QTextStream>

// Notes C APIのインクルードは、Windowsではアラインメントを1バイトにして読み込む。
#ifdef NT
#pragma pack(push, 1)
#endif

#include <global.h>
#include <nsfdb.h>

#ifdef NT
#pragma pack(pop)
#endif

// 待ち時間を読み取る場合はこのシグネチャを有効にする。
//#define GET_LATENCY_TIME

// 自身で到達させたいサーバ名を設定する。
const char *pServer = "Your/server/name";

int main(int argc, char *argv[])
{
  QCoreApplication app(argc, argv);

  QTranslator translator;
  translator.load(QLocale(), "ncl", ".", ":/translations", ".qm");
  app.installTranslator(&translator);

  // 標準出力
  QTextStream out(stdout, QIODevice::WriteOnly);

  // Notes C APIを初期化する。
  STATUS initStatus = NotesInitExtended(argc, argv);
  out << QObject::tr("NotesInitExtended status")
      << ": "
      << ERR(initStatus)
      << endl;

  // 戻り値格納変数を用意する。
  WORD serverVersion = 0;
#ifdef GET_LATENCY_TIME
  DWORD clientToServer_ms, serverToClient_ms;
#endif

  // API関数を実行する。
  STATUS status = NSFGetServerLatency(
        const_cast<char*>(pServer),
        0, // <- 既定のタイムアウト時間を指定
#ifdef GET_LATENCY_TIME
        &clientToServer_ms, &serverToClient_ms,
#else
        nullptr, nullptr,
#endif
        &serverVersion
        );
  out << QObject::tr("NSFGetServerLatency status")
      << ": "
      << ERR(status)
      << endl;

  // エラーがあれば終了する。
  if (ERR(status) != NOERROR) {
    return 1;
  }

  // 戻り値を標準出力に表示する。
  out << QObject::tr("Build version of '%1'").arg(pServer)
      << ": "
      << serverVersion
      << endl;
#ifdef GET_LATENCY_TIME
  out << QObject::tr("Latency time for client to server")
      << ": "
      << QString("%1 ms").arg(clientToServer_ms)
      << endl
      << QObject::tr("Latency time for server to client")
      << ": "
      << QString("%1 ms").arg(serverToClient_ms)
      << endl;
#endif

  // Notes C APIを終了する。
  if (ERR(initStatus) == NOERROR)
    NotesTerm();

  return 0;
}

ソースコードについて順を追って説明していきますが、まずは、以下のコードに注目してみます。

// 一部省略
#pragma pack(push, 1)
#include <global.h>
#include <nsfdb.h>
#pragma pack(pop)

インクルードしている「global.h」と「nsfdb.h」は、Notes C APIのヘッダファイルです。それを挟んでいるのは、Visual C++のプラグマディレクティブ(#pragma)です。後ろにつく「pack」というのは、構造体のアラインメントを変更する機能があります。「push,1」は「アラインメントを1バイトに変更しますよ」という意味で、「pop」は「元に戻しますね」という意味です。Visual C++のアラインメントは、既定値は8バイトなんですが、Notes C APIの構造体は一部を除いて1バイトのアラインメントになっています。Mac/Linuxではこのアラインメントの問題は起きません。マルチプラットフォームを意識した書き方をするのであれば、Mac/Linuxでは、このプラグマディレクティブはむしろ不要なので、以下のようにキーワード「NT」を元に判定します(NTはNotes C APIWindowsのみに定義する)。

#ifdef NT
#pragma pack(push, 1)
#endif

#include <global.h>
#include <nsfdb.h>

#ifdef NT
#pragma pack(pop)
#endif

ちなみに、Windowsでも例外的にアラインメントを既定値でインクルードするNotes C APIヘッダファイルがあります。私の経験上、「dsapi.h」だけはアラインメントを1バイトにしてはいけなくて、既定の8バイトのままにします。「dsapi.h」は、HTTPタスクのエクステンション用にインクルードするもので、これを1バイトでパックすると、ひどいエラーを引き起こしました。(続く)