NotesとQtでWindows、Mac OS X、Ubuntuのデスクトップアプリ(その4 - LMBCS概略編)
次はLMBCSです。
LMBCS(リンビックス、Lotus Multi-Byte Character Set)はNotes固有の文字コードセットで、STATUSの次にラッパークラスを用意したい概念です。 日本語以外の知識を持ち合わせていないのですが、日本語のみで考えればLMBCSの日本語はシフトJISがベースになっています。これは、Windows版に限らず、Max OS Xでも、Linux版でも同様だそうです。
シフトJISは、ざっくりと半角英数字、半角カナ文字、全角文字の3つに分類されます。半角文字は1バイト、全角文字は2バイトです。LMBCSは、半角英数字はそのままで1バイト、全角文字には文字コード¥x10を頭に付けて3バイト、半角カナ文字には文字コード¥x10を2つ頭に付けて3バイトの形式を取ります。
シフトJIS | バイト数 | LMBCS | バイト数 |
---|---|---|---|
ABCDE | 5 | ABCDE | 5 |
あいうえお | 10 | ¥x10あ¥x10い¥x10う¥x10え¥x10お | 15 |
アイウエオ | 5 | ¥x10¥x10ア¥x10¥x10イ¥x10¥x10ウ¥x10¥x10エ¥x10¥x10オ | 15 |
ただし、これらをユーザがいちいち変換する必要はありません。Notes APIにOSTranslateという関数があります。Notes/Domino APIプログラミング―C++とSTLによる実践的プログラミングでは、std::stringを用いてLMBCSを表現し、シフトJISとLMBCSを相互に変換する関数を紹介しています。
OSTranslateは次のような仕様になっています。
<osmisc.h> WORD LNPUBLIC OSTranslate(WORD TranslateMode , const char far *In, WORD InLength , char far *Out, WORD OutLength);
引数TranslateModeは定数で、どの文字コードからどの文字コードに変換するかが決まった定数を割り当てます。
<osmisc.h> /* Charsets used with OSTranslate */ #define OS_TRANSLATE_NATIVE_TO_LMBCS 0 /* Translate platform-specific to LMBCS */ #define OS_TRANSLATE_LMBCS_TO_NATIVE 1 /* Translate LMBCS to platform-specific */ #define OS_TRANSLATE_LOWER_TO_UPPER 3 /* current int'l case table */ #define OS_TRANSLATE_UPPER_TO_LOWER 4 /* current int'l case table */ #define OS_TRANSLATE_UNACCENT 5 /* int'l unaccenting table */ #ifdef DOS #define OS_TRANSLATE_OSNATIVE_TO_LMBCS 7 /* Used in DOS (codepage) */ #define OS_TRANSLATE_LMBCS_TO_OSNATIVE 8 /* Used in DOS */ #elif defined (OS2) #define OS_TRANSLATE_OSNATIVE_TO_LMBCS OS_TRANSLATE_NATIVE_TO_LMBCS #define OS_TRANSLATE_LMBCS_TO_OSNATIVE OS_TRANSLATE_LMBCS_TO_NATIVE #else #define OS_TRANSLATE_OSNATIVE_TO_LMBCS OS_TRANSLATE_NATIVE_TO_LMBCS #define OS_TRANSLATE_LMBCS_TO_OSNATIVE OS_TRANSLATE_LMBCS_TO_NATIVE #endif #if defined(DOS) || defined(OS2) #define OS_TRANSLATE_LMBCS_TO_ASCII 13 #else #define OS_TRANSLATE_LMBCS_TO_ASCII 11 #endif #define OS_TRANSLATE_LMBCS_TO_UNICODE 20 #define OS_TRANSLATE_LMBCS_TO_UTF8 22 #define OS_TRANSLATE_UNICODE_TO_LMBCS 23 #define OS_TRANSLATE_UTF8_TO_LMBCS 24
「NATIVE」というのが、日本語環境ではシフトJISにあたります。
Inには変換元の文字列、InLengthにはその長さ(バイト単位)、Outには変換後の文字列を格納する領域、OutLengthにはその領域の長さ(バイト単位)を指定します。戻り値は、変換後の文字列の実際の長さ(バイト単位)になります。
注意したいのは、入出力ともにWORD=64kバイト分の文字列しか処理できないことです。これは、最新のAPIキットでも変わっていません。
最近では、OSレベルでUnicodeがサポートされ、Qtにおいても文字コードはUnicodeが標準です。幸いなことに、OSTranslate関数はNATIVE以外にUNICODE、UTF8も変換対象になっています。
Qtにおいて文字はUnicode(UTF-16)表現のQCharクラス、文字列表現はQCharの配列、QStringクラスを使います。Unicode以外の文字コードを扱う場合は、QByteArrayが使われます。
そこで、ここではLMBCSをQByteArrayの派生クラスとして定義し、QStringと相互に変換できるようにします。(続く)
NotesとQtでWindows、Mac OS X、Ubuntuのデスクトップアプリ(その3 - STATUS編)
Notes C APIにおけるSTATUS型は、ほとんどの関数の戻り値となっています。関数の実行結果が成功したのか、失敗したのか、判断する材料となります。
「Notes/Domino APIプログラミング〜」では、std::exceptionを拡張した例外処理として実装しています。私も以前はこれを利用していましたが、Visual C++で例外の仕様が変更され、サンプルをコンパイルすると警告が出るようになりました。それを機に、C++における例外処理について調べてみると、処理の組み方によっては思わぬ処理フローを引き起こす場合があるようで、Notes APIを扱う場合は不向きではないかと考えるようになりました。そこで、STATUS値をラッピングするクラスは、単なるラッパークラスにすることにしました。以下はStatusクラスのヘッダーファイルです。
status.h
#ifndef NTLX_STATUS_H #define NTLX_STATUS_H #if defined(NT) #pragma pack(push, 1) #endif #include <global.h>#if defined(NT) #pragma pack(pop) #endif namespace ntlx { /** * @brief The Status class */ class Status { public: /** * @brief コンストラクタ */ Status() {} /** * @brief コンストラクタ * @param value ステータス値 */ Status(STATUS value) : value_(value) {} /** * @brief コピーコンストラクタ * @param other コピー元 */ Status(const Status& other) : value_(other.value_) {} /** * @brief 代入演算子 * @param other 代入元 * @return 自身への参照 */ Status& operator=(const Status& other) { if (this == &other) return *this; value_ = other.value_; return *this; } /** * @brief キャスト演算子 */ operator STATUS() const { return value_; } /** * @brief エラー値 * @return 上位2ピットをマスクしたエラー値 */ STATUS error() const { return ERR(value_); } /** * @brief エラーがないと真を返す * @return エラーがなければ真 */ bool success() const { return error() == NOERROR; } /** * @brief エラーがあると真を返す * @return エラーがあれば真 */ bool failure() const { return !success(); } /** * @brief すでに表示されていれば真を返す * @return 表示済であれば真 */ bool hasDisplayed() const { return ((value_ & STS_DISPLAYED) != 0); } /** * @brief リモート(Dominoサーバ)起因のエラーであれば真を返す * @return リモート起因なら真 */ bool isRemote() const { return ((value_ & STS_REMOTE) != 0); } private: STATUS value_; }; } #endif // NTLX_STATUS_H
マルチプラットフォームとして記述するので、Notes APIのヘッダーファイルをインクルードする場合、Windowsにおいては、構造体のアラインメントに配慮する必要があります。最初の方に書かれている#pragma pack(push,1)と#pragma pack(pop)はそのための記述で、これらでAPIヘッダーのインクルード行を挟みます。なお、MacとLinuxでは、構造体のアラインメントを考慮する必要はないそうです。
また、WindowsにおいてはWIN32という識別子が定義されるので、これを#ifに使うこともできますが、実はLinuxもこの識別子が必要になります。なので、識別子NTの方が判別しやすいでしょう。(続く)
NotesとQtでWindows、Mac OS X、Ubuntuのデスクトップアプリ(その2 - 共有ライブラリ・作成編)
これからの操作は、基本的にMac OS Xを中心に話を進めます。WindowsとUbuntuについては、そのたびに補足していきます。
Qt Creatorで、共有ライブラリを作成します。
選択ボタンをクリックすると、次に進みます。
(画像のエラーはサンプルなので気になさらず)
プロジェクト名はここではntlxとしました。Notes/DominoのAPIについて日本語で書かれた下記の名著に掲載されているライブラリ「NTL」にちなんで付けました。
Notes/Domino APIプログラミング―C++とSTLによる実践的プログラミング
もちろん、この本やサンプルから多大なる影響を受けています。津田様、この場をお借りして感謝申し上げます。
あとは、使用するQtのバージョンやコンパイラ、使用するQtライブラリ(QtCoreだけで構いません)、初期作成するクラス(Statusとします)、ソースコード管理(Gitなど)を選択すれば、ひな形ができあがります。(続く)
NotesとQtでWindows、Mac OS X、Ubuntuのデスクトップアプリ(その1 - 環境編)
Notes/Dominoは元々クロスプラットフォームなグループウェアです。
QtはC++ベースのGUIアプリケーション開発フレームワークで、Windows、Mac、Linuxなどのプラットフォームで使用できます。
ここでは、この2つを組み合わせてみようという話です。
環境をまとめます。
Notesは最新の9.0.1を使います。
Qtは、WindowsとMacは5.6、Ubuntuは5.5を使います。
Ubuntuが5.5なのはOSが32bitで、Notesの動作環境としてUbuntu 64bitが含まれないためです。
Windowsは10 64bit、MacはSierra(10.12)、Ubuntuは12.10 32bitです。
コンパイラは、Visual Studioは2013、Xcodeは8.2、gcc/g++は4.6.3です。
これらの環境の元、できる限り1つのコードでW-M-Lのいずれでも動作するアプリを作ってみます。
今回目標にするのは、NotesのC API Toolkitに含まれるサンプルプログラムのエントリーとなる「basic/intro」「basic/intrwin」 のようなアプリを、Qtベースで作ってみます。この2つは、ユーザから受け取ったサーバ名とパスにあるデータベースから、そのタイトル文字列を取得して返すというものです。
ここで作成するNotes APIに関する実装は、いずれライブラリとして共有していきたいので、メイン+共有ライブラリという構成で構築します。
では最初に、Notes C APIにQtをラッピングして、Qt内で使いやすいNotesクラスライブラリを作ってみます。(続く)