total:53566 t:19 y:59
2018-10 煉獄日記
煉獄日記
管理人:織田霧さくら(oda-x)
2018年10月
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
タイトル一覧
月別一覧
年度別一覧
検索
2018-10-23.Tue 車輪~
2018/10/23(Tue) 03:28:21
今日もにこにこPG。

今日は放置ゲーの機能の一つ、オフライン進行のための時間取得まわり。

このへん、オンライン前提のゲームだとよいのですが、オフゲの場合、PCの時間を変更することで簡単にチートできてしまうんですよね。なんにも考えないで作った場合。

時計を一ヶ月ぐらい進める→ゲーム内で一ヶ月放置した状態に→ウマー。

って感じで。

んで、とりあえず思いつく対策としては、ユーザーが変更することが出来ない外部から時間を取得してくる、という方法がある。
で、NTPサーバという時間取得用の鯖から時間を引っ張ってこようというわけですよ。
OSの時間なんかもNTPから定期的に取得して微調整してたりしてます。

んで、外部にネットでアクセスってとwinsockか。
……なんかググっても古い記事ばっかり。1997年の記事とか2000年ちょい過ぎぐらいのばかり。
もう枯れてて、この時代のがそのまま今でも問題ないのだろうかね。
この辺、ファイヤウォールとか、割と最近のIPv6とかそのへんの絡みで古い情報だと無理~とかならないのかなーとちょと不安になりつつもいろいろ下調べ。

で、とりあえずNTPサーバにUDPでリクエストだして時間を受け取る~までは出来たのですが。

リクエスト待ちのとき、プロセスの制御もとまっちまう~。

別スレッドでやって、メッセージの処理とか別で~とか、winアプリ開発で一番うんざりする部分じゃないですかヤダー!

ちなQTだとその辺隠蔽されてるので、情報が英語しかない点のぞけば楽だったなぁ。

で、ふとそいやDXライブラリにも通信機能あったのでわ?
と思いだし、調べてみると……普通にUDPで送受信&別プロセスで実行待機まであるじゃん……。
受信待ちの間のプログレスバー表示とかも簡単にできそうぽ。

ああ、既にある物を作ろうとしてしまった車輪~。


で、DXライブラリの方の機能でNTPサーバから時間取ってくるのぺぺいと出来。
いままでの調べ物に費やした時間はドブに~。まあ下地になる知識仕入れてたからさくっとDXライブラリ使用版のほうも作れたって部分のもあるからそう無駄ってわけでもないか……。

ちょっと悩んだのが、NTPサーバに送るパケットと、受信したデータが、ネットワークバイトオーダーなんですよね。今時のPCのCPUではリトルエンディアンが採用されてるのですが、ネットワークバイトオーダーはいまのところビッグエンディアンで統一されている模様。
んでwinsockではhtonl ntohlといったバイトオーダー変更用のマクロ? があるのだけども。

DXライブラリでUDP通信やってるのだけども、当然内部ではwinsock使ってるんだろうけど、静的ライブラリで別プロジェクトだからなのかな。
htonl、ntohlがつかえない。
んでもこれらを使う為だけのために

#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")

をこっちのプロジェクト内にも設定しなくてはいけないのはうっとうしい。
とくにWinSock2.hはインクルードの順番とかで不具合出たりするらしいので、(windows.hより前にインクルードしないと不味いとか)そういう面倒はご免被りたい。

てかバイトオーダーを反転させてるだけなんですよねhtonlとntohlて。
直接バイトオーダー反転するの自前で書いたらいかんのか?
とおもったのだけども。

WinSockのhtonl、ntohlあたりの関数は、どうもCPUのバイトエンディアン調べていろいろごにょごにょとか、環境まわりの差異の面倒をやってくれる物らしい。

