nginx の reqtime が 5 秒のところにピークがある。
応答遅延と思ったら…
5 秒のとこでピークあってどこかで詰まって返せてないのかなと思っていろいろ調べてたんだが、 User-Agent にもばらつきはないし、パスにもばらつきもないし原因が分からずずっと悩んでいた。 もう分からないので人に聞いたところ nginx には lingering_timeout という設定があるんだけどそれじゃね?と言われた。
見たとこクライアントからのデータ送信が途切れてから、lingering_timeout の分だけ待ったのち、 接続を遮断するような設定に読める。これのデフォルト設定が 5 秒。 内部的な話は調べていないのでわからないのだが、 クライアントが不意に切断された、みたいなときに nginx 的に reqtime が lingering_timeout + αみたいになるのだろうか。
とりあえず僕が悩んでいた件は指摘を受けてこれだったのでメモ書きにでも残す。
C++ の参照型で多態は効くのか。
経緯
ブログも久々だが C++ も久々で、書いているとどうにもポインタを触りたくないために、できるだけ参照型で物事を済ませたかった。のだが、そういえば参照型って多態効くんだっけと思って調べたがググらビリティが低かったので自分で調べた。
結果
効く。
test.cpp
#include <stdio.h> class Base { public: virtual ~Base() {} virtual void func() { printf("Base class function.\n"); } }; class ChildA : public Base { public: ~ChildA() {} void func() { printf("ChildA class function\n"); } }; class ChildB : public Base { public: ~ChildB() {} void func() { printf("ChildB class function\n"); } }; int main() { Base base; ChildA a; ChildB b; Base &baseRef(base); Base &aRef(a); Base &bRef(b); baseRef.func(); aRef.func(); bRef.func(); }
result
>g++ test.cpp >a.exe Base class function. ChildA class function ChildB class function
へー。まあでもそれもそうか。一安心。
Specs2 の Mockito でデフォルト引数つきのメソッドの動作定義がメソッドの呼び出し回数にカウントされる。
経緯
Scala でモックを使用したテストコードを書くときにに Mockito を使っている。Mockitoは大変便利で、 Specs2 で使う場合は以下のように簡単にメソッドの動作を定義できる。
val mockedHoge = mock[Hoge] val bar: Argument = new Argument() val result: Result = new Result() mockedHoge.foo(bar) returns result // Write a test with mockedHoge there was one(mockedHoge).foo(bar)
これはHoge
クラスをモックにして、Hoge#foo
が引数bar
で呼び出されたとき、result
を返すモックを作成している。また、テスト後にmockedHoge.foo(bar)
が一度だけ呼ばれていることを確認するテストである。とても簡単に書けて良い。
問題点
しかしデフォルト引数が入ると話がややこしくなる。以下のコードは適切な引数を受け取ると、そこからエンティティを作成し expire time つきのレポジトリに登録するサービスクラスである。
- EntityRegisterService.scala
package org.hal.stand import scala.concurrent.Future case class ExampleEntityId(value: Int) case class ExampleEntity(id: ExampleEntityId) class ExampleRepository { def set(entity: ExampleEntity, expiresInSecond: Int = 600): Future[Unit] = { Future.successful() } } class EntityRegisterService(repository: ExampleRepository) { def register(id: Int): Future[Unit] = { // 今回は引数で与えているが本来は何らかの処理で ID とエンティティを生成する。 val entityId = ExampleEntityId(id) val entity = ExampleEntity(entityId) repository.set(entity) } }
以上のサービスクラスのテストを以下のように書く。
- EntityRegisterServiceSpec.scala
package org.hal.stand import org.specs2.mock.Mockito import org.specs2.mutable.Specification import scala.concurrent._ import scala.concurrent.duration._ class EntityRegisterServiceSpec extends Specification with Mockito { val fakeExampleEntityId = ExampleEntityId(1234) val fakeEntity = ExampleEntity(fakeExampleEntityId) "register" should { "set the entity with the ID specified by the argument to the repository" in { val mockedRepository = mock[ExampleRepository] mockedRepository.set(fakeEntity) returns Future.successful() val service = new EntityRegisterService(mockedRepository) val result = service.register(fakeExampleEntityId.value) Await.result(result, Duration(100, MILLISECONDS)) mustEqual () there was one(mockedRepository).set(fakeEntity) } } }
一見これでうまく行きそうだがうまくいかない。以下の様なエラーが吐かれる。
[info] register should [info] x set the entity with the ID specified by the argument to the repository [error] The mock was not called as expected: [error] exampleRepository.set$default$2(); [error] Wanted 1 time: [error] -> at org.hal.stand.EntityRegisterServiceSpec$$anonfun$1$$anonfun$apply$3$$anonfun$apply$5.apply(MockitoTrapSpec.scala:20) [error] But was 2 times. Undesired invocation: [error] -> at org.hal.stand.EntityRegisterService.register(MockitoTrap.scala:19) (MockitoTrapSpec.scala:20)
どうもモックの動作定義時に一度メソッドが呼び出されたことになるらしく二回呼び出しされてエラーになってしまう。
解決方法
デフォルト引数やめよう。
- EntityRegisterService.scala
package org.hal.stand import scala.concurrent.Future case class ExampleEntityId(value: Int) case class ExampleEntity(id: ExampleEntityId) class ExampleRepository { def set(entity: ExampleEntity, expiresInSecond: Int): Future[Unit] = { Future.successful() } } class EntityRegisterService(repository: ExampleRepository) { final private val expireInSecond = 600 def register(id: Int): Future[Unit] = { // 今回は引数で与えているが本来は何らかの処理で ID とエンティティを生成する。 val entityId = ExampleEntityId(id) val entity = ExampleEntity(entityId) repository.set(entity, expireInSecond) } }
- EntityRegisterServiceSpec.scala
package org.hal.stand import org.specs2.mock.Mockito import org.specs2.mutable.Specification import scala.concurrent._ import scala.concurrent.duration._ class EntityRegisterServiceSpec extends Specification with Mockito { val fakeExampleEntityId = ExampleEntityId(1234) val fakeEntity = ExampleEntity(fakeExampleEntityId) "register" should { "set the entity with the ID specified by the argument to the repository" in { val mockedRepository = mock[ExampleRepository] mockedRepository.set(fakeEntity, 600) returns Future.successful() val service = new EntityRegisterService(mockedRepository) val result = service.register(fakeExampleEntityId.value) Await.result(result, Duration(100, MILLISECONDS)) mustEqual () there was one(mockedRepository, 600).set(fakeEntity) } } }
今回のはサービス層で与えるべき情報がレポジトリ層でデフォルト引数で与えられていて例が大変悪いけど、呼び出し回数がうまくカウントできずに詰んだので備忘録代わりに。
libsndfile + QIODevice で WAVE ファイルの読み書き
経緯
趣味で音声合成っぽいソフトウェアを作っている。いい加減音声ファイルの読み書き部を作らねばならなくなったのだが、C++ WAVE 読み込み 無料
と Google 先生に聞いてもマッチョな結果しか返ってこない。これはこれで興味はあるが、音声合成のドメインに信号列の処理は含まれても信号の入出力自体は含まれないのでもっと手軽に読み書きしたい。
なので OSS 、ただし GPL は除く、で良いライブラリがあるのが理想である。と言うかなんでみんなフォーマットとバイナリ読むことばっかに興味あるんだ。普通にいろいろ簡単に読み込めるライブラリをくれよ。
libsndfile インストール
いくつか探したのだが libsndfile が良さそう。ライセンスは LGPL で配布はバイナリとソースコード。以下からダウンロードできる。
- mega-nerd - libsndfile http://www.mega-nerd.com/libsndfile/
Windows のバイナリは VC++ 用なので MINGW 使いはソースコードのコンパイルをしないといけない。
MINGW + Windows 7 の組み合わせでは MSYS からインストールすれば良い。
適当に MSYS のコンソールから伝統的な./configure && make && make install
でコンパイルしてインストールできるので、 Windows でも簡単に使える。
libsndfile の使い方
普通に使う分には以下のブログ記事が役に立つ。
読んでいただければ分かるが以下のように読み込みを行う。
SndfileHandle sndfile("sample.wav", SFM_READ); sndfile.samplerate(); // サンプリング周波数 sndfile.channels(); // チャンネル数 short int* input = new short int [sndfile.frames()]; sndfile.readf(input, sndfile.frames()); // 16bit 符号付き整数の信号
ファイルから読み込むのであればこれだけで多くのフォーマットに対応できる。ライセンスも良い感じなのだが、しかしながら基本的なインタフェースはファイルしか無い。別にそれほど不自由はないが、できればQIODevice
のようなインタフェースから読める方が拡張性が高いし安心できる。
QIODevice からファイルを読み込む。
libsndfile 的にはQIODevice
からの読み込みは仮想ファイルからの読み込みという扱いになる。この機能は残念ながら C++ 版の libsndfile にはなく、 C 版のインタフェースを使うしか無い。
SF_INFO info{0, 0, 0, 0, 0, 0}; // ファイルの情報が入る構造体 SF_VIRTUAL_IO io = // 仮想 IO のコールバック関数の設定 { callbackLength, callbackSeek, callbackRead, callbackWrite, callbackTell }; QIODevice device; device.open(QIODevice::ReadOnly); // somehow open the device! SNDFILE *file = sf_open_virtual(&io, SFM_READ, &info, &data);
読み込む部分はこれだけで良い。ただしsf_open_virtual
を使うためにはSF_VIRTUAL_IO
に記述されたコールバック関数を実装せねばならない。すなわち以下の部分になる。
sf_count_t callbackLength(void *voidDevice) { QIODevice *device = (QIODevice *)voidDevice; return device->bytesAvailable(); } sf_count_t callbackSeek(sf_count_t offset, int whence, void *voidDevice) { QIODevice *device = (QIODevice *)voidDevice; switch(whence) { case SEEK_CUR: device->seek(device->pos() + offset); break; case SEEK_SET: device->seek(offset); break; case SEEK_END: device->seek(offset + callbackLength(voidDevice)); break; } return device->pos(); } sf_count_t callbackRead(void *ptr, sf_count_t count, void *voidDevice) { QIODevice *device = (QIODevice *)voidDevice; return device->read((char *)ptr, count); } sf_count_t callbackWrite(const void *ptr, sf_count_t count, void *voidDevice) { QIODevice *device = (QIODevice *)voidDevice; return device->write((const char *)ptr, count); } sf_count_t callbackTell(void *voidDevice) { QIODevice *device = (QIODevice *)voidDevice; return device->pos(); }
ただしこのやり方はストリーミングは一切考慮していない。とはいえQIODevice
から読み込むことができるのでそれなりに恩恵がありそう。あとは適当にインタフェースを整えてやればよい。僕の実装は以下にあるのでご参考までに。
Qt の JSON 機能を少し使ってみた。
今趣味で書いているプログラムの保存形式をどうしようか悩んでいたのだが、 XML は前使ってみたとき煩雑だったしもう少し簡単な形式はないものかとネットを見ていたら、どうも Qt5.0 以降から JSON をサポートしているらしい。
- QtCore 5.0: JSON Support in Qt
ということでこれを使うのが良いだろうということで使ってみた。
#include <QJsonObject> #include <QJsonValue> class Phoneme { public: QString filename; double msOffset; double msPreutterance; QJsonValue toJson() const { QJsonObject object; object["filename"] = filename; object["ms_offset"] = msOffset; object["ms_preutterance"] = msPreutterance; return QJsonValue(object); } };
これは便利すぎる。でもこれだと JSON の形式を Phoneme
オブジェクトが知らねばならないし、
そもそも Phoneme
がこの形式の JSON にのみ対応するとは限らないので以下の様なクラスを用意する。
class JsonSerializer { public: template<class T> static QJsonValue toJson(const T &t) { QJsonValue value; value << t; return value; } };
あとは <<
演算子をグローバルでオーバーロードしさえすればよいので、クラス内で toJson
を用意する必要はない。
QJonValue oprator <<(QJsonValue &left, const Phoneme &right) { QJsonObject object; object["filename"] = right.filename; object["ms_offset"] = right.msOffset; object["ms_preutterance"] = right.msPreutterance; return (left = QJsonValue(object)); } int main() { QJsonValue json(JsonSerializer::toJson(phoneme)); return 0; }
これで良い。あとは QJsonDocument
を使って Qt の JSON 系クラスと文字列を行ったり来たりできそう。
Google Mock で演算子オーバーロードしたクラスのモックを作る。
経緯
以前書いたGoogle Mock を導入してみた。に見るように 普通の仮想メンバ関数のモッキングは大変簡単なのだが、演算子のオーバーロードに対してはモッキングができないのかやり方を調べてみた。
問題
単純に仮想関数をモッキングするだけであれば、MOCK_METHOD*
を書けば良いだけなのだが以下の様なコードは許容されなかった。
どうやら単純にはできないらしい。
class MyList { public: virtual ~MyList(){ } virtual MyElement &operator[](int index) = 0; }; class MockedMyList : public MyList { public: MOCK_METHOD1(operator[], MyElement &(int index)); // こう書くことはできない。 };
解決法
operator[]
はオーバーライドし、モッキングしたメンバ関数に処理を委譲すれば良い。
すなわち以下のように定義する。
class MockedMyList : public MyList { public: MOCK_METHOD1(subscript, MyElement &(int index)); MyElement &operator[](int index) { return subscript(index); } };
あとはsubscript
の動作を定義すれば良い。
参考資料
http://stackoverflow.com/questions/6492664/how-to-create-a-mock-class-with-operator
Google Mock で参照を返す。
経緯
つい先程テストを書いていて、参照を返すタイプのメソッドをモッキングする際に失敗をしたのでメモしておく。
問題
こんな感じのクラスをモック化しようとする。
class MyClass { public: virtual ~MyClass(){ } virtual const Property &property() const; };
当然以下のようにモックを作る。
class MockMyClass : public MyClass { public: MOCK_CONST_METHOD0(proeprty, const Property &()); }
そして以下のようにモックを定義する。
MockMyClass mock; Property retval; EXPECT_CALL(mock, property()) .Times(1) .WillOnce(Return(retval));
一見うまく行きそうなのだが以下のようなエラーに遭遇する。
gmock/include/gmock/gmock-actions.h:498:5: error: size of array is negative GTEST_COMPILE_ASSERT_(!internal::is_reference<Result>::value, ^
C++ のコンパイルエラーはなかなか趣深いが何を言っているか分からなくて困る。Google 先生に聞いてみたら以下の解決策を見つけた。
解決策
参照を返すには ReturnRef
を使えばよいらしい。以下のようにして解決。
MockMyClass mock; Property reval; EXPECT_CALL(mock, property()) .Times(1) .WillOnce(ReturnRef(retval));
参考資料
https://groups.google.com/forum/#!topic/googlemock/UM6dgKz3rQQ