![見出し画像](https://assets.st-note.com/production/uploads/images/60335892/rectangle_large_type_2_aa408ad460d00f0cf6a5b58c490d6cf1.png?width=1200)
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なので、平成まででも取れるかと思ったんですが、残念です。