コンピュータ将棋ソフトとの対局サーバーを立てよう＜その９＞

前回の記事 : http://qiita.com/muzudho1/items/798dfbf267340f3801e1

Windows 10 の Visual Studio 2015 で 浮かむ瀬 のソースをいじりたいので、改造することにする。

# コンソール・アプリケーション

ソースを全部移動。

# プリコンパイル・ヘッダーを OFF にしよう。

いわゆる stdafx.h のことで、使わないので OFF にする。

- x86 を x64 に変える。
- [Debug] - [～Properties...] - [Configuration Properties] - [C/C++] - [Precompiled Headers] - [Precompiled Header] を 「Not Using Precompiled Headers」に変える。

# #if と #endif のペアがマッチしていないというエラーが出る

そんなことは無いが……。

使ってないんだったら [Ctrl]+[K]、[Ctrl]+[C]　でコメントアウトしてしまおう。

**コメントアウトしている行にエラーが出ている。**

# 文字コード

```
Severity	Code	Description	Project	File	Line	Suppression State
Warning	C4819	The file contains a character that cannot be represented in the current code page (932). Save the file in Unicode format to prevent data loss	ukamuse_sdt4_child4	e:\muzudhobusiness\workb\2017-03-04_shogiserver\その他\ukamuse_sdt4_チャイルド4（プロセス間通信）\ukamuse_sdt4_child4\usi.cpp	1	
```

(932) と言えば Shift-JIS だろう。BOM有のUTF-8に変えたらいいんだろうか？

[File] - [Save ～ As...] から [Save]の代わりに[Save with Encoding...] を選び、
「Unicode (UTF-8 with signature) - Codepage 65001」を選んで[OK]。

21 Error, 76 Worning が、21 Error, 75 Worning に減った。
あと 75回 やるのか。

[File] - [Advanced Save Options...] というサブ・メニューが増えた気がする。これを使うと少し楽。

**エラーが減らない。おかしい。**

stdafx.h、stdafx.cpp の２ファイルのエラーが７５ファイル分　出てるんじゃないか？

そうでもない。

# 聞いてみた。

```
at now
流したいジョブ
Ctrl + D
```

と

```
C4819のワーニングをスルー
```

というのがあるらしい。

やってみよう。

```
/home/★user/shogi/ukamuse_sdt4/bin# at now
The program 'at' is currently not installed. You can install it by typing:
apt install at
```

よし、インストールしたぜ。

```
# at now
warning: commands will be executed using /bin/sh
at> man at
at> ./apery
at> <EOT>
job 1 at Sun Mar 12 04:03:00 2017
```

いったい　どうなったのか？

```
# jobs
# ps aux | grep apery
root      2726  3.4  0.0 128660   920 ?        Sl   Mar11  40:46 /usr/bin/cli ./tamesi31_cs.exe ../ukamuse_sdt4/bin/apery
root     28267  0.0  0.0  12936   988 pts/6    S+   04:04   0:00 grep --color=auto apery
```

じゃあ、ログイン、ログアウトしてみるか。

```
# logout
$ logout
```

```
# ps aux | grep apery
root      2726  3.4  0.0 128660   920 ?        Sl   Mar11  40:54 /usr/bin/cli ./tamesi31_cs.exe ../ukamuse_sdt4/bin/apery
root     28367  0.0  0.0  12936   984 pts/6    S+   04:08   0:00 grep --color=auto apery
```

おっ、いた☆（*＾～＾*）

# Advanced Save Options
Visual Studio 2015 C++、文字コード変えて保存しても、[Save All] しないと　保存されてないのかだぜ？ [Advanced Save Options] は、セーブのオプションであって、セーブではない？

[Unicode - Codepage 1200] にしてみよ。改行は [Windows (CR LF)] で。

全テキストファイル、そう設定して保存したら　エラーが消えてワーニングだけ残った。

# Aperyの標準入出力
common.hpp 抜粋

```
enum SyncCout {
    IOLock,
    IOUnlock
};
std::ostream& operator << (std::ostream& os, SyncCout sc);
#define SYNCCOUT std::cout << IOLock
#define SYNCENDL std::endl << IOUnlock
```

出力の << 演算子はオーバーライドしてあって、ロックするとき 0、ロックを解除するとき 1 をストリームの間に挟むよう　つなげてあるんだが、これをプロセス間通信に置き換えられないものだろうか。

まず、Apery で std::cout を利用しているところを全検索してみよう。

ぱっと見　少なくとも　５０個所はありそうだな。

じゃあ、gameserverというクラス・ファイルを作ろう。

gameserver.h

```
#pragma once

// バックグラウンド・プロセス用の出力
#define BGP_COUT std::cout
#define BGP_ENDL std::endl

class gameserver
{
public:
	gameserver();
	~gameserver();
};

```

こんな感じでどうだろうか。ソースコードの std::cout、std::endl を全部置き換えていこう。

std::cout 89 個所。
std::endl 69 個所 が置き換わった。

```
#include "gameserver.hpp"
```

を置いていくのは手作業か。
拡張子はAperyに合わせて .h から .hpp に変えておいた。

ファイルの読み込み順序があるのか、エラーが消えない。

```
#include "gameserver.hpp"
```

はファイルの冒頭に書いてるんだが。

```
// バックグラウンド・プロセス用の出力
#define BGP_COUT BGP_COUT
#define BGP_ENDL BGP_ENDL
```

なるほど（＾～＾）

```
// バックグラウンド・プロセス用の出力
#define BGP_COUT std::cout
#define BGP_ENDL std::endl
```

修正。

# じゃあ、このソースを Ubuntu に逆輸入して動くか調べてみよう

ソース・フォルダーの中に要らんファイルを入れてくる Visual Studio 2015 のマナーの悪さはあるものの、ファイルサイズは小さいし、丸上げするか。著作権爆弾とか無いだろな。

```
cd /home/★user/shogi/ukamuse_sdt4_child4
# make profgen_sse
Makefile:1: warning: NUL character seen; rest of line ignored
Makefile:1: *** missing separator.  Stop.
```

改行コードか。

「複数ファイルの改行コードを一括置換しちゃう」（Qiita）
http://qiita.com/kouchi67/items/958e74ef792eed0c157d

Makefile、.hpp、.cpp、.sfen、.txt をLFに置換

```
# find . \( -name \Makefile -o -name \*.hpp -o -name \*.cpp -o -name \*.sfen -o -name \*.txt \) -type f | xargs -n 10 nkf -Lu --overwrite

# brew install nkf
The program 'brew' is currently not installed. You can install it by typing:
apt install linuxbrew-wrapper
# apt-get install nkf
# find . \( -name \Makefile -o -name \*.hpp -o -name \*.cpp -o -name \*.sfen -o -name \*.txt \) -type f | xargs -n 10 nkf -Lu --overwrite
# make profgen_sse
```

よし。

```
# mv apery ../bin/apery
```

実行してみると　動いている。
幻のapery（※１） でも実行しているわけでなければ　大丈夫だろう。

（※１……同名の違うファイルのこと）

# 標準入出力と、プロセス間通信を　どうやって置き換えるのか？
例えばこんなコードがあるわけだ。

```
        else if (token == "usi"      ) SYNCCOUT << "id name " << std::string(options["Engine_Name"])
                                                << "\nid author Hiraoka Takuya"
                                                << "\n" << options
                                                << "\nusiok" << SYNCENDL;
```

これを関数コール文の形に持っていきたい。例えば

```
Bgp.Enqueue( ★ );
```

みたいな。じゃあ、

```
Bgp.Enqueue( << "id name " << std::string(options["Engine_Name"])
                                                << "\nid author Hiraoka Takuya"
                                                << "\n" << options
                                                << "\nusiok" << );
```

こうすればいいのかというと、違う。文字列になってくれればいいんだが。

「数字や文字列を次々に連結してstringに出力するには？」（
C++　フリーでぷろぐらみんぐ）
http://ameblo.jp/nana-2007-july/entry-10098557843.html

じゃあ、

```
ostringstream oss;
oss << "id name " << std::string(options["Engine_Name"])
                                                << "\nid author Hiraoka Takuya"
                                                << "\n" << options
                                                << "\nusiok" <<
"";
Bgp.Enqueue( oss.str());
```

みたいな感じに置換したらいいのか？ oss の変数名がソースコード中で重複しまくらないか？

```
oss.clear();
```

を置いて、変数を使いまわせばいいか？

```
// バックグラウンド・プロセス用の出力
#define BGP_COUT bgp_oss.clear(); std::cout 
#define BGP_ENDL std::endl; bgp_enqueue (bgp_oss.str())
```

こう書くと、セミコロンが邪魔になって

```
            for (File f = File9; File1 <= f; --f)
                BGP_COUT << (this->isSet(makeSquare(f, r)) ? "  X" : "  .");
```

こういうケースで　for文のターゲットが変わってしまうので、f 変数が未定義になってしまう。

```
// バックグラウンド・プロセス用の出力
#define BGP_COUT bgp_oss.clear(), std::cout 
#define BGP_ENDL std::endl, bgp_enqueue (bgp_oss.str())
```

カンマで区切ると、bgp_enqueue( ) 関数が値を返さないじゃないか、とエラーになる。

```
int bgp_enqueue(std::string message)
{
	std::cout << message << std::endl;
	return 0;
}
```

じゃあ 0 でも返しておくか。

gameserver.hpp

```
#pragma once

#include <iostream> // std::cout
#include <sstream> // std::ostringstream

// バックグラウンド・プロセス用の出力
// #define BGP_COUT std::cout
// #define BGP_ENDL std::endl
#define BGP_COUT bgp_oss.clear(), std::cout 
#define BGP_ENDL std::endl, bgp_enqueue (bgp_oss.str())

// 文字列ストリーム出力を、文字列に置換するもの
static std::ostringstream bgp_oss;
// メッセージキューにエンキュー
static int bgp_enqueue(std::string message){
	std::cout << message << std::endl;
	return 0;
	//bgp_oss.clear();
	//bgp_oss << "あいうえお" << "";
	//bgp_oss.str();
}

class gameserver
{
public:
	gameserver();
	~gameserver();
};
```

これでコンパイルは通る。

**あれっ？**　FileZilla で転送したら　ファイルサイズが全部 0 だ。

```
# chmod 777 src
```

なんで 0 になってしまうんだろな？

```
# chmod 757 ukamuse_sdt4_child4
```

「FTPで0バイトでアップされる」（1-1/3+1/5-1/7+1/9-・・・）
http://blog.livedoor.jp/sire2/archives/50480369.html

容量制限？

```
# df -ah
```

見方が分からない。

```
# less /etc/fstab
```

見方が分からない。 [q]をタイプして抜ける。

```
# edquota ★user
The program 'edquota' is currently not installed. You can install it by typing:
apt install quota
```

無い。

「[楽天GOLD]FTPソフト(Filezilla)を使用して、」（YAHOO!JAPAN知恵袋）
http://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q11129189821

容量が問題か。じゃあ ukamuse_sdt4_child を削除しよう。

```
# rm -rf ukamuse_sdt4_child/
```

**あっ！**　転送でけた！

800メガバイトの評価ファイルを４つも置いてると　いっぱいなのか。

```
# find . \( -name \Makefile -o -name \*.hpp -o -name \*.cpp -o -name \*.sfen -o -name \*.txt \) -type f | xargs -n 10 nkf -Lu --overwrite
# make profgen_sse
# mv apery ../bin/apery
```

usi を入れてみたが、動いているぜ。

#プロセス間通信のpublishのコードを書こう

NuGet。

```
PM> Install-Package RabbitMQ.Client -Version 4.1.1
```

```
Error	C1083	Cannot open include file: 'ev.h': No such file or directory
```

さて、なぜか。

C++用のライブラリは入ってないのか？ じゃあ Ubuntu上で実験するか。

```
//#define UBUNTU

#ifdef UBUNTU
// プロセス間通信用
#include <ev.h>
#include <amqpcpp.h>
#include <amqpcpp/libev.h>
#endif
```

