スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

Re:ある「プロ」の書いた、初心者向けc言語入門プログラム

きっかけ

ある「プロ」の書いた、初心者向けc言語入門プログラム: わき道

見てみると、コードの書き方云々の前にアルゴリズムがあかん。テーブル全数探査とか馬鹿げている。

全部書き直した

というわけで書き直した。

使用例。

気になる実装

まずアルゴリズムを変更した。

  1. 母音
  2. 子音+母音
  3. 子音+(y)+母音
  4. 子音+(y/h)+母音

この4つのテーブルに分けて、先頭文字からどのパターン化を判別し、変換するようにした。

元のプログラムではバッファーの大きさを取ってないわ、変換終了時のソースと出力の変換後の位置は取れないわでお話にならないので、strtol関数を参考に書き換えた。

内部ではC++のイテレータのように作業位置のポインタと終端のポインタを持つようにした。下手にindexに変換するとsigned/unsignedが面倒だからだ。

エラー処理は、enumを書いてそれのformatterをつくるというありきたりな実装にした。その気になればstd::error_codeでのラップも簡単だろう。

スポンサーサイト

OpenCV3.1の導入withCMake

以前OpenCV 2.4.9導入-with CMake という記事を書きましたが、それからバージョンアップが続き、ついに3.1がリリースされました。

またコンパイラもVisual Studio 2015がUpdate1でようやく安定して使い物になるようになりました。

というわけで改めてまとめようかと思います。あ、pythonとか使ったこと無いのでいい加減知識です、当てにしないように。

Visual Studio Update1は入っている前提で話をすすめます。まだの人は
Clang with Microsoft CodeGenがでたので試す - Qiita
C++を始めよう for windows - Qiita
あたりみてがんばってください。

OpenCVとは

ざっというと画像処理に関するプログラミングを超強力にアシストしてくれるライブラリーです。詳細は
http://www.buildinsider.net/small/opencv/01
に譲ります。

目標

  • CMakeを使ってWin32、x64両対応できるように導入する
  • pythonでも使えるようにする
  • TBB, Eigenをなんとなく有効にする

環境

項目 内容
OpenCVバージョン OpenCV 3.1
Visual Studio Visual Studio 2015 Community Update1
ビルド構成 Win32 or x64 | Debug or Release
OS Windows 7 Homr Premium(64bit)

(任意)pythonの導入

pythonは32bit版と64bit版がありますが、64bit版は罠です。32bit版を使いましょう

