TStringListからvector / TStringDynArrayからvector[C++Builder]

C++Builder(およびDelphi)でよく使われるTStringListとかDynamicArrayを、std::vectorに変換したい場合がよくある。
この場合、いちいちループを回さなくても、STLライクなイテレータ操作が使える、ということを今さら公式ヘルプで知ったのでメモ。

TStringListからvector

TStringListからstd::vectorは以下のように一発で変換可能。

既存のvectorにはassignで代入。

スマートポインタでも問題ない。

文字列リストの基底クラスであるTStringsも同様。
おなじみのフォント一覧を取得するケース。

ソートされた結果を最初から得たい場合は、std::setをいきなり初期化してもいい。

 

DynamicArrayからvector

Delphi動的配列型のDynamicArrayも似たような代入が可能。

IOUtilsのTDirectory::GetFilesはディレクトリ内の全ファイルを一発で取れる便利な関数だが、この返値TStringDynArrayDynamicArray<String>のtypedefなので、同様にvector等に変換できる。

 

以上、C++Builder10.2.3 / Windows10(21H2)で確認。

vectorの重複要素をソートせずに削除する[c++]

vectorの重複要素の削除は、まずソートしてから std::unique / erase という定番手法があるが、現在の要素の順番を崩さないまま行いたい場合もある。

こうしたケース用に、何か定番の方法があるのかと調べたら、こちらのサイトを見ただけでも実にいろいろなやり方があるらしい。

とりあえず、要素の大きさとか重複率とか気にせず、速度や効率にこだわらなければ、結局 std::set を使うベタな方法が個人的には一番分かりやすかった。

なおstd::setのメンバ関数countは1か0を返すだけ。std::findを使うよりシンプルに書ける。

テンプレート関数を作っておくと、どんな型を突っ込んだvectorでもだいたい使えるので楽かもしれない。(というか自分用に作った)

ついでに std::remove_if と c++11以降のラムダ式も使ったバージョン。std::deque版。

これは、setのinsert関数の返値を使うともっとシンプルに書ける、とこちらのサイトで知った。

setのメンバ関数 insert は、引数1つのバージョンだと std::pair<iterator,bool>を返す。挿入に成功するとsecondにはtrueが入る。なるほど便利。

   
C++Builder10.2.3で確認。

[c++]文字列から1文字ずつ、サロゲートペアと異体字セレクタを考慮しつつ取り出す

Unicode文字列から「1文字ずつ」を取り出す場合、異体字セレクタやサロゲートペアを考慮に入れる必要がある。
以下、日本語環境での話(モンゴル文字の異体字セレクタ等は未対応)。

1.std::wstring(2バイト環境)の場合

以前の記事で書いたとおり、異体字セレクタは文字の末尾に4byte分の情報が付加される。(C++Builderで異体字セレクタを使う)

このため、wchar_tが2byteの場合、wstringの「1文字」が取り得るバイト数は以下の4通りになる。

「1文字」の4パターン バイト数
基本文字(基本多言語面) 2byte
サロゲートペア 4byte
基本文字の異体字セレクタ付 6byte
サロゲートペアの異体字セレクタ付 8byte

以下、文字列から、1文字ずつをstd::vectorに格納するメソッド。

(wchar_tが16bit/unsignedであることを前提にしたコード)

2.std::string(utf-8)の場合

utf-8文字列をstd::stringに突っ込んで使う場合もあるので(自分自身はやらないが)、ちょっと調べた。 以下のような方法で取り出せる(たぶん)。

utf-8文字は、先頭バイトを見ると1文字分のバイト数が分かるので、サロゲートペア判定は特に必要ない。 だが、そこで取れるバイト数には異体字セレクタ情報は反映されないので、自前で処理する必要がある。

文字に付加される異体字セレクタは計4byte分で、漢字用のIVSでは以下の範囲を取るようだ。

バイト順 値の範囲
0xF3 (固定)
0xA0 (固定)
0x84~0x87
0x80~0xBF
(0x80~0xAF)

4バイト目は、3バイト目が0x87の時に限り最大で0xAF(たぶん)。

以下、utf-8文字列の格納されたstd::stringから1文字ずつ取り出すメソッド。
なお、IVS最初の0xF3は、単体でも1byteで文字を表現するので、まず2byte分を判定する必要がある。

3.サンプルプログラム

C++Builderによるサンプルプログラム。