```
#ifdef UBUNTU
// メッセージキューにエンキュー
static int bgp_enqueue(std::string message){

	auto* loop = EV_DEFAULT;

	AMQP::LibEvHandler handler{ loop };
	AMQP::Address address{ "amqp://localhost:5672" };
	AMQP::TcpConnection connection{ &handler, address };
	AMQP::TcpChannel channel{ &connection };

	std::string exchange_name = "myexchange";
	std::string queue_name = "1111";
	std::string routing_key = "";

	channel.declareQueue(queue_name)
		.onError([](const char* errMsg) {
			std::cerr<< "error declaring queue: " << errMsg << "\n";
		});

	channel.bindQueue(exchange_name, queue_name, routing_key)
		.onSuccess([&connection, &channel, &exchange_name, &routing_key, &message]() {

			if (! channel.publish(exchange_name, routing_key, message.c_str(), message.size())) {
				std::cerr<< "failed to publish?\n";
			}

			// break in ev loop.
			connection.close();
		});

	// We will monitor until the connection is lost. Execute channel.declareQueue( ... ).
	ev_run(loop);

	return 0;
}
#else
static int bgp_enqueue(std::string message) {
	std::cout << message << std::endl;
	return 0;
	//bgp_oss.clear();
	//bgp_oss << "あいうえお" << "";
	//bgp_oss.str();
}
#endif
```

こんな感じで。エラー出力は吐くが、それ以外は吐かない。

```
# rm gameserver.hpp
# nano gameserver.hpp
# make profgen_sse
# mv apery ../bin/apery
```

あっ、

```
#define UBUNTU
```

コメントアウトを外してやりなおし。

```
make[2]: Entering directory '/home/★user/shogi/ukamuse_sdt4_child4/src'
g++ -std=c++11 -fno-exceptions -fno-rtti -Wextra -Ofast -MMD -MP -fopenmp -fprofile-generate -lgcov -DNDEBUG -DHAVE_SSE4 -DHAVE_SSE42 -msse4.2  -o ../obj/main.o -c main.cpp
In file included from /usr/include/amqpcpp.h:56:0,
                 from gameserver.hpp:14,
                 from bitboard.hpp:25,
                 from main.cpp:23:
/usr/include/amqpcpp/message.h: In member function ‘void AMQP::Message::setBodySize(uint64_t)’:
/usr/include/amqpcpp/message.h:75:117: error: exception handling disabled, use -fexceptions to enable
 ::max() < size) throw std::runtime_error("message is too big for this system");
                                                                              ^
```

サイズが大きいって何のことだろう。

/usr/include/amqpcpp/message.h

```
    /**
     *  Set the body size
     *  This field is set when the header is received
     *  @param  uint64_t
     */
    void setBodySize(uint64_t size)
    {
        // safety-check: on 32-bit platforms size_t is obviously also a 32-bit dword
        // in which case casting the uint64_t to a size_t could result in truncation
        // here we check whether the given size fits inside a size_t
        if (std::numeric_limits<size_t>::max() < size) throw std::runtime_error("message is too big for this system");

        // store the new size
        _bodySize = size;
    }
```

32bitシステム？

「std::numeric_limits::max」（cppreference.com）
http://en.cppreference.com/w/cpp/types/numeric_limits/max

サイズが短い文字列とか　あるんだろうか？

「size_tは環境によって定義が変わるという話」（おおたの物置）
https://ota42y.com/blog/2014/11/08/size-t/

「64bit OS と 32bit OS でのデータ型の相違一覧」（drk7.jp）
http://www.drk7.jp/MT/archives/000851.html

「C++ string クラスの size や length は strlen とは違う」（ルーチェ's Homepage）
https://www.ruche-home.net/boyaki/2011-06-30/Cstrings

setBodySize( ) はどこで呼ばれているのか？

/usr/include/amqpcpp/ ディレクトリー下のソースには　コール文は見つからない。

AMQP-CPP ライブラリが 64bit に対応してないんじゃないか？

# fexceptions

「fexceptions」（Ogawa's Home Page）
http://www2.kobe-u.ac.jp/~lerl2/l_cc_p_10.1.008/doc/main_cls/mergedProjects/copts_cls/common_options/option_fexceptions.htm

Apery は -fno-exceptions を使っていて、例外処理は使いませんよ、と指定しているんだが、AMQP-CPP は例外処理を使っているということだろうか？

Makefile

```
CFLAGS   = -std=c++11 -fno-exceptions -fno-rtti -Wextra -Ofast -MMD -MP -fopenmp
```

fno-exceptions を使ってるな。外すか。

```
#CFLAGS   = -std=c++11 -fno-exceptions -fno-rtti -Wextra -Ofast -MMD -MP -fopenmp
# 例外処理を使うライブラリを使ったので-fno-exceptions を外した。
CFLAGS   = -std=c++11 -fno-rtti -Wextra -Ofast -MMD -MP -fopenmp
```

コンパイルが進んでいる。

```
collect2: error: ld returned 1 exit status
Makefile:31: recipe for target 'apery' failed
make[2]: *** [apery] Error 1
make[2]: Leaving directory '/home/★user/shogi/ukamuse_sdt4_child4/src'
Makefile:43: recipe for target 'sse' failed
make[1]: *** [sse] Error 2
make[1]: Leaving directory '/home/★user/shogi/ukamuse_sdt4_child4/src'
Makefile:64: recipe for target 'profgen_sse' failed
make: *** [profgen_sse] Error 2
```

エラー。

```
SOURCES  = main.cpp bitboard.cpp init.cpp mt64bit.cpp position.cpp evalList.cpp \
           move.cpp movePicker.cpp square.cpp usi.cpp generateMoves.cpp evaluate.cpp \
           search.cpp hand.cpp tt.cpp timeManager.cpp book.cpp benchmark.cpp \
           thread.cpp common.cpp pieceScore.cpp
```

ここに足す必要があるんじゃないか？

```
SOURCES  = gameserver.cpp main.cpp bitboard.cpp init.cpp mt64bit.cpp position.cpp evalList.cpp \
```

```
# make profgen_sse
make CFLAGS='-std=c++11 -fno-rtti -Wextra -Ofast -MMD -MP -fopenmp -fprofile-generate -lgcov' LDFLAGS='-lpthread  -fprofile-generate -lgcov' sse
make[1]: Entering directory '/home/★user/shogi/ukamuse_sdt4_child4/src'
make CFLAGS='-std=c++11 -fno-rtti -Wextra -Ofast -MMD -MP -fopenmp -fprofile-generate -lgcov -DNDEBUG -DHAVE_SSE4 -DHAVE_SSE42 -msse4.2' LDFLAGS='-lpthread  -fprofile-generate -lgcov -flto' apery
make[2]: Entering directory '/home/★user/shogi/ukamuse_sdt4_child4/src'
g++ -o apery  -lpthread  -fprofile-generate -lgcov -flto -std=c++11 -fno-rtti -Wextra -Ofast -MMD -MP -fopenmp -fprofile-generate -lgcov -DNDEBUG -DHAVE_SSE4 -DHAVE_SSE42 -msse4.2
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
collect2: error: ld returned 1 exit status
Makefile:32: recipe for target 'apery' failed
make[2]: *** [apery] Error 1
make[2]: Leaving directory '/home/★user/shogi/ukamuse_sdt4_child4/src'
Makefile:44: recipe for target 'sse' failed
make[1]: *** [sse] Error 2
make[1]: Leaving directory '/home/★user/shogi/ukamuse_sdt4_child4/src'
Makefile:65: recipe for target 'profgen_sse' failed
make: *** [profgen_sse] Error 2
```

メインがないのか？

```
SOURCES  = main.cpp gameserver.cpp bitboard.cpp init.cpp mt64bit.cpp position.cpp evalList.cpp \
```

これでもダメ。

```
Makefile:34: recipe for target '../obj/gameserver.o' failed
make[2]: *** [../obj/gameserver.o] Error 1
make[2]: Leaving directory '/home/★user/shogi/ukamuse_sdt4_child4/src'
Makefile:43: recipe for target 'sse' failed
make[1]: *** [sse] Error 2
make[1]: Leaving directory '/home/★user/shogi/ukamuse_sdt4_child4/src'
Makefile:64: recipe for target 'profgen_sse' failed
make: *** [profgen_sse] Error 2
```

「[翻訳] 64ビット環境移植における20個の問題 」（Geospatialにっき）
http://yukinarit.blog11.fc2.com/blog-entry-27.html

「20 issues of porting C++ code to the 64-bit platform」（PVS-Studio）
https://www.viva64.com/en/a/0004/#ID0EMEBK


```
    void setBodySize(uint64_t size)
    {
        // safety-check: on 32-bit platforms size_t is obviously also a 32-bit dword
        // in which case casting the uint64_t to a size_t could result in truncation
        // here we check whether the given size fits inside a size_t
//        if (std::numeric_limits<size_t>::max() < size) throw std::runtime_error("message is too big for this system");
        if (std::numeric_limits<size_t>::max() < size) throw std::runtime_error("message is too big for this system max=" + std::to_string(std::numeric_limits<size_t>::max()) + " size=" + std::to_string(size) );

        // store the new size
        _bodySize = size;
    }
```

こんなことしていいんだろうか？

「【C++】数値型をstring型に変換する複数の方法【int/double to string】」（MaryCore）
http://marycore.jp/prog/cpp/convert-number-to-string/

```
/usr/include/amqpcpp/message.h: In member function ‘void AMQP::Message::setBodySize(uint64_t)’:
/usr/include/amqpcpp/message.h:76:210: error: exception handling disabled, use -fexceptions to enable
         if (std::numeric_limits<size_t>::max() < size) throw std::runtime_error("message is too big for this system max=" + std::to_string(std::numeric_limits<size_t>::max()) + " size=" + std::to_string(size) );
```

あれっ、こんな書き方はできない？

「文字列で学ぶC++入門」（Qiita）
http://qiita.com/7shi/items/cac7b3e9b90bf91b00cc

```
        if (std::numeric_limits<size_t>::max() < size) throw std::runtime_error( ("message is too big for this system max=" + std::to_string(std::numeric_limits<size_t>::max()) + " size=" + std::to_string(size)).c_str() );
```

じゃあ、こうか。

```
/usr/include/amqpcpp/message.h: In member function ‘void AMQP::Message::setBodySize(uint64_t)’:
/usr/include/amqpcpp/message.h:76:221: error: exception handling disabled, use -fexceptions to enable
         if (std::numeric_limits<size_t>::max() < size) throw std::runtime_error( ("message is too big for this system max=" + std::to_string(std::numeric_limits<size_t>::max()) + " size=" + std::to_string(size)).c_str() );
                                                                                                                                                                                                                             ^
```

なんのこっちゃ。

```
-fno-exceptions
```

これ　付けたり外したりしてたらダメか。外しとこう。

```
benchmark.cpp:(.text+0xe0e): undefined reference to `ev_default_loop'
benchmark.cpp:(.text+0xe87): undefined reference to `AMQP::TcpConnection::TcpConnection(AMQP::TcpHandler*, AMQP::Address const&)'
benchmark.cpp:(.text+0xede): undefined reference to `AMQP::ChannelImpl::ChannelImpl()'
benchmark.cpp:(.text+0xfbe): undefined reference to `AMQP::ChannelImpl::attach(AMQP::Connection*)'
benchmark.cpp:(.text+0x11c8): undefined reference to `ev_run'
collect2: error: ld returned 1 exit status
```

そういえば、コンパイラオプションがあった気がするな。

```
-lev -lamqpcpp -pthread
```

この３つを付け足さないと。

Makefile

```
#CFLAGS   = -std=c++11 -fno-exceptions -fno-rtti -Wextra -Ofast -MMD -MP -fopenmp
# 例外処理を使うライブラリ(AMQP-CPP)を使ったので-fno-exceptions を外した。
# RabbitMQ を使うので-lev -lamqpcpp を追加。
CFLAGS   = -std=c++11 -fno-rtti -Wextra -Ofast -MMD -MP -fopenmp -lev -lamqpcpp
```

コンパイルが通ったような。じゃあ また -fno-exceptions を付けてみよう。

やっぱエラーが出た。
やっぱり外す。

```
# ./consume.exe
found box      = [1111]
My ID watching = [amq.ctag-JpyKshTMjD_bM6_anhh7Qw]
find message   = []
find message   = []
```

Apery は空文字列を　メッセージ・キュー「1111」に入れたみたいだ。なぜ空文字列なのだろう。

```
if (! channel.publish(exchange_name, routing_key, message.c_str(), strlen(message.c_str()) )) {
                                std::cerr << "failed to publish?\n";
                        }
```

適当にいじりすぎたか。

```
if (! channel.publish(exchange_name, routing_key, message.c_str(), message.size() )) {
                                std::cerr << "failed to publish?\n";
                        }