んー現行のリトルエンディアンなCPU上でしか動かすアテがない場合なら自前でバイトオーダー反転させちゃっても問題ない……よな。
そも他の環境ってならwinsock自体つかわんだろうし。
いいや。自前でWinSock2.hインクルードとかしたくないし。
見なかったことにしよう(ぉ

そんな感じでとりあえず出来。

あとは複数のNTPサーバ登録しておいて、上手く取得出来なかったら他の~とかそういった処理とかも必要になるかな。
それにテキストファイルからのNTPサーバ選択とか。本体exe弄らずに使用NTP鯖の変更とかは必要かと。

んでもまあデバッグ時には普通にシステム時間から持ってくるってのでいいので、運用上のデバッグとかは随分先の話になりそうだけど。

はふ~
とにかく回り道をしてしまった~。
2018-10-20.Sat 数式こわい
2018/10/20(Sat) 05:20:57
先日の続き~

とりあえず文字列クラスはこんでいいかーと見切りをつける。
あれから何か特筆すべき事といえば、やはりsprintf系のフォーマット書式はつかいものにならんなと。

str = glib::ustring::format(u8"%s : %d -- %s", u8"文字列", 20, u"utf16文字列");

こゆのがどうにも出来そうにない。
複数の文字コード入り交じった感じの。全部const wchar_t*固定てんなら簡単なんですが。

なんか頑張れば出来そうな方法ありそうではあるものの、えらく無駄が多い物になりそうで、とてもスマートでないものになりそう。
そもそも、ここ最近しばらくsprintf系なんて触ってなかったので、そうだっけ? となったのだけども、適用後の文字列格納するバッファのサイズを取得する_vscwprintfの時点で、間違った内容(%dに文字列を対応させちゃったり)にしたら例外でぶち落ちるのね。即オチなのは関数にnoexcept指定してるからかもだけど。

数値にしても型に対応してないし、文字列もchar or wcharしか使えないし、事前チェックの方法もなく誤ったパラメータ与えるとぶち落ちるってんじゃこりゃ使い物にならんなと。
まあそもそも、デバッグ出力用には随分昔にこのフォーマット書式タイプの物を長らく使ってた事があったので、このタイプのフォーマット書式タイプも懐古的に残しておくかなぐらいの感じで試してみたのですけどね。
結果、やはり苦労してこの方式にこだわるよりも、とっとと過去の遺物として葬り去るのがいいという結論にw

と言うことでフォーマット書式系は、普通に型にも細かいエラー対応も出来るQStringのarg()をぱくったやつのみで行くことに。


文字関係ではもう一つ。
utf-8,16,32対応版std::regexってかんじのSRELL
ttp://www.akenotsuki.com/misc/srell/

なのだけども。
なんでかうごかなーい。テンプレート関係のエラーがずらずらと。
なんでやーと別プロジェクトでシンプルな構成のものにしたら動く~。

とおもったらなんでかutf-16だけうごかなーい。
普通にバグ? ぽいですねこれ。

7568行目
typedef basic_regex<char16_t, u16regex_traits<char16_t> > u16regex;

typedef basic_regex<char16_t> u16regex;

で動いたー!
char32_tの方にはテンプレートに二つ目のパラメータないのであるぇ? とおもったんだよなぁ。
でもほんとはutf32の方にも本来は二つ目必要で、さらに其の先でバグってるという可能性もなきにしもあらず。
てか、SRELL。掲示板とかオープンな場所での情報がまるでないので、こゆときこまるぅ。
あとやっぱテンプレート周りのエラーは見てもさっぱりわからんw

あとこれは環境の問題っぽいんだけど
#if defined(__cplusplus) && __cplusplus >= 201103L
こういうマクロでc++11以降対応してるか判定してるんだけども(char16_t,char32_tはc++11以降)msvcではデフォルトだと/std:c++latestとかしてても__cplusplusの中身は199711 Lのままだったりする……。
/Zc:__cplusplusコンパイラオプションを自前で追加しないとだめな模様。
そこでまず嵌る。

次に、そんなやったら既定オプションとして、プロジェクトの共通設定として書き込んだろ。とおもって、表示→プロジェクトマネージャ……あるぇー?
現在開いてるプロジェクト名だけしか出てこない。
前はここからプロジェクトのデフォルト設定を設定出来たのだけども。
なぜか未だに形だけ残ってるだけでじゃあ出来る場所に移動させろよでおなじみのvc++ディレクトリ設定なんかもそこで出来たのですが。
なんか覚え違いしてる? とググってみると……visualstudio2015以前までは出来たけどいまは出来なくなってるとの記述が……
今は共通設定はMicrosoft.Cpp.Win32.user.propsを直接編集エディタで開いて編集するしかないらしい。
……なんでそこまで共通設定を出来ないようにしたがるのだファッキン。
新規プロジェクト作る度に設定とかめんどくさすぎるだろうよ。
弊害が多いってんなら設定込みのテンプレ用意出来る用にするとかあるだろ……。
うち見たく、vc++使うのはDXライブラリつかったゲーム開発オンリーとか、限定された用途でしか使ってない場合だと、新規プロジェクト作った時点でDXライブラリへのパスとかは最初から設定されてて欲しいわけですよ。
他にも最近はもう使ってないけどboostとかSQLiteとかそのへんの外部ライブラリとかも。
これからは設定ファイルをエディタで開いて直接設定しなきゃ共通設定に設定出来ないとか、ほんとどうしてどんどん使いにくくしていくのかね。

んで、 /Zc:__cplusplusコンパイラオプション設定したあとビルドしたらまたSRELLでutf16使用するとエラー。
しばらく何処がおかしいのだと悩んだあげく、なんかエラー出力の感じがいつもと違うなと気づく。
今作ってるのは静的ライブラリとして作ってる部分で、エラーは本体の方のプロジェクトのmain.cppからエラーがでてるぽ。

……こっちのプロジェクトの方にも/Zc:__cplusplusコンパイラオプション設定しないとダメなのね。
ほらぁ、共通設定出来ないからこういう事に~ふぁーっく。


んで多倍長整数。

とりあえず、一桁を10億進数の多倍長整数クラスを作ってみたり。
あんま別けすぎても繰り上がりの処理が増えるだけだしってことで、最大数の9億9999...同士を掛けてもint64_tで収まるのでこれでいってみようと。

とりあえず足し算、引き算あたりは簡単に実装できる。

かけ算がややこしいなこれ。
事前に調べたKaratsuba法ってのは、それぞれ二分割したとき限定の方法だったのねこれ。
欲しかったのはn分割 * m分割 で速く計算できる方法だったのだけども。
そっちはToom-Cook法で、Karatsuba法はToom-2にあたるのだとか。

で、Toom-Cook法ての調べると、もう何が何やら。数式ばっかでゲボが出そうですよ。むりぽー。数学キライ-。

で、今のところこの多倍長整数が必要なのは、放置ゲーム的な物を一回作ってみようかなと思ってからの所なので、実際にゲームデザインとして、n桁 * m桁のかけ算てでてくるのかなぁと。ちな桁の一つは前述の10億進数での桁数です。

普通に考えて、n桁 * 1桁以外出てこないんじゃと。
可能性としてはn桁 * 1桁+小数部もあるか。倍率が小数点とかこの手のゲームではよく見るし。
そうなると、n桁 * 二桁だけあればよいのではと。
二桁限定なら普通の一桁ずつ掛けて足していく、普通の計算方法(筆算のときのあれ)で十分なんじゃないかと。コスト的にもn桁 * m桁のようにふくれあがることもなさそうだし。

小数部の計算結果用に、桁の右シフト(ついでに左シフトも)を用意しておけばあとはシンプルだなと。
そんな感じでかけ算も泥臭くはあるものの実装完了。

……割り算はさらに厄介そうなのと、そも使わないor小数部のかけ算(割る2なら0.5掛ける)とかである程度は代用できるし……ということで割り算は見なかったことにすることに。

んであとは多倍長整数の文字出力部分を。
一時は、もうめんどくさいからboostいれちゃうかなとおもったけど思いとどまった理由の一つに、boostの多倍長整数ライブラリは、文字出力が一括全桁しかないっぽいってのがあって。
結構この数値の文字化って重たい処理なんですよね。
で、毎度全桁出力とか無駄が多すぎるだろと。

実際的に使うのは、頭から5~6桁しか使わないわけですよ。
68.458quintillionとか1.58954 E+58とかこんな感じで。
なので必要な最小の桁だけ文字化する感じのがいいなーってところで、やっぱ自前で組むかーって流れだったりする。

しかし英語表記になると、結構諸説あってどれがポピュラーなのか判らないですね。途中で並びが違うのあったりとか、こっちにはなくてあっちにはあるなんてのもあったりして。
どれが正しいのかさっぱり判らん。
あとgoogolplexplexplexとかみただけで桁が想像も付かん……ってのもあるんですけど。

この手の放置系ゲームいくつもやってるとundecillionとかこのあたりぐらいまではなじみがあったりするので、だいたいどのくらいーてのがぼんやりと浮かんだりするんですけど。
とはいえScientific 科学的表記? ていうのの 1.123456+10
と、最上位一桁で其の桁までの桁数を後ろに+でつけるのは、数の大小はすぐに判別付くけど、ちょっと味気ないんだよなーというところもあって。

んでも日本表記だと桁が少なすぎる。無量大数にあっちゅーまに到達してしまう。あと単位の一つ、「杼(じょ)」これは正確でなく代用文字の方なんですけど、正式な方は外字で、表示できるか環境次第という問題なんかもあったりする。

細かいところでは日本式だと4桁区切りで単位が変わるのだけども、英数だと3桁区切りだったりする。
その辺で、多倍長整数の桁も9桁てのは3で割れるので英数だと扱いやすかったりする。

そんな感じで多倍長整数クラス出来。
数式怖い。
2018-10-15.Mon c++の闇は深い
2018/10/15(Mon) 03:32:07
今日もがりごりPG。

相変わらず文字コードがらみの自前stringクラスあたりで行ったり来たり。
この辺、はっきりとした正解というのもが無いので、好みの部分だったり、書きやすさだったりといった、つまらない部分でぐだぐだと悩んだりするぽ。

いまちょっと悩んでるのは、

class ustring final : public std::basic_string<uchar> { ... }
class ustring final : protected std::basic_string<uchar> { ... }

どっちにするか。ちなucharは
using uchar = char16_t or char32_tな感じで。
wchar_tと同じものを選択する前提で。wchar_tとノーコストで相互変換出来るほうがいいかなということで。
win環境だとchar16_tですね。

んで、このustringクラスはメンバ変数を持たず、また仮想関数も持ってないんですよね。
あるのは基底クラスの関数のオーバーロードとか追加機能の関数のみ。

そしてstd::basic_stringは仮想デストラクタを持っていないので、基本的にはprotected継承をするべきなんでしょうけど。(そもそも仮想デストラクタもってないクラスは継承するべきでないという考え方もある)

そもそも仮想デストラクタをもってないクラスを継承すると何が不味いかというと、基底クラスに暗黙のアップキャストが行われた場合、派生クラスのデストラクタが呼ばれない=メモリリークが起る。
てのが問題なわけで。
protected継承すると、基底クラスへのアップキャストを禁止出来るわけです。

が、今回のケースの場合、ustringからchar16_t(std::u16string)に暗黙変換出来ないのはちょっとヤなわけですよ。
中身のデータは、wchar_tとchar16_tのように型は違えど中身は全く一緒だし。

ustring str = u8"あいうえお";
std::u16string utf16 = str.data();

protected継承の場合はこのように基底クラスのdata()メソッド呼んでコピーしてやれば、アップキャストではなく単純に中身のデータのコピーになるので問題ないのですがやはり

std::u16string utf16 = str;

と書きたい。

これをするためにはpublic継承にしなくてはならない。

んで、いっぺん上にも書いたけど、このustringクラス。

「ustringクラスはメンバ変数を持たず、また仮想関数も持ってない」

んですよね。
実質サイズ0。仮想関数も持ってないので当然仮想関数テーブルも持っていない。
オブジェクトの実体のバイナリデータは基底クラスのそれと全く同一なわけです。

ググっても、「空の基底クラス」についての記述はあるんですが「空の派生クラス」については該当する記事は見あたらず。

とにかく盲目的に継承するなら仮想デストラクタ! と呪文のように唱えてる所ばかりぽこぽこと。

空の派生クラスの場合は仮想デストラクタ無しの基底クラス継承してアップキャストしても、問題ないように思うのだけど……
ググっても明確にそれを肯定も否定もしてる記事は見つからず。
唯一一件見つかったところは「問題は起らないけどやらない方がよい」と書かれているだけで、なにがどうしてやらないほうが良いのかは語られていない。

その辺、win環境のwchar_t=2byte 中身はutf16で
char16_tとwchat_tをreinterpret_castするのも、「問題は起らないけどやらない方がよい」と言うひともいるわけだし。
まあreinterpret_castはたしかにできれば使いたくないcastではあるけど。
気分の問題のレベル(あとものすごく特殊な状況において問題がでないともかぎらない危険性を孕んでいるのも事実だけど)なんだろうか。

そのへんで、「問題ないならつかっちゃえ」と「やっぱなんだか気持ち悪い」の2派が心の中で闘争中なのですよね。
空の派生クラスの非仮想デストラクタ基底クラスのpublic継承。

それで得られるのが、わざわざ.data()メソッドなしで直で代入とかできるだけという恩恵。

しかしコードがごちゃごちゃするのはいやだ。
んでもほんとに中身は同一だけど何にも問題無いの??
アップキャスト前後でもオブジェクトのサイズは同一だし、メモリのリークも起ってない。
問題ないから使っても良い物なのか。

そんな感じでモヤモヤ~。

そもそもこれって、派生ってより、アダプタってかんじなんですよねぇ。
言語機能としてこういうのあってくれるといいんですけどね。
アップキャストしても派生クラスのサイズが0なら派生クラスのデストラクタ呼ばれなくても問題ないっていう保証がほすぃ。
なんかc#かjavaにはそゆのあるらしい?

継承やめてメンバにstd::basic_string持つコンポジットパターンだと、メンバ全部自前で追加が苦行だしなぁ。

この問題で一番めんどくさいのが、いったん良しこっちにしようと決めたあとで、やっぱ問題あるらしいとわかって修正するとき。
この文字クラスはかなり広範囲にわたって使われる類のものなので、修正となると結構大変なんですよね。

んでも、やっぱ暗黙変換ほしいなーとおもうところに早速ぶちあたったりしてるんですよね。
std::regexとかのcharタイプを設定して使う系のライブラリとか。
まあ今現在std::regexはchar8_t、char16_t、char32_tどれも未対応ですけど。
そしてchar16_tは将来においても対応しないとか言われてたりしますけど。

SRELL (std::regex-like library)
ttp://www.akenotsuki.com/misc/srell/
char8_t、char16_t、char32_t対応版のstd::regex風ライブラリなんか導入していまのところ対応するつもりですけども。

その場合、受け渡しはchar16_tを使う事になるので、regex使う箇所に毎度.data()とか書いてられないなーと。

ていうかstd::basic_regex<wchar_t>でchar16_t←→wchar_tの変換しょっちゅう噛ましてるコード書くの面倒になって、どうせ今後もしばらく対応しないし、いまいち余所で使われてるという話を聞かないのでパフォーマンスとかどうなの? とおもっていたSRELLを本格採用してみるかとおもったところで、結局char16_t使えるようになっても、ustringから変換メソッドいるんじゃめんどくささはいっしょじゃねーか。ってなってたりして。

文字周りめんどくさ……。


んで平行してぼちぼち調べてるのが、多倍長整数ライブラリ。
文字周りとGUIまわりの部分ををいまはガリガリやってるのですが、それらの実地テストとして、簡単な放置ゲーム的なものを一本作ってみるかなと考えてたりして。

で、そこで多倍長整数てのは必要になるよなーと。
プログラムで普通に扱える一番大きな整数といえば、unsigned long long(符合なし8バイト)で1844京6744兆737億955万1615。
放置ゲーとかクリッカー系やったことある人なら判ると思うけど、京の位なんでまだまだようやく序盤抜けたかなぐらいの数字なわけで。
むかしちょっとやってた放置ゲームが最近スチーム版もあると知って懐かしくなって再開してたりするんですが、既に桁の数が80桁ぐらいになってるし。
(それでもまだ序盤ぬけてこれから中盤戦ってところっぽい)

で、そういう大きい数を扱うためには普通の変数一個じゃ収まらないわけですよ。

しかし、とっくにあるモンだと思ってたのですが、c++には標準では多倍長整数ライブラリ無い……_| ̄|○

perlにもrubyにもjavaにもあまつさえC#にもあるというのにっ。

まあc++の場合はパフォーマンス重視なところあるので、そのへんで昔から揉めてて決定的な物が出てきてない……って感じらしいです。boostには一応あるし、GNUとかLGPLライセンス付なのがうっとうしいGMPとかもあったりするのだけども。
いまのところ整数しか使う予定ないので、その手のライブラリは浮動小数とかほかにもいろんな機能いぱーいで、当然ソースもいぱーいで、整数部分だけちょろっと使いたいと思っても結局あれもこれも付いてくるってかんじで気軽に扱いづらかったりする。

そこで、機能をかなり限定すれば、自前でも十分用途には足るんじゃないかなと、車輪の再発明になっちゃう感じだけど考えて見たりしてたりで。
普通に思いつくのは基数1000にする1000進数とかかな。
999*999の結果が収まるサイズあれば十分なのでuint32あたりか。
一桁uint32で各桁もって、下の桁から順番に足してくの。
かけ算になると
「カラツバ法」
https://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%A9%E3%83%84%E3%83%90%E6%B3%95
てのがつかえるっぽい。

関連項目にカラツバ法よりも高速とか書かれてる「ショーンハーゲ・ストラッセン法」高速フーリエ変換を使用しているって、なにげにフーリエ変換で音楽の波形編集とかでよくみる語なんだけど、フーリエ変換のウィキみたら、そっとじ……あ…頭がw
てか
実際には、ショーンハーゲ・ストラッセン法がカラツバ法やToom-3をのような従来の手法より性能が上回り始めるのは、2215 - 2217(10進法で10 000 - 40 000桁)同士の掛け算である


うん、あんまりおいらには関係無いかもw

そんな感じでPGがりごり。
2018-10-04.Thu 遅々として
2018/10/04(Thu) 07:36:18
ここ最近はダンジョンRPGゲーム開発用のエディタ作りをやっていたのですが。
ちょっとエディタの作りの方も飽きてきたのでちょっと転進。
実際のゲームPGのほうのオレオレライブラリ作りの組み直しにとりかかる。

ずっとこっちに触れていない間、ずっと文字コード周りをどうするかというのを決めかねていたりで。
状況的にはあんまりかわってないんですよね。いまだにchar8_tが追加されないし。

コード自体の文字コードがutf8というのはもはやスタンダードなので良いとして、コード内の文字列リテラルをどうするのか。プロジェクト内での内部文字コードを何にするのか。

いまのところ、実行環境次第でも変化するものなので「明確な正解はない」というのが実情で。

とはいえ、明確な正解が出てくるまで待っているわけにもいかないので、なんとか将来的な変更も予想したりしながらやりくりするしかないぽ。

とりあえず今のところの方針は。
vc++環境でターゲットはwindowsという前提で。

■vc++文字コードセット設定は「unicode」を使用。
「マルチバイト」の方はshift-jis前提なのでもはや過去の遺物。古いプロジェクトの保守用でしょう。

■TCHATおよびwchar_tは使わない
wchar_tは環境によって中身が変わるため(現行win=2byte、unix系=4byte)
文字コード周りはchar16_tやchar32_tなど環境依存でない物を使う。
が、後述の理由により、「表面的にwchar_tは使わない」で妥協

■文字列リテラルに_T("")マクロおよびL""は使わないでu8""で統一
TCHATと同じ理由で使わない方がよさげ。
専用のdxT("")などを用意して、環境に合わせて変える必要がある部分は対応。
L""はwin用専用コード内部でwinapiに直接渡す場合のみ利用。

■utf-8はあくまで受け皿
というのもutf8は文字列操作に向いてない形式。
向いてるのは外部から受け取ったりするときに文字化けし難い、受け取った文字はutf8だと判別しやすいという所。

■const char* std::stringの中身はutf8と想定
shift-jisはもはや過去の遺物なので使用しないのが望ましい。
中身がutf8だと明確にするにはchar8_tが必要なのだが現状ないのでしかたなくusing char8_t = charとかusing u8string = std::stringとか気持ち悪いけどやっておく方向で。

■utf8→内部コード→最終出力コードの流れで
コード内の文字リテラル、リソースファイル内の文字コードはutf8で統一。
画面出力の際に内部コードから最終出力コードに変換する。

■内部コードは……なんだかんだでutf16
文字列操作を考えると、utf16かutf32の二択か。
しかしwin上だと、char16_tとwchar_tはバイト列では同一になるとはいえ、reinterpret_cast使うのがなんとなく気持ち悪い……。
そしてstd::wcstolあたりの標準ライブラリにchar16_t対応版が無い。つかえないこともないけど毎度wchar_tにキャストの嵐に……。
その辺の事情はutf32も同じ。標準ライブラリの対応がまったく追いついてない。しかし文字列操作を全部自前で書くというのも現実的ではない。
てかwchar_tが4バイトになるunix系ではstd::wcstolとかどうなってるの??
unix系ではそもそも日本語はじめ多バイト文字を使わない前提なかんじで無視?
内部コードはutf32がよさげなのだけども(一文字あたりつねに4バイトも使うのはアレだけど)strtol系が自前で書かなきゃ行けない。
to_stringもwchar_tの場合std::wostringstreamとかつかうんだけどもこのストリーム系もutf16、utf32はまだ未対応。なのでutf16、utf32は「早過ぎたんだ……腐ってやがる……」と巨神兵状態……。まだ使い物になりません。
じゃあどうするかと言えば内部コードはutf16としながらも内部コードを扱う文字クラスの中ではwchar_tで管理することに。結局内部コードをもつ文字列クラスはwin専用クラスに……。将来的にはこの文字列クラスの中身を書き換えるだけでいろいろと変更に対応出来る感じに組むしかないな……。

■サロゲートペア問題は局所的問題として解決
内部コードは現状、文字変換の標準ライブラリが使えるのでwchar_t(=utf16)がベターだとおもわれるのだけどもutf16にはサロゲートペアというお荷物仕様が。
問題になるのは文字列分割の際に、その境界にサロゲートペアの文字が来たときの場合なので、ゲームPGでは文字を1文字づつぺろぺろだすメッセージウィンドウとかぐらいでしょうか。ゲームスクリプトの解析なんかはスクリプト用のコマンドは半角限定にすればサロゲートペアの影響は受けないですし。
サロゲートペア対応はそれに特化したメッセージウィンドウクラスの中でのみ対処して、文字列クラスのなかではサロゲートペアがらみの処理は一切省くかんじで良いと思われる。(文字コードの相互変換部分では必要だけど)
サロゲートペアの表示自体はDXライブラリとかのお仕事なので気にしない。


そんなかんじですすめてみるかーと。

で、各種文字コード変換がひつようになるのだけども。
標準ライブラリの文字コード変換のstd::codecvtはどうも実装がかなりおそまつでセキュアでもないってことでc++17で非推奨……。代替用意してから消えて欲しいものですw
てことでutf8←→utf16はwinapiでもあるのだけどもそれ以外のutf16←→utf32とutf8←→utf32がwinapiでは用意されていない。
ということでその辺自前で実装することに。

そこでutf8←→utf16の変換、自前実装とwinapiとでコスト的にどのくらい差があったりするものなのか……と気になったので計測してみたり。

utf8→utf16変換 
結果の左は変換文字数を400文字 右は20文字
それぞれ10000回変換した結果。

MultiByteToWideChar(winapi)
debug = 280ms 210ms
relese = 50ms 14ms

自前変換utf8 → utf16
debug = 2370ms 300ms
relese = 54ms 12ms

ふむう。さすがwinapi。
独自最適化されてるせいか、デバッグビルドでもそこそこ速い。
一方自前実装の方はデバッグビルドだとクッソ遅いw
結果受け取りのバッファにstd::unique_ptr使ってるところで、リリースビルドだと生ポインタとほぼ同じコストになるけどデバッグビルドだとクッソ遅い。ってところで足を引っ張ってる物とおもわれる。
しかし何度か実行した平均値をとってきてるのだけども、少ない文字数の場合リリースビルドだと、微妙に自前実装のが速かったりする。文字数多い場合だとちょっと負けてるけど。
その辺、MultiByteToWideCharではやってる安全チェックのいくつかをすっ飛ばしてる部分の差が出てるのかなーとか。

そんな感じでutf8←→utf16変換に関しては、vc++上ではwinapiをつかって、win以外の方では自前実装のものを使う感じの運用にすることに。

あとは10000回変換してこの速度なら、毎度しょっちゅうコード全域にわたって文字コード変換してもたいしたコストじゃないんだなーやっぱり。とかおもたり。
QTのソースコードと互換性ほしいので、文字列リテラルはu8""で統一したいのだけども、毎度変換コストかかるのはどうなのかなぁとおもてたので。
QTに関してはGUIアプリ作成用なので変換コストとか気にすることはないのだけども、ゲームPGではそうもいなかかったりするし。

しかし何となく適当に多めの文字列ってところで適当な文字列をその辺からコピペしたのがだいたい400文字ぐらいだったのだけども。
400文字て普通の原稿用紙一枚分なんだよなと。
で10000回変換てことは原稿用紙10000ページ分てと、一般的な文庫本だと一冊400字詰め原稿用紙換算だと500ページぐらいらしいので小説本20冊分ぐらいか。
適当にぐぐってみたところ、一般的なビジュアルノベルの文章量てのは50万文字ぐらいとからしい(しっかりとボリュームある感じの場合? すくないのは20万文字とかいうデータもあったのでよくわからん)50万/400で原稿用紙1250ページ分とかんがえるとその約10倍の量のテキストを読み込んで(ファイル読み込みにかかる時間はこの際無視して)一括でutf8からutf16に変換してもリリースビルドなら50msしかかからないのかーと思うと、ゲームで使うテキストファイルも変換コスト避けるために内部コードと同じ文字コードに……なんてのもセコセコバッタの無駄な努力なのだなぁとかおもてしまったりw
無視出来るレベルのコストのために、無駄な面倒抱えるべきではないですね……。

そんな感じで、まだまだほんとにこれでいいのか? と模索しながらの文字コード周り。
いっそもうwin専用としてガッツり書いた方が楽なんだけどなーとか一瞬楽な方に流れそうになりながらガリガリと。

2018-10-03.Wed 台風よくくるな
2018/10/03(Wed) 04:00:09
今度の台風も結構強いとかいうので、また停電とかやだなーとおもってたら、今度はなんにもおきず。そんなもんだよね。懐中電灯とか置き場確認とかした時に限って何にも起きない。これもマーフィーの法則か。

そんな感じで台風襲来時、停電すると厄介なのでPC電源落して、読書で夜を過ごすことに。

堂場瞬一「ランニング・ワイルド」

堂場瞬一さんのは、大別すると事件物(探偵・刑事物)と記者物とスポーツ物の三つのジャンルの物を書く人なのだけども。今回読んだ「ランニング・ワイルド」は、警察官達が主役ではあるのだけども、その人達がやってる趣味でやってるスポーツの話で、そこに事件も絡んでくると言うかんじで。
いままではスポーツ物は純粋なスポーツ物をきっちり書くという感じだったので、いろいろ混ぜてきたなーと。
んでも敵役が余りにも小物感あり過ぎて、だんだん哀れになってくるというw
敵方の計画もあまりにも穴だらけで、仲間も足を引っ張る奴ばかりで、むしろこの敵方の方がじつは主役なのでわ? とさえおもえる感じで、最後はドロップアウトした元スポーツ選手の悲哀っていうのが主題だったのかなとか。
この著者の書く、他の純粋なスポーツ物のつもりで読み始めたので、普通に面白かったです。

パトリシア・コーンウェル「接触」

検屍官スカーペッタシリーズ第8作目。
後書きに、著者の発言として、長期シリーズになるとどうしてもマンネリになってしまうので今回はいろいろとやってみた的なことが書れていて、今作はマンネリ打破の為なのか随分と変化があった内容。
検屍官の仕事じゃないだろ的なアウトブレイク物とかバイオハザード物ってかんじの内容で、なんだか映画向けのシナリオって感じの趣でした。
1997年発行の本なのだけども、劇中で、一枚の犯行現場の写真を3Dで再構築して、スカーペッタがVRで写真のなかに立って犯行現場を眺める。というシーンがあるのだけども。
最新の技術紹介ネタではあるのだけど、1997年での3DでVRでは今見たら相当しょぼい内容なんだろうなーとかおもたり。
一枚の写真から、壁のシミとか現場の空気感とか現場検証してるんだけど、あの当時の先端技術ってもそこまでクオリティでてたのかなと。
まあバーチャルボーイよりはましだろうけど。てかバーチャルボーイぐぐってみたら1995年発売なのか。てと、いっときVRが注目されだした時代に反映された内容だったのかなと。
その後一端廃れたよね。そんで割と最近またVRが世に出てきた感じですね。

あとはまあ、相変わらず、個人的な嗜好として、中年のバツイチのおばちゃんの恋愛の話は読んでてきっついw
その辺はやはり女性作家だなーとかおもたり。この辺はまったく肌に合わないのだけども、検視とか監察医物が好きで、この作家さんは実際に検屍局に勤めていた経歴(検死官ではなく事務畑だけど)もあってか、とってもリアリティのある描写なので、このシリーズは読み続けたいとおもってたりで。