見出し画像

Notes C API探訪: INTLFORMATのラップ

INTLFORMATについては、以前の記事で紹介しました。

今回は、これをクラスラッピングします。

// nxpp/include/nxpp/nxpp_intl.hpp
#include <algorithm>

#ifndef NXPP_INTL_HPP
#define NXPP_INTL_HPP

#include "./lmbcs/nxpp_lmbcs.hpp"

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

#include <global.h>
#include <intl.h>

#ifdef NT
#pragma pack(pop)
#endif

namespace nxpp::intl {

/**
* @brief i18n設定クラス
*/
class Settings {
 INTLFORMAT value_;

public:
 /**
  * @brief デフォルトコンストラクタ
  */
 Settings() : value_(getCurrent()) {}

 /**
  * @return INTLFORMATへのポインタ
  */
 INTLFORMAT *data() { return &value_; }

 /**
  * @brief フラグ値から該当フラグの真偽を調べる
  * @param F フラグ
  * @return フラグが真ならtrue
  */
 bool isFlags(WORD F) const {
   return value_.Flags & F;
 }

 /**
  * @brief フラグ値の真偽を設定する
  * @param F フラグ
  * @param f 真にするならtrue
  */
 void setFlags(WORD F, bool f) {
   if (f) { value_.Flags |= F; }
   else { value_.Flags &= (~F); }
 }

 // フラグの真偽取得
 bool isCurrencySuffix() const { return isFlags(CURRENCY_SUFFIX); }
 bool isCurrencySpace() const { return isFlags(CURRENCY_SPACE); }
 bool isNumberLeadingZero() const { return isFlags(NUMBER_LEADING_ZERO); }
 bool isClock24Hour() const { return isFlags(CLOCK_24_HOUR); }
 bool isDaylightSavings() const { return isFlags(DAYLIGHT_SAVINGS); }
 bool isDateMDY() const { return isFlags(DATE_MDY); }
 bool isDateDMY() const { return isFlags(DATE_DMY); }
 bool isDateYMD() const { return isFlags(DATE_YMD); }
 bool isDate4DigitYear() const { return isFlags(DATE_4DIGIT_YEAR); }
 bool isTimeAMPMPrefix() const { return isFlags(TIME_AMPM_PREFIX); }
 bool isDateAbbrev() const { return isFlags(DATE_ABBREV); }

 // フラグの真偽設定
 void setCurrencySuffix(bool f) { setFlags(CURRENCY_SUFFIX, f); }
 void setCurrencySpace(bool f) { setFlags(CURRENCY_SPACE, f); }
 void setNumberLeadingZero(bool f) { setFlags(NUMBER_LEADING_ZERO, f); }
 void setClock24Hour(bool f) { setFlags(CLOCK_24_HOUR, f); }
 void setDaylightSavings(bool f) { setFlags(DAYLIGHT_SAVINGS, f); }
 void setDateMDY(bool f) { setFlags(DATE_MDY, f); }
 void setDateDMY(bool f) { setFlags(DATE_DMY, f); }
 void setDateYMD(bool f) { setFlags(DATE_YMD, f); }
 void setDate4DigitYear(bool f) { setFlags(DATE_4DIGIT_YEAR, f); }
 void setTimeAMPMPrefix(bool f) { setFlags(TIME_AMPM_PREFIX, f); }
 void setDateAbbrev(bool f) { setFlags(DATE_ABBREV, f); }

 /**
  * @return 通貨の小数点桁数
  */
 BYTE currencyDigits() const { return value_.CurrencyDigits; }

 /**
  * @param c 通貨の小数点桁数
  */
 void setCurrencyDigits(BYTE c) { value_.CurrencyDigits = c; }

 /**
  * @return 構造体長
  */
 BYTE length() const { return value_.Length; }

 /**
  * @param l 構造体長
  */
 void setLength(BYTE l) { value_.Length = l; }

 /**
  * @return 時差(日本UTC+9なら-9)
  */
 int timeZone() const { return value_.TimeZone; }