```

これでよかったか。

```
# make clean
# make profgen_sse
```

```
# ./consume.exe
found box      = [1111]
My ID watching = [amq.ctag-DYpzEdeU8No8uh0KoVn6fg]
find message   = []
find message   = []
```

publish はしてるんだが空っぽなのか。もっと調べてみよう。

```
std::string msg2 = "AiueoKakikukeko";
if (! channel.publish(exchange_name, routing_key, msg2.c_str(), msg2.size() )) {
```

これで試してみよう。

```
# ./consume.exe
found box      = [1111]
My ID watching = [amq.ctag-Clba0qSp8znhy88dGmUvcw]
find message   = [AiueoKakikukeko]
find message   = [AiueoKakikukeko]
```

ここは　いけてるのか。

じゃあ　次は

```
#define BGP_COUT bgp_oss.clear(), std::cout 
#define BGP_ENDL std::endl, bgp_enqueue (bgp_oss.str())
```

これなんだが、

```
enum SyncCout {
    IOLock,
    IOUnlock
};
std::ostream& operator << (std::ostream& os, SyncCout sc);
#define SYNCCOUT BGP_COUT << IOLock
#define SYNCENDL BGP_ENDL << IOUnlock
```

これと組み合わせると

```
#define SYNCCOUT bgp_oss.clear(), std::cout << IOLock
#define SYNCENDL std::endl, bgp_enqueue (bgp_oss.str()) << IOUnlock
```

アンロックの場所がおかしくなってしまう。
BGP_ENDL だけ分解するか。

```
#define BGP_ENDL BGP_ENDL1, BGP_ENDL2
#define BGP_ENDL1 std::endl
#define BGP_ENDL2 bgp_enqueue (bgp_oss.str())
```

こうして、

```
#define SYNCENDL BGP_ENDL1 << IOUnlock, BGP_ENDL2
```

こう。

```
# ./consume.exe
found box      = [1111]
My ID watching = [amq.ctag-hpyixpnOKUhN5foCbpqdCQ]
find message   = []
find message   = []
find message   = []
find message   = []
find message   = []
find message   = []
find message   = []
```

それだけではダメか。

# Windows 10 / Visual Studio 2015

[Configuration Properties] - [C/C++] - [Command Line] の [Additional Options]欄に

```
 -lev -lamqpcpp
```

と書いたら ev.h とか読込んでくれないだろうか？

```
cl : Command line warning D9002: ignoring unknown option '-lev'
1>cl : Command line warning D9002: ignoring unknown option '-lamqpcpp'
```

g++ 用の書き方だろうか。

# コーディング・ミス発見

```
#define BGP_COUT bgp_oss.clear(), std::cout 
```

これは、

```
#define BGP_COUT bgp_oss.clear(), bgp_oss
```

こうだろう。

```
# ./consume.exe
found box      = [1111]
My ID watching = [amq.ctag-vhBo9rHCF9Cjil7N-iioTg]
find message   = [id name ukamuse_SDT4
id author Hiraoka Takuya

option name Best_Book_Move type check default false
option name Book_File type string default book/20150503/book.bin
option name Byoyomi_Margin type spin default 500 min 0 max 2147483647
option name Clear_Hash type button
option name Draw_Ply type spin default 256 min 1 max 2147483647
option name Engine_Name type string default ukamuse_SDT4
option name Max_Book_Ply type spin default 32767 min 0 max 32767
option name Max_Random_Score_Diff type spin default 0 min 0 max 32600
option name Max_Random_Score_Diff_Ply type spin default 32767 min 0 max 32767
option name Min_Book_Ply type spin default 32767 min 0 max 32767
option name Min_Book_Score type spin default -180 min -32601 max 32601
option name Minimum_Thinking_Time type spin default 20 min 0 max 2147483647
option name Move_Overhead type spin default 30 min 0 max 5000
option name MultiPV type spin default 1 min 1 max 594
option name OwnBook type check default true
option name Slow_Mover type spin default 89 min 1 max 1000
option name Slow_Mover_10 type spin default 10 min 1 max 1000
option name Slow_Mover_16 type spin default 20 min 1 max 1000
option name Slow_Mover_20 type spin default 40 min 1 max 1000
option name Threads type spin default 2 min 1 max 256
option name Time_Margin type spin default 4500 min 0 max 2147483647
option name USI_Hash type spin default 256 min 1 max 1048576
option name USI_Ponder type check default true
usiok
]
```

**どうよ！**

# 浮かむ瀬に　プロセス間通信のメッセージを受信させるには？

-現状 : 無限ループの中に ReadLine() を置いて制御をブロックしている
-これを : 丸ごと consume スレッドのループの中に移動させたい

このあたりは改造するしかない。

usi.cpp の doUSICommandLoop(...)関数の中に無限ループがあるんだが、これを関数化して外に出す。

search.hpp struct Searcher

```
	STATIC bool doUSICommandLoop_inner(int argc, char* argv[], Position pos, std::string cmd);
```

usi.cpp

```
void Searcher::doUSICommandLoop_inner(int argc, char* argv[], bool evalTableIsRead, std::string cmd, std::string token) {

}
```

```
void Searcher::doUSICommandLoop(int argc, char* argv[]) {
    Position pos(DefaultStartPositionSFEN, threads.main(), thisptr);

    std::string cmd;
    for (int i = 1; i < argc; ++i)
        cmd += std::string(argv[i]) + " ";

	// プロセス間通信のメッセージ受信方法に置き換えたい。
	bool continued;
    do {
		continued = Searcher::doUSICommandLoop_inner(argc, argv, pos, cmd);
    } while (continued);

    threads.main()->waitForSearchFinished();
}
bool evalTableIsRead = false;
bool Searcher::doUSICommandLoop_inner(int argc, char* argv[], Position pos, std::string cmd) {
    // ★中略
}
```

うーん、Searcher を引数にしたいな。

コード改造はダメだｗｗｗｗｗ　壊してしまう。

メッセージの受信は　別スレッドで　回転式バッファーに入れておくようにしようｗｗｗｗｗ

```
		std::istringstream ssCmd(cmd);
```

このインプット・ストリーム、

```
		else if (token == "go") go(pos, ssCmd);
		else if (token == "position") setPosition(pos, ssCmd);
```

さらに　サブルーチンに回されてるんだが　何に使われてるんだろな。

```
void go(const Position& pos, std::istringstream& ssCmd) {
    LimitsType limits;
    std::string token;

    limits.startTime.restart();

    while (ssCmd >> token) {
        if      (token == "ponder"     ) limits.ponder = true;
        else if (token == "btime"      ) ssCmd >> limits.time[Black];
        else if (token == "wtime"      ) ssCmd >> limits.time[White];
        else if (token == "binc"       ) ssCmd >> limits.inc[Black];
        else if (token == "winc"       ) ssCmd >> limits.inc[White];
        else if (token == "infinite"   ) limits.infinite = true;
        else if (token == "byoyomi" || token == "movetime") ssCmd >> limits.moveTime;
        else if (token == "mate"       ) ssCmd >> limits.mate;
        else if (token == "depth"      ) ssCmd >> limits.depth;
        else if (token == "nodes"      ) ssCmd >> limits.nodes;
        else if (token == "searchmoves") {
            while (ssCmd >> token)
                limits.searchmoves.push_back(usiToMove(pos, token));
        }
    }
    if      (limits.moveTime != 0)
        limits.moveTime -= pos.searcher()->options["Byoyomi_Margin"];
    else if (pos.searcher()->options["Time_Margin"] != 0)
        limits.time[pos.turn()] -= pos.searcher()->options["Time_Margin"];
    pos.searcher()->threads.startThinking(pos, limits, pos.searcher()->states);
}
```

go 関数の中でさらに　ループがあるのか。多重ループを１重ループに改造したいぜ。**だが、その改造をするとまた壊してしまうｗｗｗｗｗ**。

インプットストリームを　グローバル変数にするか？

「文字列ストリームからの読み込み」（C++編【標準ライブラリ】　第２９章　文字列ストリーム）
http://ppp-lab.sakura.ne.jp/ProgrammingPlacePlus/cpp/library/029.html

Ｃ＋＋の入出力の使い方の勘が無くてつらい。

```
std::istringstream ssCmd(cmd);
```

これと、

```
std::istringstream ssCmd;
```

これで挙動が違うのな。Ｃ＋＋分からん。

## ラッピングできないのか
困ったときはラッピングを考えよう

しかし　インプット・ストリングはなんで初期値を取ってるんだ？
標準入力から拾ってるんじゃないのか？

```
if (argc == 1 && !std::getline(std::cin, cmd))
    cmd = "quit";
```

標準入力の本体はこっちの、

```
std::getline(std::cin, cmd)
```

こいつじゃないか？

std::istringstream　はただの、取り出すのが便利な　文字列なだけじゃないか？

```
// 「./apery」のように引数無しで打鍵して実行された場合に限り、なんども標準入力を待つ。
// 文字が打たれたらその文字を、空文字を打たれたら "quit" 扱いとする。
if (argc == 1 && !std::getline(std::cin, cmd))
    cmd = "quit";
```

つまりこう。

```
static std::istringstream bgp_dequeue(){
	std::string sourceString;
	if (!std::getline(std::cin, sourceString))
	{
		sourceString = "quit";
	}
	std::istringstream ssCmd(sourceString);
	return ssCmd;
}
```

こんな書き方でいいんだろうか？　いや、

```
static std::string bgp_dequeue(){
	std::string sourceString;
	if (!std::getline(std::cin, sourceString))
	{
		sourceString = "quit";
	}
	return sourceString;
}
```

もっとシンプルに　こうだろう。

```
if (argc == 1)
{
    cmd = bgp_dequeue();
}
```

これでは　動かない。ちょっと　いじると壊れてしまう。

```
if (argc == 1)
{
    if (!std::getline(std::cin, cmd))
    {
        cmd = "quit";
    }
}
```

これだと動く。何が違うのか。

```
static std::string bgp_dequeue(std::string sourceString){
	if (!std::getline(std::cin, sourceString))
	{
		sourceString = "quit";
	}
	return sourceString;
}
```

こうしてもダメ。違いが分からん。sourceString は getline で上書きされるのだろうから関係ないか。

```
static std::basic_istream<char, std::char_traits<char>>& bgp_getline(std::string& sourceString) {
	return std::getline(std::cin, sourceString);
}
```

この難しい返り値は何だろう？
&が付いてるのでアドレスなんだろうか。該当がなければヌルで、ヌルは 0 で偽だ、とか　そういうことなのか？

```
if (argc == 1)
{
    if (!bgp_getline(cmd))
    {
        cmd = "quit";
    }
    //if (!std::getline(std::cin, cmd))
    //{
    //	cmd = "quit";
    //}
}
```

これで結果が同じ。

```
static bool bgp_getline2(std::string& sourceString) {
	return !std::getline(std::cin, sourceString);
}
```

こうして、

```
if (argc == 1)
{
	//if (!bgp_getline(cmd))
	//{
	//	cmd = "quit";
	//}
	if (bgp_getline2(cmd))
	{
		cmd = "quit";
	}
}
```

こうすると結果が違う。どこが変わったのか。

標準出力の改造に　失敗してしまったんだろうか？
例えばここ。

```
static int bgp_enqueue(std::string message) {
	std::cout << message << std::endl;
	return 0;
}
```

```
static int bgp_enqueue(std::string message) {
	std::cout << IOLock << message << std::endl << IOUnlock;
	return 0;
}
```

こうした方がいいんだろうか？

だめだ～。

```
usi
id name Apery Debug Build
id author Hiraoka Takuya

option name Best_Book_Move type check default false
option name Book_File type string default book/20150503/book.bin
option name Byoyomi_Margin type spin default 500 min 0 max 2147483647
option name Clear_Hash type button
option name Draw_Ply type spin default 256 min 1 max 2147483647
option name Engine_Name type string default Apery Debug Build
option name Max_Book_Ply type spin default 32767 min 0 max 32767
option name Max_Random_Score_Diff type spin default 0 min 0 max 32600
option name Max_Random_Score_Diff_Ply type spin default 32767 min 0 max 32767
option name Min_Book_Ply type spin default 32767 min 0 max 32767
option name Min_Book_Score type spin default -180 min -32601 max 32601
option name Minimum_Thinking_Time type spin default 20 min 0 max 2147483647
option name Move_Overhead type spin default 30 min 0 max 5000
option name MultiPV type spin default 1 min 1 max 594
option name OwnBook type check default true
option name Slow_Mover type spin default 89 min 1 max 1000
option name Slow_Mover_10 type spin default 10 min 1 max 1000
option name Slow_Mover_16 type spin default 20 min 1 max 1000
option name Slow_Mover_20 type spin default 40 min 1 max 1000
option name Threads type spin default 4 min 1 max 256
option name Time_Margin type spin default 4500 min 0 max 2147483647
option name USI_Hash type spin default 256 min 1 max 1048576
option name USI_Ponder type check default true
usiok

isready
id name Apery Debug Build
id author Hiraoka Takuya

option name Best_Book_Move type check default false
option name Book_File type string default book/20150503/book.bin
option name Byoyomi_Margin type spin default 500 min 0 max 2147483647
option name Clear_Hash type button
option name Draw_Ply type spin default 256 min 1 max 2147483647
option name Engine_Name type string default Apery Debug Build
option name Max_Book_Ply type spin default 32767 min 0 max 32767
option name Max_Random_Score_Diff type spin default 0 min 0 max 32600
option name Max_Random_Score_Diff_Ply type spin default 32767 min 0 max 32767
option name Min_Book_Ply type spin default 32767 min 0 max 32767
option name Min_Book_Score type spin default -180 min -32601 max 32601
option name Minimum_Thinking_Time type spin default 20 min 0 max 2147483647
option name Move_Overhead type spin default 30 min 0 max 5000
option name MultiPV type spin default 1 min 1 max 594
option name OwnBook type check default true
option name Slow_Mover type spin default 89 min 1 max 1000
option name Slow_Mover_10 type spin default 10 min 1 max 1000
option name Slow_Mover_16 type spin default 20 min 1 max 1000
option name Slow_Mover_20 type spin default 40 min 1 max 1000
option name Threads type spin default 4 min 1 max 256
option name Time_Margin type spin default 4500 min 0 max 2147483647
option name USI_Hash type spin default 256 min 1 max 1048576
option name USI_Ponder type check default true
usiok
info string start setting eval table

```

おかしいだろ。

```
//#define SYNCCOUT BGP_COUT << IOLock
//#define SYNCENDL BGP_ENDL1 << IOUnlock, BGP_ENDL2
#define SYNCCOUT BGP_COUT
#define SYNCENDL BGP_ENDL1, BGP_ENDL2
```

これでどうか。ロックを外して BGP_ENDL2 の中でロックするんだぜ。

```
#define BGP_COUT bgp_oss.clear(), bgp_oss.flush(), bgp_oss
```

フラッシュとか要るのだろうか。

だめ。

```
#define BGP_ENDL2 bgp_oss.flush(), bgp_enqueue (bgp_oss.str())
```

じゃあ、こっちか？

だめか。

出力した全文を　もう１回　出力している感じの不具合なのだろうか？

clear() が効いてないんじゃないか？

「How do you clear a stringstream variable?」（stack overflow）
http://stackoverflow.com/questions/20731/how-do-you-clear-a-stringstream-variable

```
static int bgp_enqueue(std::string message) {
	std::cout << IOLock << message << std::endl << IOUnlock;
	bgp_oss.str(std::string());// 空文字列を入れてクリアーする
	return 0;
}
```

これでどうか。

よしおっけ！

```
static std::basic_istream<char, std::char_traits<char>>& bgp_getline(std::string& sourceString) {
	return std::getline(std::cin, sourceString);
}
static bool bgp_getline2(std::string& sourceString) {
	return !std::getline(std::cin, sourceString);
}
static std::string bgp_dequeue(){
	std::string cmd;
	if (bgp_getline2(cmd))
	{
		cmd = "quit";
	}
	return cmd;
}
```

こんな感じにラッピングして。

# プリコンパイル・ヘッダー

```
Error	C1010	unexpected end of file while looking for precompiled header. Did you forget to add '#include "stdafx.h"' to your source?
```

なんかまた　こういうコンパイル・エラーが出てきた。

**Debug を Release に変えたから設定も変わったのか。**

# Windows 10 と Ubuntu

Windows 10 版は IDE でテストできるが、Ubuntu 版は nano と g++ でやってるので　プログラムが書けてるのが　よくわからない。

gameserver.hpp

```
#pragma once

#include <string>
#include <iostream> // std::cout
#include <sstream> // std::ostringstream
#include "common.hpp"


// #define UBUNTU


// バックグラウンド・プロセス用の出力
// #define BGP_COUT std::cout
// #define BGP_ENDL std::endl
#define BGP_COUT bgp_oss
#define BGP_ENDL BGP_ENDL1, BGP_ENDL2
#define BGP_ENDL1 std::endl
#define BGP_ENDL2 bgp_enqueue (bgp_oss.str())

// 文字列ストリーム出力を、文字列に置換するもの
static std::ostringstream bgp_oss;


#ifdef UBUNTU
// OS      : Ubuntu 16.04
// Library : libev
//         : Install   : Command  : sudo apt-get update
//                                : sudo apt-get install libev-dev
// Service : RabbitMQ
//         : Reference : Web site : Top page http://www.rabbitmq.com/
//         : Install   : Web site : Installing on Debian / Ubuntu http://www.rabbitmq.com/install-debian.html
//         : Manual    : Command  : man rabbitmqctl
//         : Start     : Command  : rabbitmq-server
//         : Stop      : Command  : rabbitmqctl stop
//         : Check     : Command  : rabbitmqctl status
//         :           : Command  : rabbitmqctl list_queues
// Library : AMQP-CPP
//         : Reference : Web site : AMQP-CPP README.md https://github.com/CopernicaMarketingSoftware/AMQP-CPP
//
// gameserver.hpp

// プロセス間通信用
#include <ev.h>
#include <amqpcpp.h>
#include <amqpcpp/libev.h>

/// <summary>
/// 回転式バッファー。
/// これはメイン・スレッドに置く。
/// デキューのスレッドでエンキューすることはできない。
/// デキュー処理は、回転式バッファーを仲介にしてエンキュー処理にメッセージを渡す。
/// </summary>
namespace RotationBuffer
{
	const int bufferSize = 100;
	std::string* buffer = new std::string[bufferSize];
	int* bufferCursors = new int[] { 0, 0 };
	const int PUT_INDEX = 0;
	const int GET_INDEX = 1;
	void putMessage(std::string message)
	{
		buffer[bufferCursors[PUT_INDEX]] = message;
		bufferCursors[PUT_INDEX]++;
		if (!(bufferCursors[PUT_INDEX] < bufferSize))
		{
			bufferCursors[PUT_INDEX] = 0;
		}
	}
	std::string getMessage()
	{
		if ("" != buffer[bufferCursors[GET_INDEX]])
		{
			std::string message = buffer[bufferCursors[GET_INDEX]];
			buffer[bufferCursors[GET_INDEX]] = "";
			bufferCursors[GET_INDEX]++;
			if (!(bufferCursors[GET_INDEX] < bufferSize))
			{
				bufferCursors[GET_INDEX] = 0;
			}
			return message;
		}
		return "";
	}
}

const std::string destinationQueueName = "1113";

// メッセージキューにエンキュー
static int bgp_enqueue(std::string message){

	auto* loop = EV_DEFAULT;

	AMQP::LibEvHandler handler{ loop };
	AMQP::Address address{ "amqp://localhost:5672" };
	AMQP::TcpConnection connection{ &handler, address };
	AMQP::TcpChannel channel{ &connection };

	std::string exchange_name = "myexchange";
	std::string queue_name = "destinationQueueName";
	std::string routing_key = "";

	channel.declareQueue(queue_name)
		.onError([](const char* errMsg) {
			std::cerr << "error declaring queue: " << errMsg << "\n";
		});

	channel.bindQueue(exchange_name, queue_name, routing_key)
		.onSuccess([&connection, &channel, &exchange_name, &routing_key, &message]() {

			if (! channel.publish(exchange_name, routing_key, message.c_str(), message.size())) {
				std::cerr << "failed to publish?\n";
			}

			// break in ev loop.
			connection.close();
		});

	// We will monitor until the connection is lost. Execute channel.declareQueue( ... ).
	ev_run(loop);

	return 0;
}

// bgp_startConsume() しておくこと。
static std::string bgp_dequeue() {
	std::string message;
	
	while ("" == message)
	{
		message = RotationBuffer.getMessage();
	}

	return message;
}

static std::string bgp_dequeue() {
	std::string cmd;
	if (!std::getline(std::cin, cmd))
	{
		cmd = "quit";
	}
	return cmd;
}

const std::string sourceQueueName = "1112";

// メッセージ・キューの監視を開始
static void bgp_startConsume()
{
	// Connect to the AMQP service.
	auto *loop = EV_DEFAULT;
	AMQP::LibEvHandler handler(loop);
	AMQP::TcpConnection connection(&handler, AMQP::Address("amqp://localhost/"));
	AMQP::TcpChannel channel(&connection);

	// I will go to the front of the box named "1111".
	channel.declareQueue(sourceQueueName);

	// I look inside the box.
	auto errorCb = [](const char *errMsg) {
		std::cerr << "My ID watching failed [" << errMsg << "]" << std::endl;
	};
	auto messageCb = [&channel](const AMQP::Message &message, uint64_t deliveryTag, bool redelivered) {
		RotationBuffer.putMessage(message);

		channel.ack(deliveryTag);
	};
	channel.consume(sourceQueueName)
		.onReceived(messageCb)
		.onError(errorCb);

	// I will keep on forever.
	ev_run(loop, 0);

	// I will not come here.
	return;
}

#else
static int bgp_enqueue(std::string message) {
	std::cout << IOLock << message << std::endl << IOUnlock;
	bgp_oss.str(std::string());// 空文字列を入れてクリアーする
	return 0;
}

static std::string bgp_dequeue() {
	std::string cmd;
	if (!std::getline(std::cin, cmd))
	{
		cmd = "quit";
	}
	return cmd;
}

// メッセージ・キューの監視を開始
static void bgp_startConsume()
{
	// なにもしない
}
#endif

class gameserver
{
public:
	gameserver();
	~gameserver();
};
```

(**2017-03-13 追記:** 上プログラムの std::string queue_name = "destinationQueueName"; は、std::string queue_name = destinationQueueName; の誤り）

腹も減ってきたしつらい。

C++の文法を知らなくてつらい。

```
In file included from bitboard.hpp:25:0,
                 from main.cpp:23:
gameserver.hpp:57:31: error: expected primary-expression before ‘]’ token
  int* bufferCursors = new int[] { 0, 0 };
                               ^
