Author Archives: admin

C言語の標準ライブラリで文字コードの変換

Visual Studioだけで試した。自分用に貼っておく。

C言語とC++のlocale

localeの基本

日付の表示方法は国によって違う。アメリカなら’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言語の話だった。C言語ではlocaleは1つしかなく、グローバル設定があるだけだったが、C++ではlocaleはクラスになった。

C言語との関係

stdにもstd::setlocale()があり、これは上記と同じようにグローバルなlocaleを設定するものだろう。

localeクラス

  • C++ではcategoryの代わりにFacetと言うようになった。
  • C言語ではsetlocal()で設定した情報はグローバルでどこかに格納されそれがアプリコードからは見えないところで参照されるが、C++のストリームでは明示的にlocaleクラスを設定しなければならない

コンストラクタ―

引数のないコンストラクタstd::locale()は、グローバルlocaleクラスのコピーを作成する。

文字列を指定

std::locale("ja-JP")は”ja-JP”のlocaleクラスを作成する。作成しただけではどこにも反映されない。

std::locale::classic();

これはsetlocal(LC_ALL, "C")と同じlocaleのインスタンス参照をかえす。

グローバルに設定

staticなstd::locale::global(locale& l)を呼ぶと、locale lをグローバルに設定する。

ストリームに設定

C++方式でグローバルに設定したlocaleはC言語の関数には影響を与えるがストリームには影響しない。ストリームに反映させるにはimbue()を使って設定する。

Facetとstd::codecvt

省略、facetはbasic_stringのtraitsのように自分で作ることができる、codecvtもfacetの一つ、これは非推奨でなおかつlocaleを使いにくい。globalなlocaleも反映されない。

式の中に出てくるtypenameやtemplate

これらのキーワードは次のトークンがタイプなのかテンプレートなのかを指定する。テンプレート関数内でテンプレート引数Tに依存するトークンが出てきたとき、コンパイラはそのトークンがタイプなのかテンプレートなのか関数なのか変数なのかわからない。

Aが変数なら、例えば0に置き換えてみると以下のようになる。

<や>は小なりと大なりと解釈され、有効な式になる。

Aが関数の場合は<0>はテンプレート引数となりxを引数とする関数コールになる。

Aがテンプレートクラスの場合はそのクラスインスタンスxの定義になる。

テンプレートが実体化されたときにわかりそうだが、意図した動作と違う時にエラーの発見が難しくもなりそうなので、Tに依存するトークンでそれがあいまいな場合(どんな時あいまいなのかはよくわからない)はtypenameやtemplateといったキーワードをつける約束になっているようだ。この例の場合はAはintなどの基本タイプにはなれない。

このような用途で使うのがtypenameとtemplate。

typename

T::Aがタイプの場合に指定する。これはよく使う。意味不明なエラーが出た場合につけると直ることが多い。

template

カギカッコなしのtemplateは他の使い方もあるが、ここではTに依存する(Tの中にある)テンプレートクラスやテンプレート関数を指定するときにつけるようだ。テンプレートクラスの場合はタイプでもあるので、両方つけることもある。

こういうものをつけなければならないときはusingを使って別名にしておいた方がいいだろう。

実験したまとめ

Aが変数の場合

typenanmeもtemplateもつけてはいけない(つけるとエラー)

Aが関数テンプレートの場合

typenameとtemplateと両方つけるか、何もつけない(typenameだけだとエラー)

Aがクラステンプレートの場合

両方つけないとエラー

ソース

https://github.com/ambiesoft/blogprogs/tree/master/6151/templatetypename

参考にした動画

pythonのargparseの基本

pythonのargparseが難しいくて戸惑うので基本のもの

引数を取らないオプション

引数を1個とるオプション(デフォルト)

引数を1個とるオプション(個数指定)

0個か1個だけとるメイン(オプションのない)の引数

1個からN個とるメインの引数

nargsが+

0個からN個とるメインの引数

考察

ほかにもいろいろなことができるのだがやりすぎて使いにくくなってる感がある。基本を押さえと置けば理解もできるようになると思う。

python: 文字列はimmutableでリストはmutable

immutable

とは値を変えられないということ、というふうに言われるがpythonの場合は分かりづらい。
immutableとは変数の値が知らないところで書き換えられることがないことを示している。
文字列はimmutableと言われる。

C言語だと文字列はmutableになる。

pythonや多くの言語では上記のようなことはできず、文字列はimmutableになっている。

リストはmutable

mutableとは変数が知らないところで書き換わってしまうこと。

aとbは同じ実体を表すのでaに追加した4がbにも反映される。
しかし以下の場合は違う。