 /**
  * @param tz 時差(日本UTC+9なら-9)
  */
 void setTimeZone(int tz) { value_.TimeZone = tz; }

 /**
  * @return 午前表記文字列
  */
 Lmbcs amString() const {
   return getString<ISTRMAX>(value_.AMString);
 }

 /**
  * @return 午後表記文字列
  */
 Lmbcs pmString() const {
   return getString<ISTRMAX>(value_.PMString);
 }

 /**
  * @return 通貨記号文字列
  */
 Lmbcs currencyString() const {
   return getString<ISTRMAX>(value_.CurrencyString);
 }

 /**
  * @return 3桁区切り文字列
  */
 Lmbcs thousandString() const {
   return getString<ISTRMAX>(value_.ThousandString);
 }

 /**
  * @return 小数点文字列
  */
 Lmbcs decimalString() const {
   return getString<ISTRMAX>(value_.DecimalString);
 }

 /**
  * @return 日付区切り文字列
  */
 Lmbcs dateString() const {
   return getString<ISTRMAX>(value_.DateString);
 }

 /**
  * @return 時間区切り文字列
  */
 Lmbcs timeString() const {
   return getString<ISTRMAX>(value_.TimeString);
 }

 /**
  * @return 「昨日」文字列
  */
 Lmbcs yesterdayString() const {
   return getString<YTSTRMAX>(value_.YesterdayString);
 }

 /**
  * @return 「今日」文字列
  */
 Lmbcs todayString() const {
   return getString<YTSTRMAX>(value_.TodayString);
 }

 /**
  * @return 「明日」文字列
  */
 Lmbcs tomorrowString() const {
   return getString<YTSTRMAX>(value_.TomorrowString);
 }

 /**
  * @param src 午前表記文字列
  */
 void setAmString(const Lmbcs &src) {
   setString<ISTRMAX>(value_.AMString, src);
 }

 /**
  * @param src 午後表記文字列
  */
 void setPmString(const Lmbcs &src) {
   setString<ISTRMAX>(value_.PMString, src);
 }

 /**
  * @param src 通貨記号文字列
  */
 void setCurrencyString(const Lmbcs &src) {
   setString<ISTRMAX>(value_.CurrencyString, src);
 }

 /**
  * @param src 3桁区切り文字列
  */
 void setThousandString(const Lmbcs &src) {
   setString<ISTRMAX>(value_.ThousandString, src);
 }

 /**
  * @param src 小数点文字列
  */
 void setDecimalString(const Lmbcs &src) {
   setString<ISTRMAX>(value_.DecimalString, src);
 }

 /**
  * @param src 日付区切り文字列
  */
 void setDateString(const Lmbcs &src) {
   setString<ISTRMAX>(value_.DateString, src);
 }

 /**
  * @param src 時間区切り文字列
  */
 void setTimeString(const Lmbcs &src) {
   setString<ISTRMAX>(value_.TimeString, src);
 }

 /**
  * @param src 「昨日」文字列
  */
 void setYesterdayString(const Lmbcs &src) {
   setString<YTSTRMAX>(value_.YesterdayString, src);
 }

 /**
  * @param src 「今日」文字列
  */
 void setTodayString(const Lmbcs &src) {
   setString<YTSTRMAX>(value_.TodayString, src);
 }

 /**
  * @param src 「明日」文字列
  */
 void setTomorrowString(const Lmbcs &src) {
   setString<YTSTRMAX>(value_.TomorrowString, src);
 }

 /**
  * @return デフォルトの設定
  */
 static INTLFORMAT getCurrent() {
   INTLFORMAT intlformat;
   OSGetIntlSettings(&intlformat, sizeof(INTLFORMAT));
   return intlformat;
 }

 /**
  * @brief 文字列バッファサイズを考慮してLmbcsに変換する。
  * @tparam MAX バッファのサイズ
  * @param src 文字列バッファ
  * @return Lmbcs文字列
  */
 template <size_t MAX>
 static Lmbcs getString(const char *src) {
   auto len = std::min<size_t>(strlen(src), MAX);
   return Lmbcs(src, len);
 }

