見出し画像

Notes C API探訪: 式ハンドルの使用方法(その2)

前回、式ハンドルを使用するための関数について紹介しました。

今回は、これらを用いて@式を実行する様子をサンプルにまとめてみたので、参考にしてみて下さい。

@式を複数実行するエントリーポイントを呼び出す

まず最初に、テキストの@式をいくつか作成して、それを呼び出す関数を実装します。

#include <iostream>
#include "logger.h"
#include "ex_range.h"
#include "items.h"

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

#include <nsfsearc.h>
#include <osmem.h>
#include <textlist.h>

#ifdef NT
#pragma pack(pop)
#endif

void showEvaluateResults(
   char *pResult,
   WORD resultLen,
   BOOL noteMatchedFormula,
   BOOL noteShouldBeDeleted,
   BOOL noteModified
   );

void evaluateFormula(HCOMPUTE hCompute);

void computeFormula(void *pFormula);

void compileFormula(const char *text);

void nsfComputeEvaluate() {
 compileFormula("12.3 * 4.5");
 std::cout << std::endl;

 compileFormula("@Now");
 std::cout << std::endl;

 compileFormula("@Text(123 / 45)");
 std::cout << std::endl;
}

logger.h: この記事を参考
ex_range.h: 「Notes C API探訪: RANGE(データ型)」で連載した記事を参考
items.h: 後述
showEvaluateResults: 後述
evaluateFormula: 後述
computeFormula: 後述
compileFormula: 後述
nsfComputeEvaluate: テキスト形式の@式をcompileFormula関数に渡して、式の実行と結果の表示をしています。一つ式を実行するごとに一行空けています。

@式をコンパイルする

式をコンパイルして、成功したらcomputeFormula関数にロック済みメモリポインタを渡します。


void compileFormula(const char *text) {
 FORMULAHANDLE hFormula = NULLHANDLE;
 STATUS compileStatus = NOERROR;
 WORD wLen = 0, wLine = 0, wColumn = 0, wOffset = 0, wOffsetLen = 0;
 std::cout << "Formula is '" << text << "'." << std::endl;
 STATUS status = NSFFormulaCompile(
       nullptr, 0,
       text, static_cast<WORD>(strlen(text)),
       &hFormula,
       &wLen,
       &compileStatus,
       &wLine,
       &wColumn,
       &wOffset,
       &wOffsetLen);
 if (ERR(status) != NOERROR) {
   logger::printStatusMessage(status);
   logger::printStatusMessage(compileStatus);
   std::cout << "Line=" << wLine
             << ", Column=" << wColumn
             << ", Offset=" << wOffset
             << ", OffsetLen=" << wOffsetLen
             << "\n===> "
             << std::string(text).substr(wOffset, wOffsetLen)
             << std::endl;
   return;
 }
 computeFormula(OSLockObject(hFormula));
 OSUnlockObject(hFormula);
 OSMemFree(hFormula);
}

戻ってきたら、ハンドルをアンロックして、メモリを解放します。

計算ハンドルを得る

式ハンドルをロックしたポインタを使って、計算ハンドルを得ます。成功すればevaluateFormula関数に計算ハンドルを渡します。

void computeFormula(void *pFormula) {
 HCOMPUTE hCompute = nullptr;
 STATUS status = NSFComputeStart(0, pFormula, &hCompute);
 if (ERR(status) != NOERROR) {
   logger::printStatusMessage(status);
   return;
 }
 evaluateFormula(hCompute);
 status = NSFComputeStop(hCompute);
 if (ERR(status) != NOERROR) {
   logger::printStatusMessage(status);
 }
}

戻ってきたら、計算ハンドルの使用を停止します。

計算ハンドルを実行(評価)する

計算ハンドルを通して、式を実行します。成功すれば、showEvaluateResults関数に結果のハンドルをロックしたポインタを渡します。

void evaluateFormula(HCOMPUTE hCompute) {
 DHANDLE hResult = NULLHANDLE;
 WORD resultLen = 0;
 BOOL noteMatchesFormula = FALSE,
     noteShouldBeDeleted = FALSE,
     noteModified = FALSE;
 STATUS status = NSFComputeEvaluate(
       hCompute,
       NULLHANDLE,
       &hResult,
       &resultLen,
       &noteMatchesFormula,
       &noteShouldBeDeleted,
       &noteModified);
 if (ERR(status) != NOERROR) {
   logger::printStatusMessage(status);
   return;
 }
 showEvaluateResults(
       static_cast<char*>(OSLockObject(hResult)),
       resultLen,
       noteMatchesFormula,
       noteShouldBeDeleted,
       noteModified);
 OSUnlockObject(hResult);
 OSMemFree(hResult);
}

戻ってきたら結果のハンドルをアンロックし、解放します。

結果を表示する

@式の実行結果は、メモリハンドルという形で返ってきます。メモリハンドルが確保している領域には、WORD型の値(データタイププレフィックス)とデータ型ごとのバイナリデータが続きます。items::toStringList関数にメモリハンドルを渡して、文字列配列で受け取り、標準出力に表示します。

