2017年2月11日土曜日

c++におけるclassの相互参照について

大きなプログラムを長年メンテナンスしていて、改修を繰り返しているとclassの相互参照が必要になってくることがあります。しかも、互いのclassは昔は関係なかったために、別headerで作成していて、相互に#includeしないといけなくなったりします。
ただ、普通にそれをやってしまうとcompilerがやはり「そのclassわからない」というようなエラーを返してきます。

undefined classA
has incomplete classA

とかいってきます。まあ当たり前とは思うんですが、それでもそれを解決しないと先に進みません。色々なキーワードでぐぐってみましたが、やっと理解できたのでメモしておきます。とりあえず、以下にclass Aとclass Bが相互参照しているサンプルを示します。


複数のファイル(classA.h, classA.cpp, classB.h, classB.cpp, main.cpp)で構成されています。この相互参照を可能にするために重要なのが3点あります。

第1点
classA.hの10行目、class B;の参照を宣言しておく。

これはclassA.h内でclass Bを使うために#include "classB.h"をやっているんですが、classA.hをコンパイル時にはまだclass Bのコンパイルは行っていないため、class Bの定義内容がコンパイラにはわかりません。そこで、「とりあえず」class Bというのがあるよ、とプロトタイプ宣言だけしてコンパイラを誤魔化しているようなものです。当然、classB.hでも同じような処置をしておきます。

第2点
class A(class B)の実装は.cppファイルで別に行っておく。

理由はよくわかりませんが、楽しようとしてheaderファイル内にclassの実装もしてしまうとダメなそうです。(大きなプログラムだと大体そうなっているとは思いますが)

第3点
15行目にあるように、相互参照するclassのメンバーはポインタでしか保持できない。

これもコンパイラの問題で、プロトタイプ宣言で当該classがあるよとは誤魔化しましたが、詳細なタイプの定義まではまだこの時点ではわかっていません。そのため、ポインタであれば実体はプログラム実行中に「後で」なんとかできるので、コンパイラはパスします。
あと、重要なのは相互参照しているclassのメンバの実体化は135, 136行目の様にsetUp()関数を使ってやらないといけません。面倒臭がってclass AやBのコンストラクタでnewしてしまうと、実行時に互いをnewしあい、無限loopに入ります。(PCが暴走状態になり、再起動に苦労しました)

大抵こういう時は、調べる情報源にstackoverflow(英語のほうね)が役に立ちますが、今回はぐぐるキーワードでちょっと苦労しました。

2017年1月7日土曜日

chainerの学習データ、保存と読込

このところ人気なDeep learningです。よくサンプルプログラムがネットに出ていて、試してみては動いて喜んでいますが、基本的にサンプルプログラムは以下の構造をしています。

①modelクラスの定義
②学習データ、試験データの準備
③modelで学習(ここが1時間単位で時間のかかるもの多数)
④学習した結果で、試験データを試し学習結果の確認

それでまあ、④の学習結果が80%以上の正答率が出力され「素晴らしい」で終わってしまいます。でも、実用にしようと思うと通常のサンプルプログラムでは③の学習が外せません。(たまに、学習結果のデータも公開されていて、それよ読み込んで④の結果を出すサンプルもありますが、今度は学習部分の実態がわかりません)

ということで、実用的には③の部分が以下の2つの機能に分かれてほしいのです。

③A 学習した結果(model)をファイルに保存
③B 保存されたデータを読込、試験データで学習結果の確認

以外と、ここをピンポイントで説明したものがなかったので、ここでまとめみました。

⒈pickleを使う
pythonの汎用的なオブジェクトの保存・読込ライブラリです。詳しい説明は省きますが、単純に学習modelオブジェクトをpickleで保存・読込をする方法です。普通に、openしてread/writeします。

⒉chainerの保存機能を使う
これがあるはずなんですが、これまでぐぐっても出てきませんでした。方法は以下の通りです。(ファイル名は、"iris-learn.data"で保存しています。modelはpythonプログラムにおける実際のクラス名です。)

③A
# save                                                                                                                

serializers.save_npz("iris-learn.data", model)

③B
# load                                                                                                                 

serializers.load_npz("iris-learn.data", model)

普通にserializersモジュールを使えばよかっただけでした。(ただ、その詳細例が全然見つかりませんでした)

c++ ログ出力について:stringstream

プログラムの動作ログとしてよく文字列のログファイルを作成します。これまではよくcからのくせで、sprintf()文を使ってきました。
しかしこれって、char*を使っていますから危ないですよね。(メモリ破壊やセキュリティ上も)できれば、c++のbasic_stringを使った方がいいです。そこで、stringstrream型を使います。(それにM$のVisualStudioだと微妙に文法がg++と違い、やたらwarningを出してきます。両方で動作するプログラムを作成するとき嫌な思いをこれまでしてきました。)