 /**
  * @brief 文字列バッファサイズを考慮してLmbcs文字列をバッファにコピーする
  * @tparam MAX バッファのサイズ
  * @param dst 文字列バッファ
  * @param src Lmbcs文字列
  */
 template <size_t MAX>
 static void setString(char *dst, const Lmbcs &src) {
   strncpy(dst, src.c_str(), MAX);
 }
};

/**
* @brief 拡張フォーマットを取得する
* @tparam Type 取得データの種類
* @tparam Size バッファサイズ
* @param index Typeに応じて指定するインデックス値
*   月の場合は1-12
*   曜日の場合は1-7
*   カレンダータイプの場合は CALENDAR_***
*/
template <char Type, WORD Size>
Lmbcs getExtFormat(char index = 0) {
 char buffer[Size];
 WORD len = OSGetExtIntlFormat(Type, index, buffer, Size);
 return Lmbcs(buffer, len);
}

} // namespace nxpp::intl

#endif // NXPP_INTL_HPP

まとめ

INTLFORMATをラップするにあたり調べていたら、半角円記号は、LMBCSでは 0xBE という値が割り当てられていることを知りました。LMBCSの日本語文字セットは、全角は 0x10 を頭に付けたシフトJIS(計3バイト)、半角カナは 0x10,0x10 を頭に付けたJIS X 0201の1バイトコード(計3バイト)となっていることは知っていました。歴史上の経緯から、LMBCSでも半角円記号はバックスラッシュと同一の 0x5C だと思っていましたが、バックスラッシュとは別の 0xBE だったとは。
ちなみに、JIS X 0201では0x5Eは半角の「セ」、US-ASCIIでは4分の3記号「¾」が割り当てられていて、なぜ 0xBE なのかは判然としませんでした。

もう一つ、AM/PMに使用されるAMString、PMStringのバッファサイズは次の値で定義されています。

// #include <intl.h>
#define	ISTRMAX 5

たったの5バイトです。ここの文字列はLMBCSとして保管されています。もし仮に日本語で「AM/PM」を「午前/午後」に変更したくても、日本語全角LMBCSは1文字当たり3バイトなので、1バイトはみ出てしまいます。
おそらく、この問題を解消するために、OSGetExtIntlFormat関数が6.0.1から追加されたようです。

// #include <intl.h>

WORD LNPUBLIC OSGetExtIntlFormat (char item, char index, void *buff, WORD bufSize);
/* Item values to pass into OSGetExtIntlFormat(..)  */
#define EXT_AM_STRING		1	/* Request for AM String */
#define EXT_PM_STRING		2	/* Request for PM String */
#define EXT_CURRENCY_STRING	3	/* Request for Currency String */
#define MONTH_NAMES			4	/* Request for Month Names */
#define ABBR_MONTH_NAMES	5	/* Request for abbreviated month names */
#define WEEKDAY_NAMES		6	/* Request for weekday names */
#define ABBR_WEEKDAY_NAMES	7	/* Request for abbreviated weekday names */
#define CALENDARTYPE		8	/* Request for Calendar Type, see CALENDAR_XXX types below */
#define ERANAME				9	/* Request for Asian Native Calendar Name */
#define ABBRERANAME			10  /* Request for abbreviated Asian Native Calendar Name*/

/* CalendarType */
#define CALENDAR_NONE	0
#define CALENDAR_JAPAN	1
#define CALENDAR_TAIWAN	2
#define CALENDAR_THAI	3
#define CALENDAR_KOREA	4

これを使えば、午前/午後、1月〜12月、日曜日〜土曜日(ABBR_WEEKDAY_NAMESを使えば (日)〜(土))を取得できます。ただし、タイプにCALENDARTYPE、ERANAME、ABBRERANAMEを指定しても、何ら値は取得できませんでした。ERAという名称から年号を取得できると思ったんですが。バージョン9なので、平成まででも取れるかと思ったんですが、残念です。

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