TFormにTEditとTMemoをひとつずつ、TButtonを2つ貼り付ける。
TEditとTMemoのフォントは游明朝、サイズを40にしておく。 TEditに以下の文字列を入れておく。

文字列の漢字部分は、最初の「辻」から順に
・基本文字
・異体字セレクタ
・基本文字
・サロゲートペア
・サロゲートペア
・サロゲートペアの異体字セレクタ付
英字は小文字のaが全角、大文字のAは半角。

3-1.std::wstringを使った例

Button1Clickを次のように実装。

結果:

3-2.std::stringを使った例

C++BuilderにはUTF8String型があるので、変換にはそれを使っている。
Button2Clickを以下のように実装。

結果:
 
以上、とりあえずのテストでは上手くいっているが、何か見落とし等があるかも。

[C++Builder]画像ファイルをテキストで保存、読込

C++Builderのフォーム情報(*.dfm)ではアイコン等の画像を16進文字列で保存しているようで、似たようなことをやってみる。(使いどころはパッとは思い浮かばないが)。

とりあえずVclの例。

サンプルプログラム

TFormにTButtonとTImageを2つずつと、TOpenPictureDialogを設置。TImageは2つともProportionalプロパティをtrueにしておく。

画像を16進文字列で保存

手順は以下の通り
①画像ファイルをTMemoryStreamに読み込む
②サイズの同じunsigned char配列を用意し、MemoryStreamからデータを書き込む
③1バイトずつ16進文字列に変換
④文字列をファイル保存

なお、C++Builderは、デフォルトではcharがsignedなので、明示的にunsigned charを指定している。

C:\Windows\Web\Wallpaper\Theme2\img7.jpgを読み込んだ結果:

保存されたテキストデータをエディタで見てみる。

テキストファイルから画像を読込、表示

復元手順は以下の通り。
①文字列のテキストファイルを読み込む
②2文字ずつ数値変換しunsigned char配列に格納
③MemoryStreamにデータを書き込む
④TImageにMemoryStreamから読み込む

復元して右側のImageに表示した結果:

FireMonkeyの場合

FireMonkeyでも、ほぼ同様なコードで可能。
違いは
・OpenPictureDialogがないので代わりにOpenDialogを使う
・Image関連のコードの修正(Pictureの代わりにBitmapを使う)
・jpeg/png/gif用のインクルード不要
だけ。
Image関連の違いは以下の通り。

対象ファイル

TImageに読込・表示できる画像ならおおおむね可能な模様。JPEGのほかPNG/BMP/GIF/TIF、さらにアイコンファイル(ico)でOKだった。メタファイル(emf)はVclでないと読み込めない模様。

ただし、C:\Windows\Web\Wallpaper\Theme1配下にあるWindows10壁紙用のjpeg画像は、VCLの場合だけ失敗した。テキストとして保存はできても、復元でTImageに読み込む時点で「サポートされていないストリーム形式」のエラーが出て、復元不能だった。理由は謎。

C++Builder10.2.3/Windows10(64bit/バージョン20H2)で確認。

[c++]vectorを配列に/配列をvectorに

どっちからどっちに変換するかで方法が違うため、すぐ忘れるのでメモ。

vectorから配列

std::copyを使う方法。

配列からvector

std::copyやvectorのメンバ関数assignを使う方法もあるが、c++11以降なら、以下がたぶん一番シンプル。

配列の大きさとか

以下、おまけ。
既存のvectorに配列を代入するなど、配列のサイズ(要素数)を知りたい場合、c++11以降だと、sizeofを利用する以外の方法も用意されている。

ただし、既存のvectorに、異なるサイズの配列を代入するだけなら、メモリや実行速度にシビアでなければ、いちいちサイズを求めなくとも以下で済むといえば済む。

[c++11]本日の経過ミリ秒を取得する

現在、本日が始まってから何ミリ秒過ぎたかを求めるため、c++11で追加されたstd::chronoライブラリを勉強がてら使ってみた。もっとスマートな方法もあるかもしれないが。以下、c++11/Windows環境の場合。c++17やLinux環境だと別の方法もあるらしい。

現在時刻に関する情報はnow()関数で取れる。なおこの時刻はUTC(協定世界時)であり日本時間とは異なる。

time_since_epoc関数で「エポックからの経過時間」を求めて、メンバ関数countでミリ秒を取得。そこから現在時刻のミリ秒部分を得る。この「エポック」が何時なのかは未規定だが概ね1970/1/1で、経過ミリ秒は軽く1兆を超える値になり、32bit intでは正常に収容できないので注意が必要。(もちろんautoを使えば問題ない)。