改めてサンプルを作ろうと思い調べてみたら、stringstreamにはヘッダとして、strstreamとsstreamの2つがあるとのこと。違いは、strstreamの方が先輩なんですが実装にchar*を使っているとのこと。危険なので、内部の実装をbasic_stringなsstreamを使えとのことです。

まあ面倒なことはおいといて、一目でわかるサンプルを以下に示します。

実行すると以下の様な感じになります。

bash-3.2$ ./sstream
3.14000, 4.14000
123, 123456

まず、文字列をdoubleに変換して+1.0しています。次が数値をstirngsttreamに取り込み、string型でappendしています。これで大概のことはできます。

2016年11月6日日曜日

Doxygenを使ってみる

仕事で長年維持してきたプログラム(C++です)を久しぶりに大改造する機会ができそうなので、ちょっとリバースしたくなりました。(昔つくったもので、ドキュメントがほとんどない!しかも、その時々で人が変わって手をいれてきたので、かなり怪しい構造になってきている可能性があり、維持性に問題がでてきています。

真っ先に確認したいのが、誰からもCallされなくなった関数のチェックです。こういうのが残っていると、後から維持に携わる人も、うっかり触れなくなってしまい余分な手間が増えるだけです。
まあ、この手の機能は静的解析ツールなら大抵は持っていますが、Doxygenでやってみることにしました。(個人的に使ったことはなかったんですよね)

windowsのバイナリをDLするのが簡単そうなんですが、とりあえず慣れているLinuxでやってみます。(調べたら、macでもbrewコマンドでできそうです)

Ubuntuなら、apt-getで簡単にインストールできます。

$ sudo apt-get install doxygen doxygen-gui graphviz

doxygenが本体のパッケージ、doxygen-guiはGUIのユーザーインターフェース、graphvizはコーリングリストのツリーを図で生成するのに必要なパッケージになります。

インストールが終わったら、以下のコマンドで起動。


$ doxywizard


Step1の動作領域がよくわかりませんが、何らかの作業用ファイルを作るエリアなんでしょう。(注:これは設定ファイル:Doxyfileをここに置きます)Project nameとかは適当に入力して、解析したいプログラムのフォルダと、生成したドキュメントを格納するフォルダだけはきちんと入力します。


ここの設定で、コーリングリストを生成してくれます。(これが今回の目的)デフォルトはオフなので注意。
あとは、その他のオプションを設定して、runするだけです。

大層なGUIがついていますが、やってることはDoxyfileの設定をしているだけなので、必要な設定だけテキストエディタで手動でできるなら、そちらのほうが早いかも。以下に、設定ファイルで設定したほうがいい主な項目を挙げておきます。


option
default
description
PROJECT_NAME
"My Project"
クラス図を作成したいプロジェクトの名前
EXTRACT_PRIVATE
NO
privateなメンバ変数・メンバ関数も出力する
EXTRACT_STATIC
NO
staticなメンバ変数・メンバ関数も出力する
EXTRACT_ANON_NSPACES
NO
匿名の名前空間についても出力する
RECURSIVE
NO
ソースコードを再帰的に検索する
HAVE_DOT
NO
YESに設定するとgraphvizなどの描画ツールを使用してクラス図を出力する
DOT_NUM_THREAD
0
スレッド数
CALL_GRAPH
NO
関数やメソッドを呼び出す側の依存関係を描画する
CALLER_GRAPH
NO
関数やメソッドを呼び出される側の依存関係を描画する

面倒くさい人は、解析したプログラムのソースコードのルートに設定ファイルを以下のコマンドで作り、

$ doxygen -g

できたDoxyfileの上記項目を好みで設定して、

$ doxygen

と実行してやれば、解析結果が出力されます。


2016年10月12日水曜日

macにゲストアカウントが表示された!?

macosxが新しく、macos Sierraになり少し様子を見て問題がなさそうなので、自宅のMacBookProを更新しました。更新は特に問題なく進み、インストールしってある既存のアプリにも特に問題がないようです。(実は、更新直前に気づいたのがいつのまにかBlenderが起動しなくなっていました。2年ほど更新サボってたからな〜 Sierraにしてから最新のBlenderにしたら問題なく動きました。)

ところで困ったのが、PowerOn時の画面にゲストアカウントが表示されるようになってしまったことです。これまでそんな表示なかったのにと思い、調べて見たら結構以前のOS更新の時からそういうトラブル(更新したらゲストアカウントが表示されるようになってしまった)があったようです。設定の「ユーザとグループ」でロックを解除して、ゲストログインが許可になっているのを不許可にするだけです。
だけどこれって、いつからか知りませんがゲストアカウントが子供用のペアレンタルコントロールにも使われる様になったようです。

しかし、OSの更新で勝手にゲストをONにしないで欲しいな〜(Appleは何を考えているんだ?)

2016年8月16日火曜日

windows10 アニバーサリーUpdateでbashを試してみる

ちょいと勇気がいりましたが、windows10でanniversary updateを行い(通知が来ないので手動でやりました)、bash on windowsを試してみました。

やり方は各種サイトにありますので割愛しますが、とにかく再起動が多い!なんか懐かしい風景です。(昔はシステム更新なんかだと、こんなんばっかだった)さて肝心の"bash on windows"ですが、なんというかvirtual boxでubuntuが動いているような感じです。それとも、今はやりのコンテナに仕立ててきたのか?そんなに重くはないんですが、問題は文字コードが…
何書いてあるのかわかりません。対策をあちこち知らべましたが、どうも決め手がないようです。まだマシンによって差がでてしまうようです。

ただそれ以外は普通にubuntuが動いています!いや、これは素晴らしい。これができると、これまでプログラム作るとき、macに逃げていたのが、windowsでもやってみるか、という気になります。(それが狙いだという話もありますし)

しかし、GUIはどうするんだろう?APIが全く違うし、windowsのGUI関係のAPIの一部でもこのシステムに取り込むのは大騒ぎになるだろうし。web系のサーバに使ってくれないかな、というM$の考えなんだろうけど、まだTCP/IPのAPIが完全には機能していないらしいし、今後どうなるのかな。(個人的にはCygwinが嫌いなんで、ある程度残って欲しいんですが、かなり今の状況では怪しいです。何か使い道を考えてあげないと)
ただ無理にGUIのAPIを移植しなくてもいいと思います。今、時代はwebですべてすますのが本流なんで、GUIはブラウザにまかせてしまえばいいですから。難しいのは不安定なwindows OSの上で如何に安定して動く環境にできるかですが、こればっかりはかなり難しいかな。

PS
どうもanniversary updateにしたのは早まったようです。ファイルの移動とかでおかしな挙動が見られます。
あと肝心のbashですがどうにも日本語が表示できないので、以下の設定で動かしています。

$ LANG=en_US.UTF-8

要は英語表記でアラートとか出してね、ですね。あと、一体どういう環境になっているのかわからないのですが、コンテナでもないようで、ping、ifconfig等のネットワーク系のコマンドが実行できません。ベータ版とは聞いていますが、どんな実装になってるんだ???

2016年8月12日金曜日

SQLite3のプログラムの勉強

ちょっとSQLite3を使ったプログラムをWindowsで作るとどうなるのか調べてみたくなりました。ただ私は普段LinuxかMacでプログラムを作ることが圧倒的に多いので、まずは慣れたMac、Xcodeで作ってみます。

1. sqlite3のインストール
brewを使ってインストールしています。コマンドラインでも使えますし、ヘッダ、ライブラリも一緒にインストールしてくれます。

2. プログラムの作成
とりあえず、以下の様なプログラムを作ってみました。

とてもシンプルです。実行結果は以下の様になります。

$HOME = /Users/hogehoge
$HOME = /Users/hogehoge/ProgWork/SQLite
OPEN SUCCESS!
id: 123, name: hoge
CLOSE SUCCESS!

createがありませんが、そこはマニュアルで作っています。(プログラムに書いてもいいんですが、毎回実行するためにいちいちdbファイルを削除するのも面倒なので。)

> create table tbl_test(id integer primary key, name text);

最終的にはinsertのところをupdateに変えて、プロセス間の通信代わりに使おうと目論んでいます。

3. 注意点

  • Xcodeの設定でライブラリの追加オプション"-lsqlite"を忘れない様に。
  • 単純にやろうと思うなら、sqlite_open(), sqlite_close(), sqlite_exec()だけで済ますこともできます。ただselect文をsqlite_exec()でやろうとすると、どうしても出力をcallbackで受けないといけなくなります。callback関数を書くのが面倒だったので、非同期クエリで実装しています。(こちらも面倒だがやむを得ません)
  • sqliteはsql文の実行ではデフォルトでオートトランザクションになっています。将来的にプロセス間の通信に使うつもりなら本当は、"bgein","commit"と明確にトランザクションの指定をしてやるべきです。ただそんな複雑な通信は当面する気がなく、一方向だけ(片方がwriteして、他方はreadするだけ)のつもりなので、当面はこれでも問題はありません。
さて、これをwindowsのVisualStudioのc++で実装です。2015ではUWPアプリ(Universal Windows Platform)として、SDKにsqlite3が標準で入っているらしいです。実装もこのままでコンパイルが通りましたが、リンクでどうしてもsqlite関係の関数が「未解決の外部シンボル」と言われてしまいます。(ちゃんとwinsqlite3.libを追加のライブラリに指定しているんですが・・・)ちょいとこの問題の解決には時間がかかっています。