ふるお〜と!- FullAuto

AI・ロボットが普及しBI(ベーシックインカム)が早急に実現されることを願う元ニートのブログ

ふるお〜と!-FullAuto

C++イベント実装 Calllbacks, Promises, Signals and Events

イベント処理の実装に苦しめられている今日この頃。
ある意味、基本中の基本なのだが、
基本情報技術者試験を合格しても実装することはできないし、
応用情報技術者試験を合格しても実装することはできない。
実装するには提供された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

結局のところ、イベントとは関数のポインタを登録し、イベントに応じて呼び出すことができるようにしたものです。

世の中すべてのイベントの型を登録できるような関数を実装することは不可能です。
そこでデリゲートという仕組みが登場します。
このページで詳しく解説されています。

Delegate

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