void showEvaluateResults(
   char *pResult,
   WORD resultLen,
   BOOL noteMatchesFormula,
   BOOL noteShouldBeDeleted,
   BOOL noteModified
   ) {
 WORD prefix = *reinterpret_cast<WORD*>(pResult);
 std::cout << "prefix: " << std::hex << prefix
           << "\nresult length: " << std::dec << resultLen
           << "\nmatches formula: " << noteMatchesFormula
           << "\nshould be deleted: " << noteShouldBeDeleted
           << "\nmodified: " << noteModified
           << std::endl;
 std::vector<std::string> list = items::toStringList(pResult);
 for (std::string item : list) {
   std::cout << "Result: " << item << std::endl;
 }
}

items::toStringList: item.hに配置した関数です(後述)。

データをパースする

データタイププレフィックス付きのデータは、ざっくりと捉えればNotes C APIの「Variant型」や「Any型」的な存在です。Notes/Dominoでは、どんなデータ型でもフィールドデータとして保存可能ですが、それができるのはデータタイププレフィックスのおかげです。
items::toStringList関数は、データタイププレフィックスをチェックして、それぞれの型に合った方法でデータをパースし、文字列配列(std::vector<std::string>)という形で共通化して返します。@式が返す結果のデータ型は、単数でもすべてリスト、複数値の形をとっているので、ここではテキストリスト、数値リスト、日時リストについて対応します。なお、LMBCS文字列については考慮しません。

// items.h
#ifndef ITEMS_H
#define ITEMS_H

#include <vector>
#include <string>

namespace items {

std::vector<std::string> toStringList(void *pItem);

} // namespace items

#endif // ITEMS_H

// items.cpp
#include "items.h"
#include "ex_range.h"

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

#include <global.h>
#include <textlist.h>
#include <misc.h>

#ifdef NT
#pragma pack(pop)
#endif

namespace items {

std::vector<std::string> toStringList(void *pItem) {
 std::vector<std::string> list;
 WORD prefix = *reinterpret_cast<WORD*>(pItem);
 switch (prefix) {

 case TYPE_NUMBER_RANGE:
 {
   int count = static_cast<int>(
         RangeGetNumItem<NumberRangeTraits>(pItem, TRUE)
         );
   for (int i = 0; i < count; ++i) {
     NUMBER number = 0;
     RangeGetItem<NumberRangeTraits>(pItem, TRUE, i, &number);
     list.push_back(std::to_string(number));
   }
 }
   break;

 case TYPE_TIME_RANGE:
 {
   int count = static_cast<int>(
         RangeGetNumItem<TimeDateRangeTraits>(pItem, TRUE)
         );
   for (int i = 0; i < count; ++i) {
     TIMEDATE td;
     RangeGetItem<TimeDateRangeTraits>(pItem, TRUE, i, &td);
     char buffer[MAXALPHATIMEDATE + 1] = "";
     WORD len = 0;
     ConvertTIMEDATEToText(
           nullptr,
           nullptr,
           &td,
           buffer,
           MAXALPHATIMEDATE,
           &len
           );
     list.push_back(std::string(buffer, len));
   }
 }
   break;

 case TYPE_TEXT_LIST:
 {
   int count = static_cast<int>(ListGetNumEntries(pItem, TRUE));
   for (int i = 0; i < count; ++i) {
     char *pText = nullptr;
     WORD textLen = 0;
     ListGetText(pItem, TRUE, i, &pText, &textLen);
     list.push_back(std::string(pText, textLen));
   }
 }
   break;
 }
 return list;
}

}

実行結果

Formula is '12.3 * 4.5'.
prefix: 301
result length: 14
matches formula: 1
should be deleted: 0
modified: 0
Result: 55.350000

Formula is '@Now'.
prefix: 401
result length: 14
matches formula: 0
should be deleted: 0
modified: 0
Result: 2021/07/31 08:42:23

Formula is '@Text(123 / 45)'.
prefix: 501
result length: 22
matches formula: 0
should be deleted: 0
modified: 0
Result: 2.73333333333333

最初は数値計算です。結果の型は0x301で、TYPE_NUMBER_RANGE型を表します。
2つ目は現在の時間を取得しています。結果の型は0x401で、TYPE_TIME_RANGEを表します。
最後は数値計算したものを@Textでテキストに変換しています。結果の型は0x501で、TYPE_TEXT_LISTを表します。

まとめ

今回は、@式をNotes C APIで実際に使っている様子を、サンプルコードという形でご覧いただきました。コンパイルしてから結果を得るまで、少々複雑な工程ですが、関数やクラスにきちんとまとめれば、手軽に使えるようになるでしょう。

注意: コードの利用においてチブル・システムズは一切の責任を負いません。自己責任でご利用をお願いいたします。

この記事が気に入ったらサポートをしてみませんか?