64bit版にはnumpyとか無いんです。非公式版はあるけど私の環境では動かなかったし。

  1. https://www.python.org/downloads/
    にアクセスし、
    Windows x86 MSI installerWindows debug information files
    の2つを落とす。今回はpython3.4.4にした。もう一度言うけどx86(32bit)を選ぶんやぞ!たとえマシンが64bit、OSが64bitだとしても!
  2. python-[version番号].msiをダブルクリックして実行する。
    Add python.exe to Path
    は有効にしたほうがいいと思う。パスは空白パスとか日本語パス避ければどこでもいいと思うけど自分はデフォルトパスにした。
  3. 適当にこの辺を管理者権限コマンドプロンプトで実行する。必要かどうかは知らない。
    pip install pip --upgrade
    pip install python-dateutil
    pip install pyparsing
    pip install wheel
  4. http://sourceforge.net/projects/numpy/files/NumPy/
    にアクセスし、最新版(今回は1.10.2)をクリック、numpy-[numpyのversion番号]-win32-superpack-python[pythonのversion番号].exeを落とす。
    今回はnumpy-1.10.2-win32-superpack-python3.4.exeを落とした。 そのままの勢いでexeを実行する。なぜpipで入れないかというとどうせ失敗するから。
  5. (任意)http://sourceforge.net/projects/scipy/files/scipy/
    にアクセスし、最新版(今回は0.16.1)をクリック、scipy-[scipyのversion番号]-win32-superpack-python[pythonのversion番号].exeを落とす。
    今回はscipy-0.16.1-win32-superpack-python3.4.exeを落とした。 そのままの勢いでexeを実行する。なぜpipで入れないかというと(ry
  6. (任意)http://sourceforge.net/projects/matplotlib/files/matplotlib/
    にアクセスし、最新版(今回は1.5.0)をクリック、さらにWindowsをクリックして、matplotlib-[matplotlibのversion番号]-cp34-none-win32.whlを落とす。
    今回はmatplotlib-1.5.0-cp34-none-win32.whlを落とした。
  7. (任意)管理者権限コマンドプロンプトで以下のように実行する。
    cd [落とした場所]
    pip install matplotlib-[matplotlibのversion番号]-cp34-none-win32.whl
  8. 最初に落としたpython-[version番号]-pdb.zipを解凍し、python.exeがあるフォルダに新たに「symbols」とかフォルダ作ってそこに中身を全部入れる

pythion3 Debug Buildについて

なおここまでやって作れるのは32bitのRelease buildだけです。Debugビルドは

fatal error LNK1104: cannot open file 'python34_d.lib'

と怒られます。かと言って自力でpythonをビルドするのは至難の業でして、諦めるが吉でしょう。
頑張りたい人は
http://p-nand-q.com/python/building-python-27-with-visual_studio.html
http://stackoverflow.com/questions/17028576/using-python-3-3-in-c-python33-d-lib-not-found
を参考に。私はやりたくない。

CUDA

CUDAを使うにはCUDA Toolkit 7.5を落とすわけですが、これについてくるNsight Visual Studio Edition、Visual Studio 2015に対応してないんですよね・・・。はよう8.0こい。

doxygen

doxygenといえばJavaでいうJavaDocに相当し、ドキュメント生成で有名なツールですね。OpenCVのdocumentはオンラインでも読めますが、オフラインで読めたほうがいいので導入しましょう

  1. http://www.doxygen.jp/
    にアクセスする
  2. ダウンロードを押す
  3. A binary distribution for Windowsの64-bit versionのftpまたはhttpをクリックしてDLします
    doxy
  4. 適当な場所に解凍します。今回はD:\Program1に
  5. doxygen.exeのあるディレクトリにPATHを通す。

さすがにPATHの通し方なんて書かなくても大丈夫だよね・・・?コマンドプロンプトで

doxygen --version

として落としたバージョンが表示されれば成功です。

DL & 解凍編

Eigen

全部解凍する。CMake以外は同じパスにおいたほうが便利じゃないかな。ってことでC:\lib以下においています。

CMake編(32bit)

以下説明の都合上、

OpenCV C:\lib\opencv-3.1.0
opencv_contrib C:\lib\opencv_contrib-3.1.0
Eigen C:\lib\eigen-eigen-b30b87236a1b
Intel(R) TBB C:\lib\tbb44_20151115oss
cmake-gui.exe D:\Program1\cmake-3.4.1-win32-x86\bin
python.exe C:\Python34

にあるとします。適宜パスはよみかえてください。

  1. cmake-gui.exeを起動する
  2. 以下のように入力し、configureをclickする
    Where is the source code C:\lib\opencv-3.1.0
    Where to build the binaries C:\lib\opencv-3.1.0\build
    1
  3. フォルダを作成するか聞いてきたらyesと答える
    2
  4. とりあえず32bit向けのbuildがしたいので、「Visual Studio 14 2015」を選ぶ。64bit向けには、「Visual Studio 14 2015 Win64」を。
    3
  5. しばらく待つと、エラーが出るが、気にしない。OKを押す
    4 5
  6. searchにexamと打つと「BUILD_EXAMPLES」という項目があるので、チェックする
    6
  7. WITH_CUDAは必ずOFFにする!理由は前述のとおり。
    7
  8. searchにextと打つと「OPENCV_EXTRA_MODULE_PATH」があるので、
    C:\lib\opencv_contrib-3.1.0\modules
    とうち、下のサジェストをクリックする。こうすることで'\'(バックスラッシュ)が'/'に置き換わる。
    8
  9. searchにtbbと打つと、「WITH_TBB」があるので、チェックを付ける
    9
  10. searchにeiと打つと、「Eigen_INCLUDE_PATH」があるので、
    C:\lib\eigen-eigen-b30b87236a1b
    とうち、下のサジェストをクリックする。こうすることで'\'(バックスラッシュ)が'/'に置き換わる。
    10
  11. searchにpyと打つとpython関連の設定項目が出てくる。以下のように設定する。
    BUILD_opencv_python2 チェックしない
    BUILD_opencv_python3 チェックする
    INSTALL_PYTHON_EXAMPLES チェックする
    PYTHON3_LIBRARY_DEBUG C:/Python34/symbols
    11
  12. configureをクリックする
  13. なんかエラーが出るのでOKする
    12
  14. TBB_INCLUDE_DIRSに
    C:\lib\tbb44_20151115oss\include
    とうち、下のサジェストをクリックする。こうすることで'\'(バックスラッシュ)が'/'に置き換わる。
    そしてconfigureをクリックする
    13
  15. searchにtbbと打つと「TBB_LIB_DIR」があるので、
    C:\lib\tbb44_20151115oss\lib\ia32\vc14
    とうち、下のサジェストをクリックする。こうすることで'\'(バックスラッシュ)が'/'に置き換わる。
    ia32のところは、64bitビルドの時はintel64にするように!
    そしてconfigureをクリックする
    14
  16. generateをクリックする
    15

Build編(32bit)

  1. C:\lib\opencv-3.1.0\build
    にあるOpenCV.slnを開く
    sln
  2. 下のステータスバーに「準備完了」と出るまでしばらく待つ。私はIntelliSenseの解析は終わらせてからやる派です(ただの趣味)。
    vs_ready
  3. ビルド→バッチビルドをクリックする
    bat_build1
  4. INSTALLを探し、Debug/Releaseともにチェクをつけ、ビルドを押す。
    bat_build2
  5. 前述のとおり、opencv_python3のDebug buildに失敗して終了する。
    python3_x86debug_fail

CMake編(64bit)

CMakeは前回の時のcacheが残るので、fileからdelete cacheする

基本的にはCMake編(32bit)と同じだけど、pythonは64bitでビルドする意味がわからないので(numpyの関係上)、
BUILD_opencv_python3とINSTALL_PYTHON_EXAMPLES
のチェックを外す。

TBB_LIB_DIRの設定は、ia32のところは、64bitビルドの時はintel64にするのをお忘れなくように!

Build編(64bit)

Build編(32bit)と全く同じなので割愛

環境変数編

なくてもいいんだけど、あったほうが楽なので。

OPNECV_ROOTに、C:\lib\opencv-3.1.0\build\installを設定。

dllとかとか配置編

一般にはPATHを通すようですが、今回はそうしません。

コピー元 コピー先
C:\lib\opencv-3.1.0\build\install\x86\vc14\bin C:\Windows\SysWOW64
C:\lib\tbb44_20151115oss\bin\ia32\vc14 C:\Windows\SysWOW64
C:\lib\opencv-3.1.0\build\install\x64\vc14\bin C:\Windows\System32
C:\lib\tbb44_20151115oss\bin\intel64\vc14 C:\Windows\System32

こんな感じでコピーしてくだい。dllだけでいいですが、面倒なら全部いれてかまいません。間違えやすいので注意!

python版のインストール

まず先ほどまでの手順で
C:\lib\opencv-3.1.0\build\lib\python3\Release
cv2.pydがあるはずなので確認してください。

さらに、
C:\Python34\Lib\site-packages
をみると、すでにcv2.pydがコピーされています。マジ有能。

コピー元 コピー先
C:\lib\opencv-3.1.0\build\install\x86\vc14\bin C:\Python34\Lib\site-packages
C:\lib\tbb44_20151115oss\bin\ia32\vc14 C:\Python34\Lib\site-packages

こんな感じでコピーしてくだい。dllだけでいいです

python版の確認

pythonとコマンドプロンプトに打ってインタープリタを立ち上げ、import cv2とか、cv2.__version__とかしてあげてください。

C:\Users\yumetodo>python
Python 3.4.4 (v3.4.4:737efcadf5a6, Dec 20 2015, 19:28:18) [MSC v.1600 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import cv2
>>> cv2.__version__
'3.1.0'
>>> exit()

あとは、C:\lib\opencv-3.1.0\build\install\samples\native\pythonにある、browse.pyとかを走らせても楽しそうです。
previwウィンドウでマウスを動かすとマウス周辺の拡大図がzoomウィンドウに表示されます。

Visual Studioのプロジェクト設定&Sample実行編

  1. 構成:すべての構成
    プラットフォーム:すべてのプラットフォーム
    の状態で
    構成プロパティ→C/C++→全般→追加のインクルードディレクトリに
    $(OPENCV_ROOT)/include
    を追加、適用を押す
  2. 構成:すべての構成
    プラットフォーム:すべてのプラットフォーム
    の状態で
    構成プロパティ→リンカー→全般→追加のライブラリ ディレクトリに
    $(OPENCV_ROOT)/$(PlatformTarget)/vc14/lib
    を追加、適用を押す
  3. 構成:すべての構成
    プラットフォーム:すべてのプラットフォーム
    の状態で
    構成プロパティ→C/C++ー→コード生成→C++の例外を有効にするで
    はい-SEHの例外あり (/EHa)
    を選択(OpenCVのビルド時と揃える、つまりCMAKEのCMAKE_CXX_FLAGSと同じにする意味がある)
    構成プロパティ→C/C++ー→コード生成→浮動小数点モデルで
    Fast (/fp:fast)
    を選択、適用を押す
  4. 構成:Debug
    プラットフォーム:すべてのプラットフォーム
    の状態で
    構成プロパティ→リンカー→入力→追加の依存ファイル→入力欄クリック→右側の下矢印をクリック→編集で
    opencv_core310d.lib
    opencv_highgui310d.lib
    opencv_imgproc310d.lib
    opencv_objdetect310d.lib
    を追加する。バージョン番号は適当に読み替えてください。
  5. 構成:Release
    プラットフォーム:すべてのプラットフォーム
    の状態で
    構成プロパティ→リンカー→入力→追加の依存ファイル→入力欄クリック→右側の下矢印をクリック→編集で
    opencv_core310.lib
    opencv_highgui310.lib
    opencv_imgproc310.lib
    opencv_objdetect310.lib
    を追加する。バージョン番号は適当に読み替えてください。

という感じで設定したら、適当にサンプルをビルドしてみましょう。

#include <opencv2/core/core.hpp>       // coreモジュールのヘッダーをインクルード
#include <opencv2/highgui/highgui.hpp>  // highguiモジュールのヘッダーをインクルード
#include <iostream>
 
int main(int argc, const char* argv[])
{
  // 1幅320px、高さ240pxで赤色の画像データを生成
  cv::Mat redImg(cv::Size(320, 240), CV_8UC3, cv::Scalar(0, 0, 255));
 
  // 2画像表示用のウィンドウを生成
  cv::namedWindow("red", cv::WINDOW_AUTOSIZE);
 
  // 3ウィンドウに画像を表示
  cv::imshow("red", redImg);
 
  // 4キー入力を待機
  cv::waitKey(0);
 
  // 5作成したウィンドウを全て破棄
  cv::destroyAllWindows();
 
  return 0;
}

テーマ : プログラミング
ジャンル : コンピュータ

Visual Studio 2015でconstexpr delegating constructorが通らない

初めに

Update 1でfixされました。やったね。

みなさまナマステ。ええっとまあタイトルのとおりです。
delegating constructorもconstexprもC++11の機能ですが(C++14のconstexpr関数の制限緩和は今回関係ない)
constexprコンストラクタから他のconstexprコンストラクタを呼ぶとエラーになります。
どういうことかコードを見ましょう。

問題のコード

DxGraphicHandle.cpp

struct YPbPr {//ITU-R BT.709 cf.)http://koujinz.cocolog-nifty.com/blog/2009/03/rgbycbcr-a4a5.html
	YPbPr() = default;
	constexpr YPbPr(uint8_t i_y, uint8_t i_pb, uint8_t i_pr) : y(i_y), pb(i_pb), pr(i_pr) {}
	explicit constexpr YPbPr(uint8_t i_y) : YPbPr(i_y, 0, 0){}//C++11:delegating constructor
	uint8_t y, pb, pr;
};
dxgraphichandle.cpp(213): error C3249: 'constexpr' 関数のステートメントまはたサブ式が誤っています。
dxgraphichandle.cpp(213): error C2476: コンストラクター 'constexpr' はすべてのメンバーを初期化できません。
dxgraphichandle.cpp(214): note: 'detail::YPbPr::y' はコンストラクターによって初期化されませんでした
dxgraphichandle.cpp(214): note: 'detail::YPbPr::pb' はコンストラクターによって初期化されませんでした
dxgraphichandle.cpp(214): note: 'detail::YPbPr::pr' はコンストラクターによって初期化されませんでした

あ、detail名前空間に書いてます。
で調べてみるとかつてgcc4.7.0でも同じようなことがあったようです。
Bug 51526 - [C++11][constexpr] constexpr delegating constructor should be accepted

結論

VSのバグじゃね?どうやってバグ報告投げるんだろ。

解決策

今回はdelegating constructor使わなくても書けるので使わなければ問題無いです。

struct YPbPr {//ITU-R BT.709 cf.)http://koujinz.cocolog-nifty.com/blog/2009/03/rgbycbcr-a4a5.html
	YPbPr() = default;
	constexpr YPbPr(uint8_t i_y, uint8_t i_pb, uint8_t i_pr) : y(i_y), pb(i_pb), pr(i_pr) {}
	explicit constexpr YPbPr(uint8_t i_y) : y(i_y), pb(0), pr(0) {}
	uint8_t y, pb, pr;
};

その後

どうもすでにMSにバグ報告が何件かいっているようなんですが

MSはfixしたと言っています。つまり次のバージョン(Visual Studio 2015 Update1??)では治っているということ?現時点では打つ手なし?

テーマ : プログラミング
ジャンル : コンピュータ

Boost1.59.0をなんとなく導入する

皆様、ナマステ。
いやね、私は確かにmsxmlのラッパーを書いていたはずなんですよ。
そしたらいつの間にかBoostを導入していました。使う予定も無いのに。
きっとmsxmlのラッパーが何故にや知らんcmp命令で落ちるので
xmlの読み込み途中で落ちる · Issue #2 · yumetodo/xml_test_cooking_quiz
やけになったんでしょう、きっと。
あと、Sprout C++ Librariesを面白半分で落として、さらに無意味にcmakeしようとしたらboost必要、みたいなエラーが出たせいでもあります。

といっても導入方法は
近代汎用術式Boost C++ 1.57.0をVisual Studio 2013で使う ~導入と使い方~ - Gobble up pudding
と一切変わりません。boostとVisual Studioのバージョンが違うくらいです。

導入手順(超省略版)

  1. まずはBoostを公式サイトから落とす
    http://www.boost.org/
    ここね。入れたのはバージョン1.59.0。
    落とすときにいくつかの形式を選べるけど7zで落とした。
  2. 解凍(展開)する。私はCubeICEを使うぜ。
    そういえば解凍中に幾つかエラー出てたんだようなぁ、何だったんだろう。
  3. Visual Studio 2015の開発者コマンドプロンプトを立ち上げる。
  4. pushd C:\lib\boost_1_59_0
    bootstrap.bat && b2.exe -j 4
  5. 気長に待つ。え、カップ麺でも食べたかって?
    これみてたに決まってますよね。
  6. 環境変数を設定する。某友人(同じサークルの人)はphp開発環境導入の時に忘れて痛い目を見たらしいので、侮る事なかれ。

導入確認テスト

参考サイトではboost.regex使ったサンプルをビルドしているようですが、regexならC++11にありますしちっともありがたくないです。
というわけでC++17に入る・・・かどうかは知らないけど私の中ではBoostと言ったら
BoostAsioで可読性を求めるのは間違っているだろうか
from Yuki Miyatake

Boost.asioなんですよね~。
なんでboost.asioなのかというと、このうえのスライド、ドワンゴの「歌舞伎座.tech #8 「C++初心者会」」で発表されたもので、これの司会は日本一C++規格に詳しい江添亮さん。
全国のC++erのみなさん、この生放送のTS録画は義務ですよ(もうTS期限切れてるけど)
で、使いたい、と思ったら、
./boost_1_59_0/libs/asio/example/cpp11/buffers
というサンプルが元からついてきている。
しかもそれの解説サイトもある
C++ - 【boost::asio buffers】 boost::asioでセッション管理にはshared_ptrが便利だ - Qiita
じゃあこれで試すか。

ご丁寧にCMakeLists.txtを配布してくれてるのでそれをいじってビルドすることにする

  1. CMakeLists.txtを上のコードの「view raw」を右クリックから落として、reference_counted.cppと同じパスに置く
  2. フツーにCMake。悪いけどVisual StudioでかつWindows7以上前提で書いたからそれ以外の人は適宜直してくれ
  3. ソリューション(.sln)を開いたら、 ./boost_1_59_0/boost/asio/detail/config.hpp
    を開く。
    #if !defined(BOOST_ASIO_ERROR_CATEGORY_NOEXCEPT)
    # if (BOOST_VERSION >= 105300)
    #  define BOOST_ASIO_ERROR_CATEGORY_NOEXCEPT BOOST_NOEXCEPT
    # elif defined(__clang__)
    #  if __has_feature(__cxx_noexcept__)
    #   define BOOST_ASIO_ERROR_CATEGORY_NOEXCEPT noexcept(true)
    #  endif // __has_feature(__cxx_noexcept__)
    # elif defined(__GNUC__)
    #  if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4)
    #   if defined(__GXX_EXPERIMENTAL_CXX0X__)
    #     define BOOST_ASIO_ERROR_CATEGORY_NOEXCEPT noexcept(true)
    #   endif // defined(__GXX_EXPERIMENTAL_CXX0X__)
    #  endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4)
    # endif // defined(__GNUC__)
    # if defined(BOOST_ASIO_MSVC)
    #  if (_MSC_VER >= 1900)
    #   define BOOST_ASIO_ERROR_CATEGORY_NOEXCEPT noexcept(true)
    #  endif // (_MSC_VER >= 1900)
    # endif // defined(BOOST_ASIO_MSVC)
    # if !defined(BOOST_ASIO_ERROR_CATEGORY_NOEXCEPT)
    #  define BOOST_ASIO_ERROR_CATEGORY_NOEXCEPT
    # endif // !defined(BOOST_ASIO_ERROR_CATEGORY_NOEXCEPT)
    #endif // !defined(BOOST_ASIO_ERROR_CATEGORY_NOEXCEPT)
    これだと二重定義になるので
    #if !defined(BOOST_ASIO_ERROR_CATEGORY_NOEXCEPT)
    # if (BOOST_VERSION >= 105300)
    #  define BOOST_ASIO_ERROR_CATEGORY_NOEXCEPT BOOST_NOEXCEPT
    # elif defined(__clang__)
    #  if __has_feature(__cxx_noexcept__)
    #   define BOOST_ASIO_ERROR_CATEGORY_NOEXCEPT noexcept(true)
    #  endif // __has_feature(__cxx_noexcept__)
    # elif defined(__GNUC__)
    #  if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4)
    #   if defined(__GXX_EXPERIMENTAL_CXX0X__)
    #     define BOOST_ASIO_ERROR_CATEGORY_NOEXCEPT noexcept(true)
    #   endif // defined(__GXX_EXPERIMENTAL_CXX0X__)
    #  endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4)
    # elif defined(BOOST_ASIO_MSVC)
    #  if (_MSC_VER >= 1900)
    #   define BOOST_ASIO_ERROR_CATEGORY_NOEXCEPT noexcept(true)
    #  endif // (_MSC_VER >= 1900)
    # endif // defined(BOOST_ASIO_MSVC)
    # if !defined(BOOST_ASIO_ERROR_CATEGORY_NOEXCEPT)
    #  define BOOST_ASIO_ERROR_CATEGORY_NOEXCEPT
    # endif // !defined(BOOST_ASIO_ERROR_CATEGORY_NOEXCEPT)
    #endif // !defined(BOOST_ASIO_ERROR_CATEGORY_NOEXCEPT)
    に書き換える。Visual Studio 2015はnoexceptに対応してるからね。これで213行目の定義だけになるはず。
  4. もう一箇所。ctime関数はstatic領域へのポインタを返すから危険というわけで_s付き関数使えというエラー対策。おのれPOSIX
      void do_write()
      {
        std::time_t now = std::time(0);
        shared_const_buffer buffer(std::ctime(&now));
    
        auto self(shared_from_this());
        boost::asio::async_write(socket_, buffer,
            [this, self](boost::system::error_code /*ec*/, std::size_t /*length*/)
            {
            });
      }
    
      void do_write()
      {
        const auto now = std::time(0);
    #ifdef _MSC_VER
    	char buf[128];
    	ctime_s(buf, _countof(buf), &now);
    	shared_const_buffer buffer(buf);
    #else
        shared_const_buffer buffer(std::ctime(&now));
    #endif
    
        auto self(shared_from_this());
        boost::asio::async_write(socket_, buffer,
            [this, self](boost::system::error_code /*ec*/, std::size_t /*length*/)
            {
            });
      }

実行ファイルは、reference_counted.cppと同じパスにできるbinフォルダ内にあるのでそれをコマンドプロンプトでそのパスに移ってから

reference_counted.exe 8000

のように実行すると簡易サーバーが立ち上がる。ブラウザから、
http://localhost:8000
にアクセスして見ると現在時刻が表示されると思う。初めてHello World!をCで書いたとき並に私は感動したよ。
あ、ちなみ、Windows7標準機能のlocalhostと喧嘩したのか、ブラウザでlocalhostをしょっちゅう見失ってたことを付け加えておきます。というかFirefoxと相性悪い・・・?

msys2でもboostしたい

導入は至って簡単です。すでにgcc/clangは入っているものとします。

pacman -S mingw-w64-i686-boost mingw-w64-x86_64-boost

これで導入完了。exampleは別途落とさないといけないので上記参考に落としてきます。とくにファイルに修正はいりません。cmakeも不要。適当なディレクトリにreference_counted.cppをコピーしてcdしてそこに移りましょう。

g++ -std=c++14 reference_counted.cpp -lboost_system-mt -lboost_thread-mt -lboost_serialization-mt -lws2_32 -lwsock32 -o reference_counted.exe

これだけ。あとは

./reference_counted.exe 8000

として実行してみてください。あとは同じ。

テーマ : プログラミング
ジャンル : コンピュータ

深夜テンションでC言語プログラムを解説するPart 1

きっかけは、Mincraftの魔術mod、Witcherryの解説動画といえばこの人、
まとらさんの
http://www.nicovideo.jp/user/17540909
http://com.nicovideo.jp/community/co2082219
のこのツイート。

ちょうど一般化学実験が終わり、意気揚々と中央線に運良く座りながら帰っていた私は、意気揚々とノートPCを立ち上げ、C89版とC++14版の回答プログラムを書いた。
そうしたら

という返信が来た。もちろんマトラさんみたいなComputerCraftに触れる人ならすぐに上達するだろうから私の解説は不要だろう。
しかし今私はサークルでC言語を教えている。せっかく飛び込んできた材料を無駄にする手はない。
というわけで1/3くらい下書きのつもりで解説をしていく。
残り2/3は?知らん。

プログラミングでもっとも大切なこと

これに関してはMineCraft1.2.5工業化勢最高峰のめいさん
http://www.nicovideo.jp/user/20215125
http://com.nicovideo.jp/community/co2061778
http://dic.nicovideo.jp/a/めい(ゆっくり実況主)
がとてもためになる言葉を、意図してかはともかく、述べている。それは

めんd

である。利用できるものは利用し、コピペできるところはコピペで済ます。どこまでも面倒臭がって初めて良いプログラムはできる、ということだ。ああ、耳が痛い、なんでだろうなぁ。なぜならば

プログラム 書かなければ バグらない

からだ。さて本題に入ろうか。

マトラ氏作のプログラム

まずは問題のプログラムを見てみよう。

#include <stdio.h>
#include <string.h>

int main(void)
{
	int kingaku[50];
	char shouhin[50][10], work[10];
	int i, a, b, count = 0;
	char i;

	printf("データ(商品コード、 金額)を入力してください\n");
	while(scanf("%s %d", &i, &j) != EOF){
		shouhin[count][0] = i;
		kingaku[count] = j;
		count++
	}
	for(a = 0; a < count; a++){
		for(b = a + 1; b < count - 1; b++){
			if(shouhin[a][0] > shouhin[b][0]){
				work[0]=shouhin[a][0];
				j=kingaku[a];
				shouhin[b][0] = work[0];
				kingaku[b] = j;
			}
		}
	}
	for(j = 0; j < 50; j++){
		printf("%s %d\n", shouhin[j][0], kingaku[j]);
	}
	return 0;
}

で、中央線に乗っていた過去の私は何を思ったのか、配列の中身を逆順に並び替えるプログラムと勘違いしてこんな
http://ideone.com/jjo8aC
http://ideone.com/aTGu2f
プログラムを書いていたが、思うにマトラさんのやりたいことはそうではない。
ここでこのプログラムの要件をまとめる

入力を受ける
12~16行目が該当
ソート
バブルソートを使用。判断基準は文字列データ。17~26行目が該当。
画面表示
27~29行目が該当

「ようはstd::reverseがしたいんだろ?」
と思っていた過去の私、違いますよ?
マトラさんがしたいのはバブルソートですよ?マトラさんがびっくりしていますよ?
std::reverseじゃないですよ?
しかし編集中の私の声はどうあがいても届かない。

マトラ氏作のプログラムの問題点

変数名がローマ字だったりわかりにくい

冗談抜きで著しく可読性を下げる。変数名が長くても実行速度には影響しないのだから、英語で長くわかりやすく書けばいいのである

即値がおおい

そこかしこに即値、すなわち直接数字(整数リテラル)が書いてある。これまた可読性を下げ、後の修正可能性を下げるので避ける。
今回は配列の宣言に使われているので変数にするわけにいかないので#defineマクロを利用する。C++11ならconstexpr使うんだけどね。
とりあえずこんな感じ。ついでなのでちと数を増やしといた。

#define INPUT_LIMITS 100
#define INPUT_STR_LIMITS 50

ソートさせるデータが2つある

ソート部分の可読性を大きく下げているのは変数名'shouhin'と'kingaku'が常にひとまとめに扱われているにもかかわらず、2つの変数にわかれていることである。
解決策は簡単だ、構造体を使えばいい。

typedef struct{
	char name[INPUT_STR_LIMITS];
	int price;
} record_data_t;

ソート部分がmain関数内にある

今回は31行に収まっていますが、可能な限り関数化したほうが可読性が上がります。
でC言語にはqsortというクイックソートする標準関数があるのでそれを参考に関数を作りましょう。といっても今回はわかりやすさを優先し、第3引数は省略します。
第1引数でポインタ型がなにから派生したかがわかれば必要ありませんからね。

void bubblesort(void *base, size_t num, size_t size, int (*compare)(const void*, const void*)){
	/* 中略 */
}

scanf関数の使い方が混乱している

	while(scanf("%s %d", &i, &j) != EOF){

12行目を見てください。ここでiの型は何でしょうか?

	char i;

scanfの型変換指定子は'%s'になっていますから、文字列を受けたかったのだと思いますが、char型で受けられるのは文字であって文字列では無いですよ?

そもそもscanf系関数で数値入力を受けてはいけない

これに関しては
[迷信] scanf ではバッファオーバーランを防げない
http://www.kijineko.co.jp/tech/superstitions/buffer-overrun-of-scanf.html
に投げます。
ざっというと、もしint型で表せない範囲の数値が入力されてもscanfでは検知する方法がない、という、いわゆるオーバーフロー、アンダーフロー問題です。
今回はこの記事を参考にpueseIntという関数を作成しています。

int purseInt(char* str){
	long t;
	errno = 0;
	t = strtol(str, nullptr, 10);
	if (0 != errno || t < INT_MIN || INT_MAX < t){
		return -1;
	}
	return (int)t;
}

printf関数が謎

		printf("%s %d\n", shouhin[j][0], kingaku[j]);

28行目に注目してください。
型変換子は'%s'なのでchar*型を受けないといけません。しかし'shouhin[j][0]'と指定しています。それ、char型ですよ?
ちなみに'shouhin[j]'としても、'char[INPUT_STR_LIMITS]'型じゃないか、と思うかもしれませんが、配列は式中では、sizeof演算子と&演算子(アドレス演算子)のオペランドと配列初期化時の文字列リテラルと(lvalue)参照に代入するときは以外は常にポインタ型に読み替えられます。だからchar*型に読み替えられます。

C89規格に沿って書いたプログラムと問題点

という感じで修正してみたのが以下のプログラムです。

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>//in gcc
#include <limits.h>//in gcc
#include <string.h>
#define INPUT_LIMITS 100
#define INPUT_STR_LIMITS 50

#ifndef __cplusplus
#define nullptr NULL
#endif
typedef struct{
	char name[INPUT_STR_LIMITS];
	int price;
} record_data_t;
int purseInt(char* str){
	long t;
	errno = 0;
	t = strtol(str, nullptr, 10);
	if (0 != errno || t < INT_MIN || INT_MAX < t){
		return -1;
	}
	return (int)t;
}
void swap_record_data_t(record_data_t* a, record_data_t* b){
	record_data_t tmp;
	tmp = *a;/* 構造体だからこそ'='でコピーが出来る。 */
	*a = *b;
	*b = tmp;
}
int compare_record_data_t(void* a, void* b){
	int tmp;
	tmp = strcmp(((record_data_t*)(a))->name, ((record_data_t*)(b))->name);
	return (tmp) ? tmp : ((record_data_t*)(a))->price - ((record_data_t*)(b))->price;
}
void bubble_sort(record_data_t *base, size_t num, int (*compare)(const void*, const void*)){
	int i, j, temp;
    for (i = 0; i < num - 1; i++) {
        for (j = num - 1; j > i; j--) {
            if (0 < compare(&base[j - 1], &base[j])) {
        		swap_record_data_t(&base[j - 1], &base[j]);
            }
        }
   	}
}
int main(void){
	int i, j, inputed_num;
	char buf[INPUT_STR_LIMITS] = { 0 };/* 念のため初期化 */
	char buf_to_num[INPUT_STR_LIMITS] = { 0 };/* 念のため初期化 */
	record_data_t shouhin[INPUT_LIMITS] = { 0 };/* 念のため初期化 */
	/* input data */
	for(i = 0; i < INPUT_LIMITS && NULL != fgets(buf, INPUT_STR_LIMITS, stdin); i++){
		sscanf(buf, "%s %s", shouhin[i].name, buf_to_num);/* 数値も一度文字列として読み込む */
		shouhin[i].price = purseInt(buf_to_num);/* 整数型(int)に変換 */
	}
	/* この時iは入力したデータ数(1起算)になっている。配列は0から始まることに注意 */
	inputed_num = i;
	/* バブルソート */
	bubble_sort(shouhin, inputed_num, compare_record_data_t);
	/* クイックソートを利用するときはこっち */
	/* qsort(shouhin, inputed_num, sizeof(record_data_t),compare_record_data_t); */

	/* print out result */
	for(i = 0; i  INPUT_LIMITS && i < inputed_num; i++){
		printf("%s %d\n", shouhin[i].name, shouhin[i].price);
	}
	return 0;
}

関数ポインタについて大急ぎで補足します。
変数に型があるのはC/C++erの常識ですが、この型の役割は何でしょうか?
ずばり、メモリー上にどのようにデータを置くか、ということを示しています。
関数も変数と同じように型が存在します。どういうことでしょう?
関数は呼び出す際にreturn値を書き込む場所、引数をスタックに積みます。そうです、どのように積むかが関数の型に当たるわけです。
例えば'compare_record_data_t'という関数の型は'int ()(void*, void*)'です(関数呼び出し規約についてはここでは無視します)。
で、これへのポインタ型なので'int (*)(void*, void*)'型となるわけです。

ただこれにもいくつか問題点があります。

swap関数が遅い
まあソートアルゴリズムがバブルソートなので速度にこだわっても仕方ないですが、これは遅すぎます。
ソースコードにも書いていますがこれは構造体のコピー機能を利用しています。なので文字列もすべて精密にコピーされます。
お陰でシンプルにわかりやすくかけているのですが、メモリーアクセスが多すぎます。
compare関数が関数ポインタ経由呼び出し
関数ポインタ経由の呼び出しだとコンパイラーは関数をinline展開しにくくなり、コードが遅くなりがちです。
入力できるデーター数が固定(コンパイル時定数)
C99ならいざしらず、C89には可変長配列なんて物はありません。実行時に配列の大きさを決定するには動的確保する必要があります。
具体的には文字列は1文字ずつ読み込みreallocする処理を書くことになります
が、はっきりいって骨が折れます、心折設計とはまさにこのためにあるような言葉です。
ソートアルゴリズムが良くない
バブルソートはシンプルでわかりやすいのですが、あまり速くありません。
今回は学習用だろうと思いあえてそのままにしましたが、'stdlib.h'にあるqsort関数を使うべきです。(一応コメントでその方法を書いている)

C++14で書いてみる

ざっと以下の機能を使います。

  • クラス
  • コンストラクタの継承
  • コピーコンストラクタ
  • ムーブコンストラクタ
  • lvalue参照とrvalue参照
  • std::move
  • std::string
  • std::vector
  • std::cout, std::cerr
  • std::exception
  • std::runtime_error
  • Range-base for
  • 型推論(auto)
#include <iostream>
#include <string>
#include <vector>
#include <stdexcept>
#include <algorithm>
class record_data_t {
public:
	record_data_t() : name() {}
	record_data_t(const std::string& n, const int& p) : name(n) {//copy コンストラクタ
		this->price = p;
	}
	record_data_t& operator=(const record_data_t& r){
		this->name = r.name;
		this->price = r.price;
		return *this;
	}
	friend inline bool operator< (const record_data_t& l, const record_data_t& r);
	friend inline std::string to_string(const record_data_t&);
private:
	std::string name;
	int price;
};
inline bool operator< (const record_data_t& l, const record_data_t& r) {//ソートの基準となる、friend関数
	return (l.name < r.name) ? true : (l.name == r.name) ? l.price < r.price : false;
}
inline std::string to_string(const record_data_t& in) {//friend関数。operator<<をオーバーロードするのはめんdので逃げる。
	return (in.name + " " + std::to_string(in.price));
}
auto split(const std::string &str, char delim) {
	std::vector<std::string> res;
	size_t current = 0, found;
	while (std::string::npos != (found = str.find_first_of(delim, current))) {
		res.push_back(std::string(str, current, found - current));
		current = found + 1;
	}
	res.push_back(std::string(str, current, str.size() - current));
	return res;
}
auto convert_from_input(const std::string & line) {
	const auto vec = split(line, ' ');//空白文字で区切る
	if (2 != vec.size()) throw std::runtime_error("unknown input");
	return record_data_t(vec.at(0), std::stoi(vec.at(1)));//std::vectorからrecord_data_tに変換するためにコピーコンストラクタ(10行目)呼び出し
}
int main() {
	std::vector<record_data_t> shouhin;
	//input
	for (std::string buf; getline(std::cin, buf); ) {//一行を変数bufに読み込み
		try {
			shouhin.push_back(std::move(convert_from_input(buf)));//shouhinに追加。C++11:std::move,rvalue refarrence
		}
		catch (std::exception er) {//std::runtime_errorはstd::exceptionの派生型なのでこれでcatchできる
			std::cerr << er.what() << std::endl;
		}
	}
	//sort
	std::sort(shouhin.begin(), shouhin.end());

	//print data
	for (const auto& i : shouhin) {//Range-base for(C++11)
		std::cout << to_string(i) << std::endl;
	}
	return 0;
}

テーマ : プログラミング
ジャンル : コンピュータ

検索フォーム
デジタル・コルクマ3
コルクマワールド
東京 での時間:
更新履歴


総記事数:
Calendar 1.1
<
>
- - - - - - -
1 2 3 4 5 67
8 9 10 11 12 1314
15 16 17 18 19 2021
22 23 24 25 26 2728
29 30 31 - - - -

全記事

Designed by 石津 花

最新記事
カテゴリ
最新コメント
最新トラックバック
月別アーカイブ
リンク
RSSリンクの表示
ブロとも申請フォーム

この人とブロともになる

QRコード
QR
プロフィール

yumetodo

Author:yumetodo
FC2ブログへようこそ!

Powered By FC2ブログ

今すぐブログを作ろう!

Powered By FC2ブログ

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。