vectorの要素をloopしながら削除(添え字版)

久しぶりにやるといつも忘れて、Web検索するハメになるけれど、イテレータの使用例ばかり出てくる(気がする)ので、添え字版をメモ。

std::dequeでも同様。

std::accumulateとtemplate

一度どハマりしたことがあるのでメモ。
std::accumulateは、vectorなどコンテナの要素を加算してくれる便利な関数だが、第三引数にちょっと罠がある。
まず特に問題ないサンプル。

出力結果 : sum : 6

ところが以下の例だと値が丸められてしまう。

出力結果 : sum : 6

この場合は、第三引数がdoubleでなくてはならない。

出力結果 : sum : 6.6

テンプレートクラス内などで使う場合はstatic_castを使う。
例えばメンバをvectorで保持するクラスに合計値を出す関数を実装する例。

出力してみる。

出力結果:
sum : 6
sum : 6.6

OpenTypeフォントにも望み通りの扁平・長体をかける

1.CreateFontIndirectではうまくいかない?

Windowsで、フォントに任意の扁平(長体)をかけて描画するには、CreateFontIndirect(もしくはCreateFont)Apiを使えばいい、はずだった。90年代のプログラミング本にはそうした記述があった記憶がある。が、現在、実際にはOpenTypeフォントや一部のTrueTypeフォントでは、うまくいかない。

OpenTypeフォントなら今時はDirectWriteを使うべきなのではという声もあるだろうが、20世紀からある由緒正しい(?)Windows Apiを使って何とかしてみよう、というお題。
なお、以下の記述は、Windows Apiの関連仕様をすべて正しく把握できている保証はなく、試行錯誤した経験則と推測によるもの。(突っ込み歓迎)

とりあえず試してみる。
基本的にはCreateFontIndirectに渡す適切なLOGFONT構造体を作るだけ。以下、扁平に必要なLOGFONT構造体を作る最低限のコード。

実際にVclアプリケーションで「MS ゴシック」と「游ゴシック」で試してみる。

フォント名以外、全く同一の指定をしているはずなのに、結果はこうなってしまう。

右側の「游ゴシック」の方は、明らかに扁平率が違う。
しかも、天地の描画位置も微妙にずれている。等間隔に線を描画してみると分かる。

2.TEXTMETRIC構造体にない3メンバ

さて、”おかしな扁平” “ずれた位置”になってしまうことに関係しそうなのは、TEXTMETRIC構造体になく、NEWTEXTMETRICEX構造体には存在する以下の3メンバ。
Microsoftの解説は英語しかないが、google翻訳で和訳。

メンバ 解説(Google翻訳)
ntmSizeEM フォントの正方形のサイズを指定します。 この値は概念上の単位(つまり、フォントが設計された単位)になります。
ntmCellHeight フォントの概念的な単位での高さを指定します。 この値は、ntmSizeEMメンバーの値と比較する必要があります。
ntmAvgWidth フォントの平均文字幅を概念上の単位で指定します。 この値は、ntmSizeEMメンバーの値と比較する必要があります。

分かったような分からないような説明だが、ともかく、このNEWTEXTMETRIC構造体の値は、昔ながらのApi EnumFontFamiliesExのコールバック関数EnumFontFamProcで取得できる。

コードは後回しにして、先の2フォントの値はこうなっている。

MS ゴシック 游ゴシック
ntmSizeEM 256 2048
ntmCellHeight 256 2636
ntmAvgWidth 128 1972

MSゴシックの場合はシンプルだ。ntmAvgWidthはntmSizeEMのちょうど半分。ntmSizeEMとntmCellHeightの値が全く同じ。
一方、游ゴシックはntmAvgWidthがntmSizeEMの半分より大きい。ntmCellHeightもtmSizeEMの値を上回る。

先ほどの描画結果とにらみ合わせてみると…。

以下のように推察される。
ntmAvgWidth……ntmSizeEMと同じサイズにしたい場合、この値をlfWidthに指定して初めて正方になる
ntmCellHeight……ntmSizeEMと同じサイズを指定した場合、描画時に、この値分の領域を上下に取る(つまりずれる)

3.3メンバを考慮した指定と描画

つまり、フォントに、望み通りの扁平(長体)をかけ、望み通りの位置に描画するには
(1)LOGFONTの指定時に、lfWidthにntmSizeEMとntmAvgWidthを考慮した値を指定する
(2)描画時の天地位置に、ntmSizeEMとntmCellHeightを考慮した値を指定する
ことが必要になる。

以下、サンプルコード。フォントごとにこれらの値を取得しておき、LOGFONT構造体作成時および描画時に値を調整する。必要な値はVclFormのコンストラクタで取得している。(丸め処理等は省略)

LOGFONT作成関数は以下のように書き換える

実際に描画するコードでは位置調整をする。

実行してみると、望み通りの扁平描画になる。

天地位置も正常。

あらゆるOpenTypeフォントでうまくいくのかどうかは不明だが、手持ちのOpenTypeフォントでは上手くいっている。
C++Builder10.2.3、Windows10で確認。

TDateの++演算子、欠落した日付の列挙

TDateには++演算子が使えることを今さら知った。値を一日ずつ進めてくれる。
比較演算子(<)もあるので、std::setやmapにも格納できる。

Vclアプリケーションでのテストコード。

出力結果:
2018/10/31
2018/11/01

もちろん閏月判定もやってくれる。

出力結果:
2015/02/28
2015/03/01
—————-
2016/02/28
2016/02/29
2016/03/01

前置と後置の評価タイミングも当然異なる。

出力結果:
postfix : equal
prefix : differ

もともと与えられた日付の一覧から、欠落した日付を列挙する方法を調べていて気付いたので、以下、そのサンプル。

出力結果:
2016/02/26
2016/02/28
2016/02/29
2016/03/04
2016/03/05
2016/03/08

なおTDateはTDateTimeのtypedefなので、TDateTimeでも同様に++演算子で1日ずつ値が進む。

出力結果:
2018/11/30 14:35:21
2018/12/01 14:35:21
2018/12/02 14:35:21

Vcl/FireMonkey共通。
C++Bulider10.2.3 / Windows10で確認。