gameserver.hpp:57:40: error: too many initializers for ‘int [1]’
  int* bufferCursors = new int[] { 0, 0 };
                                        ^
gameserver.hpp: In function ‘std::__cxx11::string bgp_dequeue()’:
gameserver.hpp:130:27: error: expected primary-expression before ‘.’ token
   message = RotationBuffer.getMessage();
                           ^
gameserver.hpp: In function ‘std::__cxx11::string bgp_dequeue()’:
gameserver.hpp:136:20: error: redefinition of ‘std::__cxx11::string bgp_dequeue()’
 static std::string bgp_dequeue() {
                    ^
gameserver.hpp:125:20: note: ‘std::__cxx11::string bgp_dequeue()’ previously defined here
 static std::string bgp_dequeue() {
                    ^
gameserver.hpp: In lambda function:
gameserver.hpp:164:17: error: expected primary-expression before ‘.’ token
   RotationBuffer.putMessage(message);
                 ^
```

## 配列の初期化

「配列の要素の初期化」（自明でない日記）
http://d.hatena.ne.jp/tt4cs/20120219/1329636595

```
int bufferCursors[2] = { 0, 0 };
```

こんなんでいいのか。

```
const int bufferSize = 100;
std::string buffer[bufferSize] = {};
```

これもありか。

他のエラーはこんな感じ。

```
In file included from bitboard.hpp:25:0,
                 from main.cpp:23:
gameserver.hpp: In function ‘std::__cxx11::string bgp_dequeue()’:
gameserver.hpp:130:27: error: expected primary-expression before ‘.’ token
   message = RotationBuffer.getMessage();
                           ^
gameserver.hpp: In function ‘std::__cxx11::string bgp_dequeue()’:
gameserver.hpp:136:20: error: redefinition of ‘std::__cxx11::string bgp_dequeue()’
 static std::string bgp_dequeue() {
                    ^
gameserver.hpp:125:20: note: ‘std::__cxx11::string bgp_dequeue()’ previously defined here
 static std::string bgp_dequeue() {
                    ^
gameserver.hpp: In lambda function:
gameserver.hpp:164:17: error: expected primary-expression before ‘.’ token
   RotationBuffer.putMessage(message);
                 ^
```

ネームスペースとメソッドをつなぐのは「::」だったかな。

まだ残っているコンパイルエラーは。

```
In file included from bitboard.hpp:25:0,
                 from main.cpp:23:
gameserver.hpp: In function ‘std::__cxx11::string bgp_dequeue()’:
gameserver.hpp:136:20: error: redefinition of ‘std::__cxx11::string bgp_dequeue()’
 static std::string bgp_dequeue() {
                    ^
gameserver.hpp:125:20: note: ‘std::__cxx11::string bgp_dequeue()’ previously defined here
 static std::string bgp_dequeue() {
                    ^
gameserver.hpp: In lambda function:
gameserver.hpp:164:37: error: could not convert ‘message’ from ‘const AMQP::Message’ to ‘std::__cxx11::string {aka std::__cxx11::basic_string<char>}’
   rotationBuffer::putMessage(message);
                                     ^
```

同じメソッドで２個も作ってるじゃないか。コピペ忘れか。直した。

残ってるエラーは。

```
In file included from bitboard.hpp:25:0,
                 from main.cpp:23:
gameserver.hpp: In lambda function:
gameserver.hpp:155:37: error: could not convert ‘message’ from ‘const AMQP::Message’ to ‘std::__cxx11::string {aka std::__cxx11::basic_string<char>}’
   rotationBuffer::putMessage(message);
                                     ^
```

AMQP::Message 型は .body() で文字列を取れるんだったか。

```
		std::string myString(message.body(), message.bodySize());
		rotationBuffer::putMessage(myString);
```

こうだったか。直した。
まだエラーが出る。

```
../obj/bitboard.o:(.bss+0x891e40): multiple definition of `rotationBuffer::buffer[abi:cxx11]'
../obj/main.o:(.bss+0x20): first defined here
../obj/bitboard.o: In function `rotationBuffer::getMessage[abi:cxx11]()':
```

これと似たのがたくさん。重複定義がどこかにあるのか。

```
const int bufferSize = 100;
static std::string buffer[bufferSize] = {};
```

グローバル変数には static を付けないとダメなんだろうか？

グローバル関数にも static が必要そうだ。

修正して　コンパイルは通った。

```
    対局
    ====
        usi
            usiok
        isready
            readyok
        usinewgame
        position sfen lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL b - 1 moves
        go
            例) bestmove 2g2f ponder 1c1d
        quit
```

何も反応のない画面に向かって、タイミングを開けながら　このコマンドを打ちたい。

```
# ./apery
usi
isready
usinewgame
position sfen lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL b - 1 moves
go
quit
```

quit が効かないので　あやしい。

```
# rabbitmqctl list_queues
Listing queues ...
1112    0
1111    0
1113    0
myqueue 25
```

投げる先を間違えているのだろうか？

tamesi33a2_cs.cs がエンキュー、デキューの両方の機能を持っていただろうか。それを使ってテストしてみよう。

./apery はバックグラウンドで起動させる。

```
# ./apery &
[1] 16835
root@tk2-217-18401:/home/★user/shogi/ukamuse_sdt4_child4/bin# jobs
[1]+  Running                 ./apery &
```

tamesi33a2_cs.cs は対話タイプではなかったようだ。
対話型は tamesi33a1_cs.cs では？

tamesi33a1_cs.cs は 自分のエンキューしたメッセージに、daze を付けてデキューするだけなので対話的でない。

tamesi34_cs.cs を次回に作りたい。

# C#でテスト用の tamesi34_cs.cs を作る。
Visual Studio 2015 で AMQP-CPP のテストができる C# 版でひとまず用意することにする。

- エンキュー先メッセージ・キュー名を指定する
- デキュー先メッセージ・キュー名を指定する
- デキューは常に監視し、割込みさせる
- 常に　標準入力は　エンキューさせる

ソースコードはこんな感じでどうか？

```
// OS  : Windows 10
// IDE : Visual Studio 2015
//       Install : NuGet   : Install-Package RabbitMQ.Client -Version 4.1.1
//
// OS  : Ubuntu 16.04
//       Compile : Command : mcs /r:RabbitMQ.Client.dll -define:UBUNTU tamesi34_cs.cs
//               : Command : chmod 755 tamesi34_cs.cs
//       Execute : Command : // フォアグラウンドで実行する
//                         : ./tamesi34_cs.exe
//       Check   : Command : // キューの中身の数を調べる
//                         : rabbitmqctl list_queues
//--------------------------------------------------------------------------------
// tamesi34_cs.cs

// Ubuntu の RabbitMQ はソースのバージョンが古いのか、API が異なった。
// #define UBUNTU

using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Text;

namespace UsagiMQ
{
    /// <summary>
    /// メッセージを　エンキュー、デキューします。
    /// エンキューは「1112」、デキューは「1113」キューに向けて行います。
    /// 
    /// 参照 : QueueDeclare http://docs.spring.io/spring-amqp-net/docs/1.0.x/api/html/Spring.Messaging.Amqp.Rabbit~Spring.Messaging.Amqp.Rabbit.Connection.CachedModel~QueueDeclare(String,Boolean,Boolean,Boolean,Boolean,Boolean,IDictionary).html
    /// 参照 : EventingBasicConsumer https://www.rabbitmq.com/releases/rabbitmq-dotnet-client/v1.4.0/rabbitmq-dotnet-client-1.4.0-net-2.0-htmldoc/type-RabbitMQ.Client.Events.EventingBasicConsumer.html
    /// 参照 : BasicConsume https://www.rabbitmq.com/releases/rabbitmq-dotnet-client/v1.4.0/rabbitmq-dotnet-client-1.4.0-net-2.0-htmldoc/type-RabbitMQ.Client.IModel.html#method-M:RabbitMQ.Client.IModel.BasicConsume(System.UInt16,System.String,System.Boolean,System.Collections.IDictionary,RabbitMQ.Client.IBasicConsumer)
    /// 参照 : C#でconstな配列を実現する (もっとクールにプログラミング) http://pgnote.net/?p=885
    /// </summary>
    class Program
    {
        public const string HOST_NAME = "localhost";
        public static string[] QUEUE_NAMES = new string[2];
        public const int ENQUEUE_INDEX = 0;
        public const int DEQUEUE_INDEX = 1;

        public static ConnectionFactory GetFactory()
        {
            if (null == m_factory_)
            {
                m_factory_ = new ConnectionFactory() { HostName = HOST_NAME };
            }
            return m_factory_;
        }
        static ConnectionFactory m_factory_;

        public static IConnection GetConnection()
        {
            if (null == m_connection_)
            {
                m_connection_ = GetFactory().CreateConnection();
            }
            return m_connection_;
        }
        static IConnection m_connection_;

        public static IModel GetChannel(int index)
        {
            if (null == m_channels_[index])
            {
                m_channels_[index] = GetConnection().CreateModel();
#if UBUNTU
                // Ubuntuでは何故か Spring.Messaging.Amqp.Rabbit の引数 7 つのやつになっている。
                m_channels_[index].QueueDeclare(QUEUE_NAMES[index], false, false, false, false, false, null);
#else
                m_channels_[index].QueueDeclare(QUEUE_NAMES[index], false, false, false, null);
#endif
            }
            return m_channels_[index];
        }
        static IModel[] m_channels_ = new IModel[2];

        public static EventingBasicConsumer GetConsumer(int index)
        {
            if (null == m_consumers_[index])
            {
#if UBUNTU
                // Ubuntuでは何故か v1.4.0 の引数が 0 個のやつになっている。調べたが引数が1個～6個のものは無かった。
                m_consumers_[index] = new EventingBasicConsumer();
#else
                m_consumers_[index] = new EventingBasicConsumer(GetChannel(index));
#endif

            }
            return m_consumers_[index];
        }
        static EventingBasicConsumer[] m_consumers_ = new EventingBasicConsumer[2];

        /// <summary>
        /// 受信できたときに割り込んでくる処理
        /// </summary>
#if UBUNTU
        public static BasicDeliverEventHandler GetDequeueHandler()
#else
        public static EventHandler<BasicDeliverEventArgs> GetDequeueHandler()
#endif
        {
            if (null == m_dequeueHandler_)
            {
#if UBUNTU
                m_dequeueHandler_ = new BasicDeliverEventHandler((model, ea) =>
#else
                m_dequeueHandler_ = new EventHandler<BasicDeliverEventArgs>((model, ea) =>
#endif
                {
                    byte[] body = ea.Body;
                    string message = Encoding.UTF8.GetString(body);
                    Console.WriteLine("<---- [interrupt!] Dequeue(^q^) {0}", message);
                });
            }

            return m_dequeueHandler_;
        }

#if UBUNTU
        static BasicDeliverEventHandler m_dequeueHandler_;
#else
        static EventHandler<BasicDeliverEventArgs> m_dequeueHandler_;
#endif

        /// <summary>
        /// 対応するオープンは無いけれど、開けたら閉める、を完璧に対応する必要がある。
        /// </summary>
        static void CloseConnection()
        {
            if (null != m_connection_)
            {
                m_connection_.Close();
                m_connection_ = null;
            }
        }
        /// <summary>
        /// 対応するオープンは無いけれど、開けたら閉める、を完璧に対応する必要がある。
        /// </summary>
        static void CloseChannel(int index)
        {
            if (null != m_channels_[index])
            {
                m_channels_[index].Close();
                m_channels_[index] = null;
            }
        }

        static void Main(string[] args)
        {
            string line;
            Console.WriteLine(@"エンキュー先のメッセージ・キューの名前を入れろだぜ☆（＾～＾）");
            line = Console.ReadLine();
            QUEUE_NAMES[ENQUEUE_INDEX] = line;

            Console.WriteLine(@"デキュー先のメッセージ・キューの名前を入れろだぜ☆（＾～＾）");
            line = Console.ReadLine();
            QUEUE_NAMES[DEQUEUE_INDEX] = line;

            StartConsume();

            Console.WriteLine(@"終了するときは[Ctrl]+[C]キーを押せだぜ☆（＾～＾）
エンキューするときはメッセージを打ち込んで[Enter]キーを押せだぜ☆(＾◇＾)");

            for (;;)
            {
                // "Hello World!" などを入力
                line = Console.ReadLine();
                Enqueue(line);
            }

            // ここには来ない
            // CloseConnection();
        }

        static void Enqueue(string message)
        {
            IModel channel = GetChannel(ENQUEUE_INDEX);

            byte[] body = Encoding.UTF8.GetBytes(message);
            channel.BasicPublish("", QUEUE_NAMES[ENQUEUE_INDEX], null, body);

            Console.WriteLine(" Enqueue(^q^) {0}", message);

            // 対応するオープンは無いが、ちゃんと閉じないと、レシーブしてくれない。
            CloseChannel(ENQUEUE_INDEX);
        }

        static void StartConsume()
        {
            IModel channel = GetChannel(DEQUEUE_INDEX);
            EventingBasicConsumer consumer = GetConsumer(DEQUEUE_INDEX);

            // 受信できたときに割り込んでくる処理
            consumer.Received += GetDequeueHandler();

#if UBUNTU
            // Ubuntuでは何故か引数が 5 個のやつになっている。
            channel.BasicConsume( QUEUE_NAMES[DEQUEUE_INDEX], true, "", null, consumer);
#else
            channel.BasicConsume( QUEUE_NAMES[DEQUEUE_INDEX], true, consumer);
#endif
            // 終了はさせない
            // consumer.Received -= GetReceiveHandler();
            // CloseChannel(DEQUEUE_INDEX);
        }
    }
}
```

これでどうか。

```
# ps aux | grep apery
root      2726  3.3  0.0 128660    48 ?        Sl   Mar11  88:51 /usr/bin/cli ./tamesi31_cs.exe ../ukamuse_sdt4/bin/apery
root     16835  0.0 14.9 1408108 151936 ?      Sl   Mar12   0:00 ./apery
root     22496  0.0  0.0  12936   988 pts/7    S+   03:47   0:00 grep --color=auto apery
```

apery は生きてるように見えるが反応はない。一度止めてみるか。

```
# rabbitmqctl list_queues
Listing queues ...
1112    0
1111    0
1113    0
myqueue 25
```

myqueue の内容もデキューしとこ。

```
# ./tamesi34_cs.exe
エンキュー先のメッセージ・キューの名前を入れろだぜ☆（＾～＾）
myqueue
デキュー先のメッセージ・キューの名前を入れろだぜ☆（＾～＾）
myqueue

Unhandled Exception:
RabbitMQ.Client.Exceptions.OperationInterruptedException: The AMQP operation was interrupted: AMQP close-reason, initiated by Peer, code=406, text="PRECONDITION_FAILED - inequivalent arg 'durable' for queue 'myqueue' in vhost '/': received 'false' but current is 'true'", classId=50, methodId=10, cause=
  at RabbitMQ.Client.Impl.SimpleBlockingRpcContinuation.GetReply () <0x41c3e0f0 + 0x00103> in <filename unknown>:0
  at RabbitMQ.Client.Impl.ModelBase.ModelRpc (RabbitMQ.Client.Impl.MethodBase method, RabbitMQ.Client.Impl.ContentHeaderBase header, System.Byte[] body) <0x41c3bfa0 + 0x000fd> in <filename unknown>:0
  at RabbitMQ.Client.Framing.Impl.v0_8.Model.QueueDeclare (System.String queue, Boolean passive, Boolean durable, Boolean exclusive, Boolean autoDelete, Boolean nowait, IDictionary arguments) <0x41c402b0 + 0x00113> in <filename unknown>:0
  at UsagiMQ.Program.GetChannel (Int32 index) <0x41c01c30 + 0x000ce> in <filename unknown>:0
  at UsagiMQ.Program.StartConsume () <0x41c01b70 + 0x0000f> in <filename unknown>:0
  at UsagiMQ.Program.Main (System.String[] args) <0x41bfe000 + 0x0008f> in <filename unknown>:0
[ERROR] FATAL UNHANDLED EXCEPTION: RabbitMQ.Client.Exceptions.OperationInterruptedException: The AMQP operation was interrupted: AMQP close-reason, initiated by Peer, code=406, text="PRECONDITION_FAILED - inequivalent arg 'durable' for queue 'myqueue' in vhost '/': received 'false' but current is 'true'", classId=50, methodId=10, cause=
  at RabbitMQ.Client.Impl.SimpleBlockingRpcContinuation.GetReply () <0x41c3e0f0 + 0x00103> in <filename unknown>:0
  at RabbitMQ.Client.Impl.ModelBase.ModelRpc (RabbitMQ.Client.Impl.MethodBase method, RabbitMQ.Client.Impl.ContentHeaderBase header, System.Byte[] body) <0x41c3bfa0 + 0x000fd> in <filename unknown>:0
  at RabbitMQ.Client.Framing.Impl.v0_8.Model.QueueDeclare (System.String queue, Boolean passive, Boolean durable, Boolean exclusive, Boolean autoDelete, Boolean nowait, IDictionary arguments) <0x41c402b0 + 0x00113> in <filename unknown>:0
  at UsagiMQ.Program.GetChannel (Int32 index) <0x41c01c30 + 0x000ce> in <filename unknown>:0
  at UsagiMQ.Program.StartConsume () <0x41c01b70 + 0x0000f> in <filename unknown>:0
  at UsagiMQ.Program.Main (System.String[] args) <0x41bfe000 + 0x0008f> in <filename unknown>:0
```

接続した　メッセージ・キュー　と設定の違う方法でアクセスしたときに起こるエラーだろう。対応するコードを考える。

## キューの寿命も質問するコード

これでどうだろうか？

```
// OS  : Windows 10
// IDE : Visual Studio 2015
//       Install : NuGet   : Install-Package RabbitMQ.Client -Version 4.1.1
//
// OS  : Ubuntu 16.04
//       Compile : Command : mcs /r:RabbitMQ.Client.dll -define:UBUNTU tamesi34_cs.cs
//               : Command : chmod 755 tamesi34_cs.cs
//       Execute : Command : // フォアグラウンドで実行する
//                         : ./tamesi34_cs.exe
//       Check   : Command : // キューの中身の数を調べる
//                         : rabbitmqctl list_queues
//
//       Library : Website : CopernicaMarketingSoftware/AMQP-CPP https://github.com/CopernicaMarketingSoftware/AMQP-CPP
//--------------------------------------------------------------------------------
// tamesi34_cs.cs

// Ubuntu の RabbitMQ はソースのバージョンが古いのか、API が異なった。
// #define UBUNTU

using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Text;

namespace UsagiMQ
{
    /// <summary>
    /// メッセージを　エンキュー、デキューします。
    /// キューの名前は指定してください。
    /// デキューは割込みを受け付けます。
    /// 
    /// 参照 : QueueDeclare (v1.0) http://docs.spring.io/spring-amqp-net/docs/1.0.x/api/html/Spring.Messaging.Amqp.Rabbit~Spring.Messaging.Amqp.Rabbit.Connection.CachedModel~QueueDeclare(String,Boolean,Boolean,Boolean,Boolean,Boolean,IDictionary).html
    /// 参照 : EventingBasicConsumer https://www.rabbitmq.com/releases/rabbitmq-dotnet-client/v1.4.0/rabbitmq-dotnet-client-1.4.0-net-2.0-htmldoc/type-RabbitMQ.Client.Events.EventingBasicConsumer.html
    /// 参照 : BasicConsume https://www.rabbitmq.com/releases/rabbitmq-dotnet-client/v1.4.0/rabbitmq-dotnet-client-1.4.0-net-2.0-htmldoc/type-RabbitMQ.Client.IModel.html#method-M:RabbitMQ.Client.IModel.BasicConsume(System.UInt16,System.String,System.Boolean,System.Collections.IDictionary,RabbitMQ.Client.IBasicConsumer)
    /// 参照 : C#でconstな配列を実現する (もっとクールにプログラミング) http://pgnote.net/?p=885
    /// </summary>
    class Program
    {
        const string HOST_NAME = "localhost";
        static string[] QUEUE_NAMES = new string[2];
        /// <summary>
        /// キューの寿命
        /// (0) durable    : RabbitMQが止まってもキューを残す
        /// (1) autodelete : コンシューマーが１人も接続していなかったら消す
        /// (2) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
        /// (3) exclusive  : この接続でだけ使える。この接続が切れたら消す
        /// </summary>
        static int[] lifeSpans_queue = new int[] { 0, 0 };
        static bool[]
            durable_lifeSpans = new bool[2],
            autodelete_lifeSpans = new bool[2],
            passive_lifeSpans = new bool[2],
            exclusive_lifeSpans = new bool[2];
        const int ENQUEUE_INDEX = 0;
        const int DEQUEUE_INDEX = 1;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="index_queue"></param>
        /// <param name="name_queue"></param>
        /// <param name="lifeSpan">
        /// (0) durable
        /// (1) autodelete
        /// (2) passive
        /// (3) exclusive
        /// </param>
        static void SetLifeSpan(int index_queue, string name_queue, int lifeSpan)
        {
            QUEUE_NAMES[index_queue] = name_queue;
            lifeSpans_queue[index_queue] = lifeSpan;

            // 一旦クリアー
            durable_lifeSpans[index_queue] = false;
            autodelete_lifeSpans[index_queue] = false;
            passive_lifeSpans[index_queue] = false;
            exclusive_lifeSpans[index_queue] = false;

            switch (lifeSpan)
            {
                case 0: // durable
                    durable_lifeSpans[index_queue] = true;
                    break;
                case 1: // autodelete
                    autodelete_lifeSpans[index_queue] = true;
                    break;
                case 3: // exclusive
                    exclusive_lifeSpans[index_queue] = true;
                    break;
                default: // passive
                    passive_lifeSpans[index_queue] = true;
                    break;
            }
        }


        public static ConnectionFactory GetFactory()
        {
            if (null == m_factory_)
            {
                m_factory_ = new ConnectionFactory() { HostName = HOST_NAME };
            }
            return m_factory_;
        }
        static ConnectionFactory m_factory_;

        public static IConnection GetConnection()
        {
            if (null == m_connection_)
            {
                m_connection_ = GetFactory().CreateConnection();
            }
            return m_connection_;
        }
        static IConnection m_connection_;

        public static IModel GetChannel(int index)
        {
            if (null == m_channels_[index])
            {
                m_channels_[index] = GetConnection().CreateModel();
#if UBUNTU
                // Ubuntuでは何故か Spring.Messaging.Amqp.Rabbit の引数 7 つのやつになっている。
                m_channels_[index].QueueDeclare(QUEUE_NAMES[index], passive_lifeSpans[index], durable_lifeSpans[index], exclusive_lifeSpans[index], autodelete_lifeSpans[index], false, null);
#else
                m_channels_[index].QueueDeclare(QUEUE_NAMES[index], durable_lifeSpans[index], exclusive_lifeSpans[index], autodelete_lifeSpans[index], null);
#endif
            }
            return m_channels_[index];
        }
        static IModel[] m_channels_ = new IModel[2];

        public static EventingBasicConsumer GetConsumer(int index)
        {
            if (null == m_consumers_[index])
            {
#if UBUNTU
                // Ubuntuでは何故か v1.4.0 の引数が 0 個のやつになっている。調べたが引数が1個～6個のものは無かった。
                m_consumers_[index] = new EventingBasicConsumer();
#else
                m_consumers_[index] = new EventingBasicConsumer(GetChannel(index));
#endif

            }
            return m_consumers_[index];
        }
        static EventingBasicConsumer[] m_consumers_ = new EventingBasicConsumer[2];

        /// <summary>
        /// 受信できたときに割り込んでくる処理
        /// </summary>
#if UBUNTU
        public static BasicDeliverEventHandler GetDequeueHandler()
#else
        public static EventHandler<BasicDeliverEventArgs> GetDequeueHandler()
#endif
        {
            if (null == m_dequeueHandler_)
            {
#if UBUNTU
                m_dequeueHandler_ = new BasicDeliverEventHandler((model, ea) =>
#else
                m_dequeueHandler_ = new EventHandler<BasicDeliverEventArgs>((model, ea) =>
#endif
                {
                    byte[] body = ea.Body;
                    string message = Encoding.UTF8.GetString(body);
                    Console.WriteLine("<---- [interrupt!] Dequeue(^q^) {0}", message);
                });
            }

            return m_dequeueHandler_;
        }

#if UBUNTU
        static BasicDeliverEventHandler m_dequeueHandler_;
#else
        static EventHandler<BasicDeliverEventArgs> m_dequeueHandler_;
#endif

        /// <summary>
        /// 対応するオープンは無いけれど、開けたら閉める、を完璧に対応する必要がある。
        /// </summary>
        static void CloseConnection()
        {
            if (null != m_connection_)
            {
                m_connection_.Close();
                m_connection_ = null;
            }
        }
        /// <summary>
        /// 対応するオープンは無いけれど、開けたら閉める、を完璧に対応する必要がある。
        /// </summary>
        static void CloseChannel(int index)
        {
            if (null != m_channels_[index])
            {
                m_channels_[index].Close();
                m_channels_[index] = null;
            }
        }

        static void Main(string[] args)
        {
            Console.Write(@"エンキュー先のメッセージ・キューの名前を入れろだぜ☆（＾～＾）
Queue name? > ");
            string queueName_enqueue = Console.ReadLine();

            Console.Write(@"エンキュー先のメッセージ・キューの寿命を選んで入れろだぜ☆（＾～＾）
(0) durable    : RabbitMQが止まってもキューを残す
(1) autodelete : コンシューマーが１人も接続していなかったら消す
(2) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(3) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > ");
            int lifeSpan_enqueue = int.Parse( Console.ReadLine());
            SetLifeSpan(ENQUEUE_INDEX, queueName_enqueue, lifeSpan_enqueue);

            Console.Write(@"デキュー先のメッセージ・キューの名前を入れろだぜ☆（＾～＾）
Queue name? > ");
            string queueName_dequeue = Console.ReadLine();

            Console.Write(@"デキュー先のメッセージ・キューの寿命を選んで入れろだぜ☆（＾～＾）
(0) durable    : RabbitMQが止まってもキューを残す
(1) autodelete : コンシューマーが１人も接続していなかったら消す
(2) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(3) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > ");
            int lifeSpan_dequeue = int.Parse(Console.ReadLine());

            StartConsume(queueName_dequeue, lifeSpan_dequeue);

            Console.Write(@"終了するときは[Ctrl]+[C]キーを押せだぜ☆（＾～＾）
エンキューするときはメッセージを打ち込んで[Enter]キーを押せだぜ☆(＾◇＾)
Enqueue? > ");

            for (;;)
            {
                // "Hello World!" などを入力
                string line = Console.ReadLine();
                Enqueue(line);
                Console.Write(@"Enqueue? > ");
            }

            // ここには来ない
            // CloseConnection();
        }

        static void Enqueue(string message)
        {
            IModel channel = GetChannel(ENQUEUE_INDEX);

            byte[] body = Encoding.UTF8.GetBytes(message);
            channel.BasicPublish("", QUEUE_NAMES[ENQUEUE_INDEX], null, body);

            Console.WriteLine(" Enqueue(^q^) {0}", message);

            // 対応するオープンは無いが、ちゃんと閉じないと、レシーブしてくれない。
            CloseChannel(ENQUEUE_INDEX);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="name_queue">メッセージ・キューの名前</param>
        /// <param name="lifeSpan_queue">既存のメッセージ・キューの場合、メッセージ・キューの設定は合わせる必要がある。
        /// durable    : RabbitMQが止まってもキューを残す
        /// autodelete : コンシューマーが１人も接続していなかったら消す
        /// passive    : キューが存在するかどうかチェックするだけで、中身を見ない時はこれ
        /// exclusive  : この接続でだけ使える。この接続が切れたら消す
        /// </param>
        static void StartConsume(string name_queue, int lifeSpan_queue)
        {
            SetLifeSpan(DEQUEUE_INDEX, name_queue, lifeSpan_queue);

            IModel channel = GetChannel(DEQUEUE_INDEX);
            EventingBasicConsumer consumer = GetConsumer(DEQUEUE_INDEX);

            // 受信できたときに割り込んでくる処理
            consumer.Received += GetDequeueHandler();

#if UBUNTU
            // Ubuntuでは何故か引数が 5 個のやつになっている。
            channel.BasicConsume( QUEUE_NAMES[DEQUEUE_INDEX], true, "", null, consumer);
#else
            channel.BasicConsume( QUEUE_NAMES[DEQUEUE_INDEX], true, consumer);
#endif
            // 終了はさせない
            // consumer.Received -= GetReceiveHandler();
            // CloseChannel(DEQUEUE_INDEX);
        }
    }
}
```

```
デキュー先のメッセージ・キューの寿命を選んで入れろだぜ☆（＾～＾）
(0) durable    : RabbitMQが止まってもキューを残す
(1) autodelete : コンシューマーが１人も接続していなかったら消す
(2) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(3) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 0

Unhandled Exception:
RabbitMQ.Client.Exceptions.OperationInterruptedException: The AMQP operation was interrupted: AMQP close-reason, initiated by Peer, code=406, text="PRECONDITION_FAILED - inequivalent arg 'durable' for queue '1113' in vhost '/': received 'true' but current is 'false'", classId=50, methodId=10, cause=
  at RabbitMQ.Client.Impl.SimpleBlockingRpcContinuation.GetReply () <0x40246570 + 0x00107> in <filename unknown>:0
  at RabbitMQ.Client.Impl.ModelBase.ModelRpc (RabbitMQ.Client.Impl.MethodBase method, RabbitMQ.Client.Impl.ContentHeaderBase header, System.Byte[] body) <0x402442f0 + 0x00101> in <filename unknown>:0
  at RabbitMQ.Client.Framing.Impl.v0_8.Model.QueueDeclare (System.String queue, Boolean passive, Boolean durable, Boolean exclusive, Boolean autoDelete, Boolean nowait, IDictionary arguments) <0x402487b0 + 0x00113> in <filename unknown>:0
  at UsagiMQ.Program.GetChannel (Int32 index) <0x40209f90 + 0x00138> in <filename unknown>:0
  at UsagiMQ.Program.StartConsume (System.String name_queue, Int32 lifeSpan_queue) <0x40209eb0 + 0x0002b> in <filename unknown>:0
  at UsagiMQ.Program.Main (System.String[] args) <0x40205d50 + 0x000df> in <filename unknown>:0
[ERROR] FATAL UNHANDLED EXCEPTION: RabbitMQ.Client.Exceptions.OperationInterruptedException: The AMQP operation was interrupted: AMQP close-reason, initiated by Peer, code=406, text="PRECONDITION_FAILED - inequivalent arg 'durable' for queue '1113' in vhost '/': received 'true' but current is 'false'", classId=50, methodId=10, cause=
  at RabbitMQ.Client.Impl.SimpleBlockingRpcContinuation.GetReply () <0x40246570 + 0x00107> in <filename unknown>:0
  at RabbitMQ.Client.Impl.ModelBase.ModelRpc (RabbitMQ.Client.Impl.MethodBase method, RabbitMQ.Client.Impl.ContentHeaderBase header, System.Byte[] body) <0x402442f0 + 0x00101> in <filename unknown>:0
  at RabbitMQ.Client.Framing.Impl.v0_8.Model.QueueDeclare (System.String queue, Boolean passive, Boolean durable, Boolean exclusive, Boolean autoDelete, Boolean nowait, IDictionary arguments) <0x402487b0 + 0x00113> in <filename unknown>:0
  at UsagiMQ.Program.GetChannel (Int32 index) <0x40209f90 + 0x00138> in <filename unknown>:0
  at UsagiMQ.Program.StartConsume (System.String name_queue, Int32 lifeSpan_queue) <0x40209eb0 + 0x0002b> in <filename unknown>:0
  at UsagiMQ.Program.Main (System.String[] args) <0x40205d50 + 0x000df> in <filename unknown>:0
```

寿命を何に設定したのか　忘れたときはどうするのか……。

## キュー削除機能を付けたものの、属性が分からない
属性が分からないと削除できないんだろうか？

```
// OS  : Windows 10
// IDE : Visual Studio 2015
//       Install : NuGet   : Install-Package RabbitMQ.Client -Version 4.1.1
//
// OS  : Ubuntu 16.04
//       Compile : Command : mcs /r:RabbitMQ.Client.dll -define:UBUNTU tamesi34_cs.cs
//               : Command : chmod 755 tamesi34_cs.cs
//       Execute : Command : // フォアグラウンドで実行する
//                         : ./tamesi34_cs.exe
//       Check   : Command : // キューの中身の数を調べる
//                         : rabbitmqctl list_queues
//
// Library : RabbitMQ
//           Refference : Website : RabbitMQ http://www.rabbitmq.com/
//                      : Website : RabbitMQ管理コマンド（rabbitmqctl）使い方 (Qiita) http://qiita.com/tamikura@github/items/5293cda4c0026b2d7022
//                      : Website : amqpを使ってRabbitMQのキューを操作する (Qiita) http://qiita.com/tamikura@github/items/a268afa51c5537ca4fe6
//
// Library : AMQP-CPP
//           Refference : Website : CopernicaMarketingSoftware/AMQP-CPP https://github.com/CopernicaMarketingSoftware/AMQP-CPP
//--------------------------------------------------------------------------------
// tamesi34_cs.cs

// Ubuntu の RabbitMQ はソースのバージョンが古いのか、API が異なった。
// #define UBUNTU

using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Text;

namespace UsagiMQ
{
    /// <summary>
    /// メッセージを　エンキューします。
    /// キューの名前は指定してください。
    /// デキューは割込みを受け付けます。
    /// 
    /// 参照 : QueueDeclare (v1.0) http://docs.spring.io/spring-amqp-net/docs/1.0.x/api/html/Spring.Messaging.Amqp.Rabbit~Spring.Messaging.Amqp.Rabbit.Connection.CachedModel~QueueDeclare(String,Boolean,Boolean,Boolean,Boolean,Boolean,IDictionary).html
    /// 参照 : EventingBasicConsumer https://www.rabbitmq.com/releases/rabbitmq-dotnet-client/v1.4.0/rabbitmq-dotnet-client-1.4.0-net-2.0-htmldoc/type-RabbitMQ.Client.Events.EventingBasicConsumer.html
    /// 参照 : BasicConsume https://www.rabbitmq.com/releases/rabbitmq-dotnet-client/v1.4.0/rabbitmq-dotnet-client-1.4.0-net-2.0-htmldoc/type-RabbitMQ.Client.IModel.html#method-M:RabbitMQ.Client.IModel.BasicConsume(System.UInt16,System.String,System.Boolean,System.Collections.IDictionary,RabbitMQ.Client.IBasicConsumer)
    /// 参照 : C#でconstな配列を実現する (もっとクールにプログラミング) http://pgnote.net/?p=885
    /// </summary>
    class Program
    {
        const int ENQUEUE_INDEX = 0;
        const int DEQUEUE_INDEX = 1;
        const int DELETEQUEUE_INDEX = 2;
        const int NUM_INDEX = 3;
        const string HOST_NAME = "localhost";
        static string[] QUEUE_NAMES = new string[NUM_INDEX];
        /// <summary>
        /// キューの寿命
        /// (0) durable    : RabbitMQが止まってもキューを残す
        /// (1) autodelete : コンシューマーが１人も接続していなかったら消す
        /// (2) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
        /// (3) exclusive  : この接続でだけ使える。この接続が切れたら消す
        /// </summary>
        static int[] lifeSpans_queue = new int[NUM_INDEX];
        static bool[]
            durable_lifeSpans = new bool[NUM_INDEX],
            autodelete_lifeSpans = new bool[NUM_INDEX],
            passive_lifeSpans = new bool[NUM_INDEX],
            exclusive_lifeSpans = new bool[NUM_INDEX];
        /// <summary>
        /// 
        /// </summary>
        /// <param name="index_queue"></param>
        /// <param name="name_queue"></param>
        /// <param name="lifeSpan">
        /// (0) durable
        /// (1) autodelete
        /// (2) passive
        /// (3) exclusive
        /// </param>
        static void SetLifeSpan(int index_queue, string name_queue, int lifeSpan)
        {
            QUEUE_NAMES[index_queue] = name_queue;
            lifeSpans_queue[index_queue] = lifeSpan;

            // 一旦クリアー
            durable_lifeSpans[index_queue] = false;
            autodelete_lifeSpans[index_queue] = false;
            passive_lifeSpans[index_queue] = false;
            exclusive_lifeSpans[index_queue] = false;

            switch (lifeSpan)
            {
                case 0: // durable
                    durable_lifeSpans[index_queue] = true;
                    break;
                case 1: // autodelete
                    autodelete_lifeSpans[index_queue] = true;
                    break;
                case 3: // exclusive
                    exclusive_lifeSpans[index_queue] = true;
                    break;
                default: // passive
                    passive_lifeSpans[index_queue] = true;
                    break;
            }
        }


        public static ConnectionFactory GetFactory()
        {
            if (null == m_factory_)
            {
                m_factory_ = new ConnectionFactory() { HostName = HOST_NAME };
            }
            return m_factory_;
        }
        static ConnectionFactory m_factory_;

        public static IConnection GetConnection()
        {
            if (null == m_connection_)
            {
                m_connection_ = GetFactory().CreateConnection();
            }
            return m_connection_;
        }
        static IConnection m_connection_;

        public static IModel GetChannel(int index)
        {
            if (null == m_channels_[index])
            {
                m_channels_[index] = GetConnection().CreateModel();
#if UBUNTU
                // Ubuntuでは何故か Spring.Messaging.Amqp.Rabbit の引数 7 つのやつになっている。
                m_channels_[index].QueueDeclare(QUEUE_NAMES[index], passive_lifeSpans[index], durable_lifeSpans[index], exclusive_lifeSpans[index], autodelete_lifeSpans[index], false, null);
#else
                m_channels_[index].QueueDeclare(QUEUE_NAMES[index], durable_lifeSpans[index], exclusive_lifeSpans[index], autodelete_lifeSpans[index], null);
#endif
            }
            return m_channels_[index];
        }
        static IModel[] m_channels_ = new IModel[NUM_INDEX];

        public static EventingBasicConsumer GetConsumer(int index)
        {
            if (null == m_consumers_[index])
            {
#if UBUNTU
                // Ubuntuでは何故か v1.4.0 の引数が 0 個のやつになっている。調べたが引数が1個～6個のものは無かった。
                m_consumers_[index] = new EventingBasicConsumer();
#else
                m_consumers_[index] = new EventingBasicConsumer(GetChannel(index));
#endif

            }
            return m_consumers_[index];
        }
        static EventingBasicConsumer[] m_consumers_ = new EventingBasicConsumer[NUM_INDEX];

        /// <summary>
        /// 受信できたときに割り込んでくる処理
        /// </summary>
#if UBUNTU
        public static BasicDeliverEventHandler GetDequeueHandler()
#else
        public static EventHandler<BasicDeliverEventArgs> GetDequeueHandler()
#endif
        {
            if (null == m_dequeueHandler_)
            {
#if UBUNTU
                m_dequeueHandler_ = new BasicDeliverEventHandler((model, ea) =>
#else
                m_dequeueHandler_ = new EventHandler<BasicDeliverEventArgs>((model, ea) =>
#endif
                {
                    byte[] body = ea.Body;
                    string message = Encoding.UTF8.GetString(body);
                    Console.WriteLine("<---- [interrupt!] Dequeue(^q^) {0}", message);
                });
            }

            return m_dequeueHandler_;
        }

#if UBUNTU
        static BasicDeliverEventHandler m_dequeueHandler_;
#else
        static EventHandler<BasicDeliverEventArgs> m_dequeueHandler_;
#endif

        /// <summary>
        /// 対応するオープンは無いけれど、開けたら閉める、を完璧に対応する必要がある。
        /// </summary>
        static void CloseConnection()
        {
            if (null != m_connection_)
            {
                m_connection_.Close();
                m_connection_ = null;
            }
        }
        /// <summary>
        /// 対応するオープンは無いけれど、開けたら閉める、を完璧に対応する必要がある。
        /// </summary>
        static void CloseChannel(int index)
        {
            if (null != m_channels_[index])
            {
                m_channels_[index].Close();
                m_channels_[index] = null;
            }
        }

        static void Main(string[] args)
        {
            //----------------------------------------
            // Delete
            //----------------------------------------
            for (;;)
            {
                Console.Write(@"削除したいキューがあれば名前を、無ければ空文字列を入れろだぜ☆（＾～＾）
キュー名を入力    : キューを削除します
空文字列で[Enter] : 次のステップへ進む
Name or empty ? > ");
                string queueName_delete = Console.ReadLine();
                if (""== queueName_delete.Trim())
                {
                    break;
                }

                Console.Write(@"削除するメッセージ・キューの寿命を選べだぜ☆（＾～＾）
(0) durable    : RabbitMQが止まってもキューを残す
(1) autodelete : コンシューマーが１人も接続していなかったら消す
(2) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(3) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > ");
                int lifeSpan_delete = int.Parse(Console.ReadLine());
                SetLifeSpan(DELETEQUEUE_INDEX, queueName_delete, lifeSpan_delete);
                uint result = DeleteQueue();
                Console.WriteLine(@"["+ queueName_delete + "]キューを削除したはずだぜ☆（＾～＾） result=["+ result + "]");
            }

            //----------------------------------------
            // Enqueue settings
            //----------------------------------------
            for (;;)
            {
                Console.Write(@"エンキュー先のメッセージ・キューの名前を入れろだぜ☆（＾～＾）
Queue name? > ");
                string queueName_enqueue = Console.ReadLine();

                Console.Write(@"エンキュー先のメッセージ・キューの寿命を選べだぜ☆（＾～＾）
(0) durable    : RabbitMQが止まってもキューを残す
(1) autodelete : コンシューマーが１人も接続していなかったら消す
(2) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(3) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > ");
                int lifeSpan_enqueue;
                if(int.TryParse(Console.ReadLine(),out lifeSpan_enqueue))
                {
                    SetLifeSpan(ENQUEUE_INDEX, queueName_enqueue, lifeSpan_enqueue);
                    break;
                }
            }

            //----------------------------------------
            // Enqueue settings
            //----------------------------------------
            for (;;)
            {
                Console.Write(@"デキュー先のメッセージ・キューの名前を入れろだぜ☆（＾～＾）
Queue name? > ");
                string queueName_dequeue = Console.ReadLine();

                Console.Write(@"デキュー先のメッセージ・キューの寿命を選べだぜ☆（＾～＾）
(0) durable    : RabbitMQが止まってもキューを残す
(1) autodelete : コンシューマーが１人も接続していなかったら消す
(2) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(3) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > ");
                int lifeSpan_dequeue;
                if(int.TryParse(Console.ReadLine(),out lifeSpan_dequeue))
                {
                    StartConsume(queueName_dequeue, lifeSpan_dequeue);
                    break;
                }
            }

            Console.Write(@"終了するときは[Ctrl]+[C]キーを押せだぜ☆（＾～＾）
エンキューするときはメッセージを打ち込んで[Enter]キーを押せだぜ☆(＾◇＾)
Enqueue? > ");
            for (;;)
            {
                // "Hello World!" などを入力
                string line = Console.ReadLine();
                Enqueue(line);
                Console.Write(@"Enqueue? > ");
            }

            // ここには来ない
            // CloseConnection();
        }

        static uint DeleteQueue()
        {
            IModel channel = GetChannel(DELETEQUEUE_INDEX);
            uint result = channel.QueueDelete(QUEUE_NAMES[DELETEQUEUE_INDEX],true,true);

            // 対応するオープンは無いが、ちゃんと閉じないと、レシーブしてくれない。
            CloseChannel(DELETEQUEUE_INDEX);
            return result;
        }

        static void Enqueue(string message)
        {
            IModel channel = GetChannel(ENQUEUE_INDEX);

            byte[] body = Encoding.UTF8.GetBytes(message);
            channel.BasicPublish("", QUEUE_NAMES[ENQUEUE_INDEX], null, body);

            Console.WriteLine(" Enqueue(^q^) {0}", message);

            // 対応するオープンは無いが、ちゃんと閉じないと、レシーブしてくれない。
            CloseChannel(ENQUEUE_INDEX);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="name_queue">メッセージ・キューの名前</param>
        /// <param name="lifeSpan_queue">既存のメッセージ・キューの場合、メッセージ・キューの設定は合わせる必要がある。
        /// (0) durable    : RabbitMQが止まってもキューを残す
        /// (1) autodelete : コンシューマーが１人も接続していなかったら消す
        /// (2) passive    : キューが存在するかどうかチェックするだけで、中身を見ない時はこれ
        /// (3) exclusive  : この接続でだけ使える。この接続が切れたら消す
        /// </param>
        static void StartConsume(string name_queue, int lifeSpan_queue)
        {
            SetLifeSpan(DEQUEUE_INDEX, name_queue, lifeSpan_queue);

            IModel channel = GetChannel(DEQUEUE_INDEX);
            EventingBasicConsumer consumer = GetConsumer(DEQUEUE_INDEX);

            // 受信できたときに割り込んでくる処理
            consumer.Received += GetDequeueHandler();

#if UBUNTU
            // Ubuntuでは何故か引数が 5 個のやつになっている。
            channel.BasicConsume( QUEUE_NAMES[DEQUEUE_INDEX], true, "", null, consumer);
#else
            channel.BasicConsume( QUEUE_NAMES[DEQUEUE_INDEX], true, consumer);
#endif
            // 終了はさせない
            // consumer.Received -= GetReceiveHandler();
            // CloseChannel(DEQUEUE_INDEX);
        }
    }
}
```

でも、これ２匹で　プロセス間通信のテストができるようになった。

## サンプル・プログラムを別記した
記事 : http://qiita.com/muzudho1/items/dd85b10c139255a28f43

じゃあ、これを使って Apery をバックグラウンドで動かしてテストしてみる。

## Apery の方にも　コマンドライン引数に　キュー名と寿命を付けたいが
例えば

```
--RabbitMQ 1114 durable 1115 durable
```

みたいな。書き足せないだろうか。

```
--MsgQueue 1114 durable 1115 durable
```

の方が汎用的か？

改造中の浮かむ瀬の

```
	do {
		// 「./apery」のように引数無しで打鍵して実行された場合に限り、なんども標準入力を待つ。
		// 文字が打たれたらその文字を、空文字を打たれたら "quit" 扱いとする。
		if (argc == 1)
		{
			cmd = bgp_dequeue();
		}
```

この部分の argc == 1 に改造がいる。

恐らく　スペース区切りで分割すると「--MsgQueue」「1114」「durable」「1115」「durable」みたいに細切れにされてしまうだろう。

ところで浮かむ瀬は

```
	for (int i = 1; i < argc; ++i)
	{
		cmd += std::string(argv[i]) + " ";
	}
```

と書いているが、argv[0] を飛ばしているので、コマンドライン引数だけを cmd に入れなおしているのだろう。

そして　コマンドライン引数を　最初に入力されるコマンド　として解釈しているのだろう。

# Unicode の Code page 1200 だと UBUNTU で文字化け
やっぱ UTF-8のBOM有り Code page 65001 の方がいいのか。

# 浮かむ瀬のコマンドライン引数とコマンド
なんか

- コマンドライン引数を付けずに ./apery で実行したら対話式、
- コマンドライン引数を付けたら　コマンド１個で実行終了

という縛りがあるんで、これを崩す。

```
	bool dialogue = false;
	// 「./apery」のように引数無しで打鍵して実行された場合に限り、なんども標準入力を待つ。
	if (argc == 1)
	{
		dialogue = true;
	}
```

```
		if (token == "--msgqueue") {
#if UBUNTU
			ssCmd >> bgp_queueName_enqueue;
			ssCmd >> bgp_lifeSpan_enqueue;
			ssCmd >> bgp_queueName_dequeue;
			ssCmd >> bgp_lifeSpan_dequeue;
			dialogue = true;
#else
			std::cerr << "Command line parameter error : \"--msgqueue\" option for UBUNTU.";
#endif
		}
```

```
	} while (token != "quit" && dialogue);
```

なんかこんな感じで改造。

あっ、オプションをつけ忘れていた。

Makefile

```
# Windows10 と Ubuntu ではライブラリのAPIが異なったので、-DUBUNTU も追加。
CFLAGS   = -std=c++11 -fno-rtti -Wextra -Ofast -MMD -MP -fopenmp -lev -lamqpcpp -DUBUNTU
```

するとエラーが出た。

```
In file included from bitboard.hpp:25:0,
                 from main.cpp:23:
gameserver.hpp: In function ‘std::__cxx11::string bgp_dequeue()’:
gameserver.hpp:150:13: error: ‘RotationBuffer’ was not declared in this scope
   message = RotationBuffer.getMessage();
             ^
```

```
# ./apery --msgqueue 1114 durable 1115 durable &
[1] 28591
root@tk2-217-18401:/home/csg10/shogi/ukamuse_sdt4_child4/bin# jobs
[1]+  Running                 ./apery --msgqueue 1114 durable 1115 durable &
```

ひとまず稼働。
ログイン、ログアウトしてみる。

```
# ps aux | grep apery
root      2726  3.3  0.0 128660    48 ?        Sl   Mar11 100:11 /usr/bin/cli ./tamesi31_cs.exe ../ukamuse_sdt4/bin/apery
root     16835  0.0 14.9 1408108 151936 ?      Sl   Mar12   0:00 ./apery
root     28591  0.0 14.3 1408116 145876 ?      Sl   09:19   0:00 ./apery --msgqueue 1114 durable 1115 durable
root     28745  0.0  0.0  12936   984 pts/8    S+   09:26   0:00 grep --color=auto apery
```

とりあえず　うしろで割り込み待機状態になっているようだ。

```
root@tk2-217-18401:~# kill 2726
root@tk2-217-18401:~# kill 16835
root@tk2-217-18401:~# ps aux | grep apery
root     28591  0.0 14.3 1408116 145876 ?      Sl   09:19   0:00 ./apery --msgqueue 1114 durable 1115 durable
root     28771  0.0  0.0  12936   984 pts/8    S+   09:28   0:00 grep --color=auto apery
```

この浮かむ瀬と　プロセス間通信できれば　話しは早いんだが。

```
終了するときは[Ctrl]+[C]キーを押せだぜ☆（＾～＾）
エンキューするときはメッセージを打ち込んで[Enter]キーを押せだぜ☆(＾◇＾)
Enqueue? > usi
 Enqueue(^q^) usi
Enqueue? >
```

反応はない。これは　長そうだ。

```
# rabbitmqctl list_queues
Listing queues ...
1112    0
1111    0
1113    0
myqueue 25
1115    1
1114    0
```

1115 には多分「usi」が入っているんだろう。
バックグラウンドの浮かむ瀬は止まっているんだろうか？

#長くなったので次の記事へ