aに代入した[4,5,6]はもともとa,b両方が参照していたリストとは違うリストで新しく代入している。この場合はaとbは違うリストを参照している。
2つの変数が参照しているものが同じかどうかを調べるにはid()を使う。

文字列とリストでの関数の違い

しかし文字列でも同じid()を持つことはある。同じであってもそれが読み込みしかされていないなら問題はない。しかしそれを変更するわけにはいかないことから、このような違いは文字列とリストでの関数の違いとなって現れる。文字列の関数は常に新しい文字列を返し、自分自身を変更することはしない。リストの関数は常に自分自身を変更し、リストを返さない。

upper()は新しい文字列を返し、自分自身は変更しない。

append()は何も返さず、自分自身を変更する。

注意点

このようなふるまいからリストを使うときは注意を要することがある。

append()はリストを返さないのでlには何も入らない。以下のようにしないといけない。

pythonでcat

catコマンド

ファイルの中身を表示する

以下のようにしても動く

これは標準入力からファイルの内容を受け取りそれを出力している。

標準入力での切り分け

上記のことをpythonで実現するにはまず標準入力が普通の標準入力(キーボード)かパイプかを調べて処理を分ける。

ttyとはteleprinterのことで、昔あった手元にあるキーボードから遠くのプリンターに文字をタイプするマシンのことらしい。Linux界では接続されているキーボードをttyという概念で認識する。

ttyだった場合、引数にファイルがあればそれを表示し、なければ標準入力(キーボード)から入力を受け付ける。

ファイルの処理

パイプの処理

標準入力がキーボードで引数がない場合やパイプの場合はstdinから読み込む

これで大体動くが、catとの違いはキーボードから入力したときのエコーバックの時期が違う。catは1行ごとにエコーバックするが、このスクリプトの場合、Ctrl+D(Linux)やCtrl+Z(Windows)でEOFが送られるか、バッファがいっぱいになるまでwriteへ処理がいかない。よってこれを直す。

考察

キーボードとパイプの両方から入力を受け付けると便利なことが多い。スクリプトがjsonファイルを受け付けるとき、ユーザー側には他の形式のファイルがあってそれをjsonに変換してスクリプトの入力としたい時などパイプ入力がないといったんファイルに変換しないとならなくなる。

C++/CLIのstatic コンストラクタの呼ばれる順番

staticコンストラクタはクラスがはじめてつかわれる時に一回だけ呼ばれるコンストラクタ

テスト

上記の場合はA→Bの順で呼ばれる。

上記の場合はB→Aの順で呼ばれる。Bが使われてるところでAも呼ばれる。

考察

上記のAがライブラリのような自分で触れないコードの場合、それがいつ呼ばれるのか、すでに呼ばれているのかを知る一般的な方法はないと思う。Astaticコンストラクタでなにかのデータを初期化してる場合はBstaticコンストラクタにそのデータを書くのは危険だろう。

多重継承できるインターフェースのstaticコンストラクタは呼ばれなかった。

ソースコードにgitのhashを埋め込んでおく

ビルドの際にgitのリビジョン(sha1ハッシュ)をソースに埋め込んでおけば、デグレードが起きた際などにソースをかんたんに復元できるだろう。gitにはrev-parseというサブコマンドがあり、これでHEADの40桁のsha1を出力できるのでpythonで取得するには以下のように書けばいい。

check_outputはコマンドの出力を返してくれる(コマンドが0以外を返すと例外)。

あとはC++ヘッダーファイルなどに書き出して、ビルドすればよい。

gitおサブモジュールを使っている場合はこれで問題ないが、他のgitで管理しているソースも取り込んでいる場合には、それらのハッシュも記録しておいたほうがいいだろう。

C++/CLIでListのソート

大変

基本

これは簡単。しかし要素がクラスだと難しい

SortはインターフェースIComparerを引数にとるので、まずはこれを継承するクラスをつくりその仮想関数Compareで比較をする。

自分のクラスで

自分のクラスMyClassstatic int Compareを定義する。次にMyClassComparerも定義する。こうしないでMyClassIComparerから派生させると、Sortの際に自分のクラスを渡すことになってなんかおかしい感じになる。

ソース:https://github.com/ambiesoft/blogprogs/tree/master/6085

to_stringとto_wstring

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。

テストコード

bool追加

to_stringにはboolがなく、intとして解釈されるようなのでboolも完全特殊化で定義

ソース:https://github.com/ambiesoft/lsMisc/blob/master/stdosd/stdosd.h

考察

C++にはもともとstreamがあって、それを使えばすべて汎用関数で書けそう。しかしto_stringとかの実装はそうなってないようなのでスルー。

これとは逆の関数。文字列からintなどに変える関数は引数に文字列しか入らないので型の推論ができないのでそれぞれの型ごとに別関数にするしかないものと思われる。