#C++でブロックプログラミング
仕事でMatlabを使ったりしているとSimulinkを使う機会も出てきます。Simulinkが非常に便利なのは、なんといってもブロックを置いて配線を繋げば、それで処理が動いてしまうことです。(実際はそう簡単には行かないが。。。) 世の中、モデリングの仕方はいろいろあります。クラス構成を作ってみたり、処理のフローを考えてみたり。でも、実際、お客さんや上司、はたまた素人の人に説明するときに、「クラスを継承して・・・」と言ったところで「???」となってしまうことが正直多い。構造をわかりやすくするためのモデリングをしても、構造の意味が伝わらないということは、モデリングとして結構致命的なんだろうと感じます。has-aやis-aの関係と言っても、分からない人には、本当にさっぱり分かりません。
ところが、唯一一つ直感的にすぐに分かってもらえる図というか構造があります。俗に言う「ブロック図」です。半分嘘を承知で言うなら「コラボレーション図」とかが、これに近いと言えるでしょう。この構造は本当に分かりやすい。実際、まずブロック図で説明してね!と言われるくらい直感的でもあります。特に、ハード屋さんにとっては、非常に親和性もあり、先に出てきたSimulinkもまさにこの王道なわけです。
しかし、この構造をC++とかで実現しようとすると結構厄介です。インターフェイスを定義して、ブロック間の接続の管理やら、その他、バッファはどうする?とか、パフィーマンスはどうする?、部分的にパラレル的に動作させるか?とか、タイミング制御はどうする?とか・・・。構造は分かりやすい割に、実際大変ですし、ここに到達するまでが非常に長い。。。
そこで、RAFTLIBです。
ブロック構造を簡単に定義し、ブロックを自由自在に接続し、必要があればパラレル化も出来る素晴らしいライブラリです。ブロックの定義もシンプルかつ汎用的であり、自分の作る処理のフィルタは、全て、このライブラリに沿って作っておきたいくらいです。
サンプルコードを以下に引用してみました(引用元:https://github.com/RaftLib/RaftLib/wiki/Getting-Started)
#include <cassert>
#include <iostream>
#include <cstdint>
#include <cstdlib>
#include <raft>
#include <raftio>
#include <raftrandom>
#include <raftmath>
#include <raftutility>
template< typename T > class Sum : public raft::kernel
{
public:
Sum() : raft::kernel()
{
/** declare ports **/
input.template addPort< T >( "input_a", "input_b" );
output.template addPort< T >( "sum" );
}
virtual raft::kstatus run()
{
T a,b;
input[ "input_a" ].pop( a );
input[ "input_b" ].pop( b );
/** smart obj allocate directly on output port **/
auto out( output[ "sum" ].template allocate_s< T >() );
/** like an iterator, dereference the out to write to it **/
(*out) = a + b;
/** out will automatically release to the next kernel on scope exit **/
return( raft::proceed );
}
};
int
main()
{
const auto count( 1000 );
/** define type for streams **/
using type_t = std::int64_t;
/**
* random_variate is a threaded wrapper around the C++ random number gen **/
using gen = random_variate< std::default_random_engine,
std::uniform_int_distribution,
type_t >;
/** this will be our worker 'sum' kernel **/
using sum = Sum< type_t >;
/** make shorter name for print kernel **/
using p_out = raft::print< type_t, '\n' >;
gen a( count ), b( count );
sum s;
print p;
raft::map m;
/** link the only output port of a to the "input_a" port of s **/
m += a >> s[ "input_a" ];
/** link the only output port of b to the "input_b" port of s **/
m += b >> s[ "input_b" ];
/** take the only output port of s and link it to the only input port of p **/
m += s >> p;
/**
* NOTE: this will be going away soon,
* to be called on scope exit, an explicit
* barrier call will enable integration with
* sequential code.
*/
m.exe();
return( EXIT_SUCCESS );
}
非常にシンプルです。
前段のSumクラスが定義されています。このSumクラスの中で、ブロックの入出力に関してのピンの名前と型が宣言されています。わずか2行でインターフェイスが定義されてしまうシンプルさです。素晴らしい!!
フィルタの実行部は、runメソッドが受け持ちます。これも非常に分かりやすい構造になっています。ここに独自のフィルタ処理を置けば、ブロックとして成立します。
実際のブロック(画像処理で言うならフィルターグラフ?)をmain関数内で定義していますが、これも構造が非常にわかりやすくシンプルに記述出来ます。