イベント処理の実装に苦しめられている今日この頃。
ある意味、基本中の基本なのだが、
基本情報技術者試験を合格しても実装することはできないし、
応用情報技術者試験を合格しても実装することはできない。
実装するには提供されたAPIのサンプルを動かしてみて、へーこう動くのかぁ。というような感じで理解して実装するのが定石だろうか...。
現在作られているアプリケーションのほとんどがGUIからのイベントドリブンなのに、
イベント処理は上級レベルの知見となっている。
学校で教えてくれない。
AIロボットもハードウェアからのイベントドリブンだ。
なんらかの情報をインプットされたことを、
イベントととして処理をしてアウトプットする。
ハードウェアからの割り込み待ちまたはポーリング(状態の変化を観察しまくる)をする。
共通して言えることはメモリのどこかにあるhogeという処理を別の処理fooにそのポインタを教えてやって実行する。
というところでしょうか....。
Callbacks
引数を関数ポインタとして与えて、処理を任せます。 ここではhogeにhelloworldという関数(ポインタ)を与えます。 main
#include <cstdio> void main() { hoge( helloworld ); }
コールバック関数となるhelloworld関数の実装
void helloworld() { printf("Hello World!!"); }
コールバック関数を引数として実行するhogeの実装
void hoge( void (*callback)() ) { callback(); }
関数の型はがvoidであり、関数なので()を付けます。
Promises
コールバックによる手法には、ネストが深くなりすぎるという欠点があります。
そこで非同期処理でコールバック関数を実装する場合はpromiseで簡便にできます。
#include <iostream> #include <future> #include <thread> #include <utility> void hoge(std::promise<int> p) { p.set_value(1234); // 結果値を書き込む } int main() { std::promise<int> p; std::future<int> f = p.get_future(); // 別スレッドで計算を行う std::thread t(hoge, std::move(p)); // calc()によって書き込まれた結果を取得 std::cout << f.get() << std::endl; t.join(); }
ビルド
$ g++ -std=c++11 -pthread helloworld.cpp
Signals
シグナルに応じて、登録した関数ポインタを実行するという実装の方法もあります。
#include <csignal>や#include <signal.h>をインクルードします。
下記の例は
「SIGINTというシグナルが発生するsignal_handlerが呼び出される」
という登録をして、
後にSIGINTを発生させます。
void signal_handler(int signal) { gSignalStatus = signal; } int main() { // Install a signal handler std::signal(SIGINT, signal_handler); std::cout << "SignalValue: " << gSignalStatus << '\n'; std::cout << "Sending signal " << SIGINT << '\n'; std::raise(SIGINT); std::cout << "SignalValue: " << gSignalStatus << '\n'; }
Signal & Slot
Qt,Boost,GDKなどのライブラリを使うことで簡単に実装できます。
connect関数を用いてシグナルとスロットを結びつけることができます。
Events
結局のところ、イベントとは関数のポインタを登録し、イベントに応じて呼び出すことができるようにしたものです。
世の中すべてのイベントの型を登録できるような関数を実装することは不可能です。
そこでデリゲートという仕組みが登場します。
このページで詳しく解説されています。
Event Class
クラスとして定義をすることも可能です。
そのイベントクラスはシグナルの状態とイベントメソオッドが定義されています。
参考
javascriptの場合
Callbacks, Promises, Signals and Events | Blog | Miller Medeiros
callback関数
C++ の小さな技術を紹介するシリーズ【小技C++ 全9回】#3<コールバック> - Qiita
Promises
promise - cpprefjp C++日本語リファレンス
Signal
std::signal - cppreference.com
Delegate
Delegate
非同期処理を理解する - Sansan Builders Blog
C++11における同期処理(std::mutex, std::unique_guard, std::lock_guard, std::condition_variable) - Qiita