時間・分・秒を取得するため、time_tに変換。さらに現地時間(日本時間)に変換する。

この時点で本日の経過ミリ秒は自前で計算可能だが、せっかくtime_pointは減算が効くので、本日0時0分0秒000のtime_stampを求めてみる。

time_point同士の減算から本日の経過ミリ秒を求める。

以下、まとめてシンプルに。

月初めからや年頭からの経過ミリ秒も求めたいが、std::chrono::monthやstd::chrono::yearはc++20で追加予定らしいので、c++11だとすんなりとはいかないようだ。
c++20では、ローカル時間用のtime_pointとか、 1日内の時間情報を取得するクラスtime_of_dayなども追加されるらしく、time_t変換などせずに、もっと楽に取得できるようになるかも。

c++builder10.2.3/Windows10(64bit/1909)で確認。

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

続・Compositeパターンをshared_ptrで実装する…Clone/Assignの追加

Compositeパターンをshared_ptrで実装するの続き。

DeepCopyを作る”Clone”メソッドや、他のComponentから内容をコピーする”Assign”メソッドもあると便利。

Compositeもメンバ変数を保持することも想定して実装してみる。
もっとスマートなやり方もありそうだが、とりあえず、Compositeとその子クラスに非公開のデフォルトコンストラクタを実装する方式で。
(前回同様、c++11の機能を使っているが、c++11以前でもboost利用で同様の実装が可能。また行数省略のためすべてheaderに実装している。)

1.Componentクラス
Clone/Assign用の純粋仮想関数を追加する。

2.Compositeクラス
Clone/Assignメソッドを実装する。
Compositeの派生クラス用の純粋仮想関数doClone/doAssignも追加。
デフォルトConstructorはprotectedとする。
メンバ変数”name_”も追加、

3.ConcreteCompositeクラス
doClone/doAssignメソッドを実装する。非公開のコンストラクタも追加。
doCloneは非公開のデフォルトコンストラクタで作成したshared_ptrを返すだけ。

(不正なAssignの対応は何もしていないが、本来は例外を投げるなどのエラー処理が必要かも)

4.テストコード
適当に親子関係を作成した後、CloneとAssignを実行する。

実行結果
————————
name:c1/A: val == 3
 name:c2/B: str == child
  name:c3/B: str == child of child
   name:c4/A: val == 107
 name:c5/B: str == other child
–Clone–
name:c1/A: val == 3
 name:c2/B: str == child
  name:c3/B: str == child of child
   name:c4/A: val == 107
 name:c5/B: str == other child
–Before Assign–
name:assigned_compo/A: val == 0
–After Assign–
name:c1/A: val == 3
 name:c2/B: str == child
  name:c3/B: str == child of child
   name:c4/A: val == 107
 name:c5/B: str == other child
————————

ConcreteCompositeのメンバを追加・変更した場合、doAssignメソッドのみを書き換えればOK。

Compositeパターンをshared_ptrで実装する

かつて情報をネットで探したときに、断片的な情報はあるものの、分かりやすいサンプルが見付からなかったのでメモ。
Compositeパターンをshared_ptrで実装するのは、単にポインタをshared_ptrに置き換えるだけだと、循環参照が発生する。

ポイントは2つ。
 1.親(Parent)のポインタはshared_ptrでなくweak_ptrに置き換える
 2.shared_from_thisを使う

以下のコードはc++11以降のstl版だが、c++11以前でもboostで同様の実装が可能。必要なヘッダをincludeすれば”std::”を”boost::”に置き換えるだけでOK。
(説明を短くするために全てheaderに入れている)

まずComponentクラス。
メンバ”parent_”をweak_ptrで保持する。

(Executeは階層構造を出力するだけのテスト用Method)

つづいてCompositeクラス。
継承元に”std::enable_shared_from_this”を指定するのがポイント。
また”Add”で親をセットする際、”shared_from_this()”を使う。


CompositeのConcreteクラスを2つ作ってみる。ひとつはstd::string, もう一つはintをメンバに持つことにする。

実行してみる。適当に親子関係を作り、出力。

出力結果。
—————————————-
A: val == 3
 B: str == child
  B: str == child of child
   A: val == 107
 B: str == other child
—————————————-

生ポインタを使うのと比べ、解放処理が不要で便利。