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,
¬eMatchesFormula,
¬eShouldBeDeleted,
¬eModified);
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で実際に使っている様子を、サンプルコードという形でご覧いただきました。コンパイルしてから結果を得るまで、少々複雑な工程ですが、関数やクラスにきちんとまとめれば、手軽に使えるようになるでしょう。
注意: コードの利用においてチブル・システムズは一切の責任を負いません。自己責任でご利用をお願いいたします。