std::pairとstd::tieとstd::make_pairを使う。
std::pairとstd::tieとstd::make_pairを使う。
Visual Studioだけで試した。自分用に貼っておく。
日付の表示方法は国によって違う。アメリカなら’Friday, July 23, 2021’みたいになり、日本なら’2021/7/23’みたいになる。ほかにも、数字のカンマやピリオドのつけ方や金額表示の仕方もちがう。このような違いを扱うのがlocaleという概念。
catergoryには時刻を扱う。LC_TIMEなどいろいろあるが、ここでは一括で扱うLC_ALLを基本的に考える。
locale文字列は国・地域・言語を表す文字列で、”en-US”などと指定するがプログラム開始時には”C”になっており、”C”は1文字1charであることを意味している。”en-US”などの場合は言語と地域を表しており、さらに追加して”en-US.UTF8″などとも書ける。localeにNULLを渡すと現在の値を取得する。localeに空文字を渡すとユーザのデフォルトの国や地域そしてコードページが指定される。
これを英語のWindowsで実行すると以下のように出力される。
Englishが言語で、United Statesが国・地域、1252がコードページを表しこれは’en-US.1252″と同じ意味だろう。
localeに”.UTF8″とするとコードページだけ変えることができる。ここで指定したコードページはmbtowcsなどでワイド文字列に変換するときに使われる。
上記の話はC言語の話だった。C言語ではlocaleは1つしかなく、グローバル設定があるだけだったが、C++ではlocaleはクラスになった。
stdにもstd::setlocale()があり、これは上記と同じようにグローバルなlocaleを設定するものだろう。
引数のないコンストラクタstd::locale()は、グローバルlocaleクラスのコピーを作成する。
std::locale("ja-JP")
は”ja-JP”のlocaleクラスを作成する。作成しただけではどこにも反映されない。
これはsetlocal(LC_ALL, "C")
と同じlocaleのインスタンス参照をかえす。
staticなstd::locale::global(locale& l)
を呼ぶと、locale lをグローバルに設定する。
C++方式でグローバルに設定したlocaleはC言語の関数には影響を与えるがストリームには影響しない。ストリームに反映させるにはimbue()
を使って設定する。
省略、facetはbasic_stringのtraitsのように自分で作ることができる、codecvtもfacetの一つ、これは非推奨でなおかつlocaleを使いにくい。globalなlocaleも反映されない。
これらのキーワードは次のトークンがタイプなのかテンプレートなのかを指定する。テンプレート関数内でテンプレート引数Tに依存するトークンが出てきたとき、コンパイラはそのトークンがタイプなのかテンプレートなのか関数なのか変数なのかわからない。
Aが変数なら、例えば0に置き換えてみると以下のようになる。
<や>は小なりと大なりと解釈され、有効な式になる。
Aが関数の場合は<0>はテンプレート引数となりxを引数とする関数コールになる。
Aがテンプレートクラスの場合はそのクラスインスタンスxの定義になる。
テンプレートが実体化されたときにわかりそうだが、意図した動作と違う時にエラーの発見が難しくもなりそうなので、Tに依存するトークンでそれがあいまいな場合(どんな時あいまいなのかはよくわからない)はtypenameやtemplateといったキーワードをつける約束になっているようだ。この例の場合はAはintなどの基本タイプにはなれない。
このような用途で使うのがtypenameとtemplate。
T::Aがタイプの場合に指定する。これはよく使う。意味不明なエラーが出た場合につけると直ることが多い。
カギカッコなしのtemplateは他の使い方もあるが、ここではTに依存する(Tの中にある)テンプレートクラスやテンプレート関数を指定するときにつけるようだ。テンプレートクラスの場合はタイプでもあるので、両方つけることもある。
こういうものをつけなければならないときはusingを使って別名にしておいた方がいいだろう。
typenanmeもtemplateもつけてはいけない(つけるとエラー)
typenameとtemplateと両方つけるか、何もつけない(typenameだけだとエラー)
両方つけないとエラー
https://github.com/ambiesoft/blogprogs/tree/master/6151/templatetypename
to_stringとto_wstringはintなどを文字列にして返す標準ライブラリ関数。便利なのだが、charとwchar_tで分かれているので汎用コードを書きたいときにそのまま書きたくない。Windowsはもはやwchar_tが主流だしLinuxではcharが主流でwchar_tは4バイトになることが多い。よってこれらをそのまま使うと汎用性がなくなる。
これらの関数をテンプレート関数にしたいとき、テンプレート引数は2つになり、最初の引数はcharかwchar_t、次の引数はintやdoubleとなる。テンプレート関数は部分特殊化ができないので、これらの組み合わせすべて書くしかなくなって効率が悪い。
テンプレートクラスは部分特殊化ができるので、これを利用して汎用関数をつくりたい。文字列クラスのデフォルトをWindowsならwchar_tとしそれ以外ならcharとして扱えば同じ関数記述にできる。
これは実体化されることを想定してないので、static_assert常に失敗。
次にcharの部分特殊化
同様にwchar_t
ここまではユーザは意識しないコード。次がユーザが呼ぶ関数。
SYSTEM_CHAR_TYPEは事前にtypedefされているcharかwchar_t。
テストコード
to_stringにはboolがなく、intとして解釈されるようなのでboolも完全特殊化で定義
ソース:https://github.com/ambiesoft/lsMisc/blob/master/stdosd/stdosd.h
C++にはもともとstreamがあって、それを使えばすべて汎用関数で書けそう。しかしto_stringとかの実装はそうなってないようなのでスルー。
これとは逆の関数。文字列からintなどに変える関数は引数に文字列しか入らないので型の推論ができないのでそれぞれの型ごとに別関数にするしかないものと思われる。
まだ完全にはわからない
std::vector<T>などのTに何でも入れられることを前提にしてつくられているクラスはヘッダに書くしかないと思う。
この場合はソースファイルにかける。
ここでは文字列の長さをを返す関数でchar*とwchar_t*をとるGetStringLength()を考える。
最初のテンプレート関数は汎用のTを受け取る。もしテンプレート関数が実体化されたときはstatic_assertでエラーになるようにしている(sizeof(T)は決して0にならない。falseを指定してしまうと実体化しなくてもエラーになってしまう(コンパイラ依存))。char*とwchar_t*で特殊化するので、それ以外のポインタで呼ばれたときに実体化される。
次の2つは特殊化の宣言。定義はソースに書く。
次の2つは特殊化が実体化されるときの場所の指定。externでどこかにそれがあることを示している。
最初の2つは特殊化の定義。
次の2つは特殊化が定義されるときここに実体化されることを意味する。
今回の場合は実装がほぼないのでテンプレートにする意味があんまりないが、2つの実装がほぼ同じで、一部だけ書き換えたいときは汎用Tの方に実装を書くこともできる。その場合にこのテンプレートを使うこともできる。
ソース:https://github.com/ambiesoft/blogprogs/tree/master/6038
テンプレート関数の定義をソース・ファイルに書くと、他のソース・ファイルからは見ることができないのでリンカーでつなげることになる。よって鉤括弧なしのtemplateは実装を実体化し外部に公開して他のソース・ファイルから見えるようにしているのだと思われる。
鉤括弧ありのテンプレートはあくまでコンパイルフェーズで解決するものであり、宣言だけのテンプレートを実体化しても宣言だけになり、それがリンクされることもないのだと思われる。
is_class<T>を使うとTがクラス(構造体含む)かどうか判定してくれる。
テンプレート関数funcはクラスや、クラスへのポインターや、クラスへのポインターのポインターを渡すとエラーになる。 ただしクラスへのポインターのポインターのポインターを渡すことはできる。
https://github.com/ambiesoft/blogprogs/tree/master/6022/isClass
Tがポインタだとエラーになる。
最初の引数の型Tに対してチェックしている。CheckKataに渡される引数がwchar_t*だったりポインタのポインタだったらエラーになる。ただwchar_tはセーフにしている。
remove_cvなどの挙動がいまいちよくわからないのでこの書き方であっているのか不明。以下のようなコードで実行時の型を表示してくれる。
is_sameは渡された2つの型が同じならtrue。
decayは配列参照などをポインタにしてくれる。
remove_cvはconstやvolatileを除去
remove_pointerはポインタを1つ除去する。
_vで終わるのはvalue,is_same::valueなどと同じ。
_tで終わるのはtype,remove_cv::typeなどと同じ。
ソース:https://github.com/ambiesoft/blogprogs/tree/master/6006/ConsoleApplication1
Cのリテラル文字列は2つ続けて書くと連結してくれる。
ここでL”abcxyz”がほしいときは以下のようにする。
2重のマクロにしないとならない。1重にすると以下のように解釈される
これは以下のように展開される。
つまりマクロを一回かますと、マクロが展開されることになる。##は連結するがシンボルとかを意識しないので単に結びつける、マクロを一回かまさないと展開されないのでLABCになってしまう。このLABCがどこかで#defineされていればちゃんと展開される。
ちなみにマクロの#は文字列にして展開してくれるので以下のように書くこともできる。もっと良い書き方があるのかよくわからない。
https://github.com/ambiesoft/blogprogs/tree/master/5910/literalmacro
コンパイルに/Pオプションを付けるとマクロの展開した時点のソースを拡張子.iをつけて出力してくれる。
レジストリキーHKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps\iwillcrash.exe以下にダンプ情報を作成する。
これはレポジトリにあるcreatecrashdump.regを実行することで作成できる。
iwillcrash.exeを実行して、「Access Null Pointer」をクリックするとアプリが落ちて、%LOCALAPPDATA%\CrashDumpsにダンプファイルが作成される。
上記の実例ではiwillcrash.exeというファイルのアプリのみのダンプをとるので、レジストリキーのiwillcrash.exeのキー名を対象アプリのファイル名に変えれば任意のアプリのダンプをとることができる。
Registry:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CrashControl