囲碁プログラムを作ろうと思うんだが、
C#プログラマーには タイマー1個使うにも C++ はよくわからん。
作るのは コマンドライン打ち込みができる コンソール・アプリケーションだ。
あと、Unityなら初期装備のユニット・テストも Visual Studio 2015 C++ では なんか導入方法が分からんかった。困ったもんだ。
とりあえず タイマーを自作してみた。
関係ないコードを削って 抜粋してみる。削りそこなうかもしれないが……。
1段階目
Console_Kifuwarabe.cpp
// Console_Kifuwarabe.cpp : Defines the entry point for the console application.
//
# include "stdafx.h"
# include <iostream>
# include "UtilFile.h"
# include "UtilTimer.h"
/**
* 指定時間間隔で実行したい処理を書くクラス
*/
class Worker : public DefaultWorker
{
public:
Worker() : DefaultWorker()
{
std::cout << "サブクラスのコンストラクター、呼んでるのかだぜ☆(^~^)?" << std::endl;
}
void OnStep()
{
std::cout << "別スレッドで実行中だぜ☆(^▽^)v" << std::endl;
// TODO: ここにやりたい処理を書く
}
};
int main()
{
// スレッドテスト
UtilTimer timer;
Worker* pWorker = new Worker();
// スレッドスタート。
timer.Start(pWorker,1000);
// 10秒経ったら スレッドを止めよう☆(^~^)
UtilTimer::Sleep_Milliseconds(5000L);
std::cout << "5秒経ったけど、動いてんの☆(^~^)?" << std::endl;
UtilTimer::Sleep_Milliseconds(5000L);
timer.Break_Force();
delete pWorker;
// 横軸はアルファベットにする☆(1桁で済む) Iを飛ばすのは 少なくともわたしはグニュー碁1.2(1995年)では見かけた昔からある習慣☆ 縦棒と区別するぜ☆(^▽^)
std::cout << " |ABCDEFGHJKLMNOPQRST|" << std::endl
<< "--+-------------------+" << std::endl
<< " 1| |" << std::endl
<< " 2| |" << std::endl
<< " 3| |" << std::endl
<< " 4| |" << std::endl
<< " 5| |" << std::endl
<< " 6| |" << std::endl
<< " 7| |" << std::endl
<< " 8| |" << std::endl
<< " 9| |" << std::endl
<< "10| |" << std::endl
<< "11| |" << std::endl
<< "12| |" << std::endl
<< "13| |" << std::endl
<< "14| |" << std::endl
<< "15| |" << std::endl
<< "16| |" << std::endl
<< "17| |" << std::endl
<< "18| |" << std::endl
<< "19| |" << std::endl
<< "--+-------------------+" << std::endl
<< "何かボタンを押せだぜ☆(^▽^)" << std::endl;
getchar();
//system("pause");
return 0;
}
UtilTimer.h
# pragma once
# include <iostream>
# include <fstream>
# include <atomic>
# include <thread>
# include <chrono>
/**
* 指定時間間隔で実行したい処理を書くクラス
*/
class DefaultWorker
{
public:
DefaultWorker()
{
std::cout << "スーパー・クラスのコンストラクターはこれだけど……☆(^~^)" << std::endl;
}
virtual void OnStep()
{
std::cout << "このOnStep()は実行してはいけないぜ☆(>_<)" << std::endl;
// なんにもしないぜ☆(^▽^)
}
/**
* ファンクターというのは、クラス名の後ろに() 演算子を付けたものです。
* ファンクターの引数は 無しにしておいてください。
*/
virtual void operator()()
{
std::cout << "スーパークラスのファンクターなんか呼んでんの☆(^~^)?" << std::endl;
// なんにもしないぜ☆(^▽^)
}
};
/**
* スレッドを立て、テキストファイルの内容が更新されていないか確認します。
* 参照 : 「c++で一定間隔で関数を実行させたい」 (teratail) https://teratail.com/questions/27449
* 参照 : 「std::thread::thread」 (C++言語の入門サイトです) http://kaworu.jpn.org/cpp/std::thread::thread
* 参照 : 「std::this thread::sleep for」 (C++言語の入門サイトです) http://kaworu.jpn.org/cpp/std::this_thread::sleep_for
* 参照 : 「C++1z ラムダ式での*thisのコピーキャプチャ」 (Faith and Brave - C++で遊ぼう) http://faithandbrave.hateblo.jp/entry/2016/04/27/191209
* 参照 : 「C++ で継承したときにサブクラスのメンバ関数を呼ぶためには virtual をつけて仮想関数にする」 (sotarokのお勉強) http://d.hatena.ne.jp/strkpy/20100401/1270133069
*/
class UtilTimer
{
public:
UtilTimer();
~UtilTimer();
/**
* スレッドを開始します。
*/
void Start(DefaultWorker* pWorker, long milliseconds);
/**
* スタートさせたスレッドが動いている間は、処理を先に進ませません。
*/
void Block();
/**
* 処理の途中でも、強制的にスレッドを終了します。
*/
void Break_Force();
/**
* (このクラスの持つスレッドではなく)この関数を呼び出したスレッドを、指定ミリ秒停止させます。
* ネームスペースを切るのがめんどくさいので このクラスに置いている。
*/
static void Sleep_Milliseconds(long milliseconds);
protected:
std::thread m_thread1;
/**
* スレッドを停止しても、処理のループは動き続けているようだったので、
* このフラグを偽にすることで、スレッドの中のループから抜けさせます
*/
bool m_alive;
/**
* 別スレッドで実行したい処理を書いたメソッドを持っています。
*/
DefaultWorker* m_pWorker;
};
UtilTimer.cpp
# include "stdafx.h"
# include "UtilTimer.h"
UtilTimer::UtilTimer()
{
}
UtilTimer::~UtilTimer()
{
}
void UtilTimer::Start(DefaultWorker* pWorker, long milliseconds)
{
m_alive = true;
m_pWorker = pWorker;
m_thread1 = std::thread([=,&milliseconds] {
// 処理と処理の間を n 秒以上空ける。(毎回 n 秒間隔保障なので、n 秒毎に定期的にスタートされるという意味ではない)
const std::chrono::milliseconds interval(milliseconds);
while (this->m_alive) {
auto start = std::chrono::system_clock::now();
// 処理が書かれたメソッドを呼び出す
this->m_pWorker->OnStep();
auto end = std::chrono::system_clock::now();
auto elapsed = end - start;
std::cout << "elapsed.count()=[" << elapsed.count() << "]" << std::endl;
// 毎回、インターバル時間のスリープを保障
if (elapsed < interval) {
std::this_thread::sleep_for(interval - elapsed);
}
}
std::cout << "finished." << std::endl;
});
}
void UtilTimer::Block()
{
// スタートさせたスレッドが終了するまで、これ以上進まない。
m_thread1.join();
// スレッドを止めるか、勝手に止まるかすると ここから以下に進む。
}
void UtilTimer::Break_Force()
{
// スレッドを止める
m_thread1.detach();
// スレッドの中のループから抜けさせる
this->m_alive = false;
}
void UtilTimer::Sleep_Milliseconds(long milliseconds)
{
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
}
とりあえず 動いている……、よし!
2段階目
コメントをもろた。
UtilTimer.h
DefaultWorkerクラスの OnStep() メソッドんとこを次のように書き換えた。すっきりした。
/**
* 指定時間間隔で実行したい処理を書くクラス
*/
class DefaultWorker
{
public:
DefaultWorker()
{
std::cout << "スーパー・クラスのコンストラクターはこれだけど……☆(^~^)" << std::endl;
}
virtual void OnStep() = 0;
};
別スレッドでテキスト・ファイルを読込もう
別記事 : 「[C++] テキストファイル読書き (C#プログラマ向け)」 http://qiita.com/muzudho1/items/fa1446b85ae64d01a5ca
参照 : 「std::string の前後の余分な空白文字を取り除く」 (C++ プログラミング) http://program.station.ez-net.jp/special/handbook/cpp/string/trim.asp
これと組み合わせて、別スレッドで テキスト・ファイルを監視して 存在したら読込むようにしよう。
Worker クラスを、 ReaderWorker に改造する。
Console_Kifuwarabe.cpp
/**
* 指定時間間隔で実行したい処理を書くクラス
*
* 参照 : 「ファイルやディレクトリの存在確認を行う方法」 (教えて!goo) https://oshiete.goo.ne.jp/qa/3399080.html
* 参照 : 「FAQ: Cannot convert from 'const char [..]' to 'LPCTSTR'」 (Visual Studio) https://social.msdn.microsoft.com/Forums/vstudio/en-US/c1b08c0a-a803-41c3-ac8c-84eba3be1ddb/faq-cannot-convert-from-const-char-to-lpctstr?forum=vclanguage
*/
class ReaderWorker : public DefaultWorker
{
public:
ReaderWorker() : DefaultWorker()
{
std::cout << "サブクラスのコンストラクター、呼んでるのかだぜ☆(^~^)?" << std::endl;
}
void OnStep()
{
std::string contents;
if (UtilFile::Read(Kifuwarabe::filename_move, contents)) {
contents = UtilString::Trim(contents);
std::cout << "指し手は[" << contents << "]だぜ☆(^▽^)v" << std::endl;
}
else {
std::cout << "ファイル[" << Kifuwarabe::filename_move << "]開けね☆(^~^)" << std::endl;
}
}
};
ほんとはここに 局面パーサーとか 書いて 思考ルーチンを叩きたい。
テストは ほとんど同じ。 main関数で試し書き。
// スレッドテスト
UtilTimer timer;
ReaderWorker* pWorker1 = new ReaderWorker();
// スレッドスタート。
timer.Start(pWorker1, 1000);
// 10秒経ったら スレッドを止めよう☆(^~^)
UtilTimer::Sleep_Milliseconds(5000L);
std::cout << "5秒経ったけど、動いてんの☆(^~^)?" << std::endl;
UtilTimer::Sleep_Milliseconds(5000L);
timer.Break_Force();
delete pWorker1;
本番では スレッドを止めずに 結果が出るまで ずっと動かしておけばいいだろう。