the_app
アプリ記述setup(),loop()の切り替え
アプリケーションはsetup()
,loop()
の2つの関数で記述されますが、例えば1画面ごとに大きく振る舞いが違うもの(ここではサブアプリと呼びます)を複数定義して、メニューで切り替えるといった動作を行う場合は、そのままsetup()
,loop()
内に記述しては煩雑になります。また変数についても管理する必要があります。
setup(), loop(), 関連する変数の切り替えを簡素化するためMWM5ライブラリでは以下の2つの仕組みを実装しています。
サブアプリ :
setup()
,loop()
,関連変数
をクラスとしてまとめ、切り替えるアプリハンドラ: サブアプリ内で
setup()
,loop()
を切り替える
サブアプリ
上記例は説明に必要な最小限のコードのみを記述します。APP_DEF
を継承したApp_Foo
,App_Bar
が、2種類のサブアプリの定義となり、各々がsetup()
,loop()
メンバー関数を定義しています。実際には振る舞いごとに必要なメンバー変数やその他メンバー関数が定義されます。サブアプリの登録や切り替えはライブラリ定義のクラスオブジェクトthe_app
に対して操作を行います。s_change_app()
関数はサブアプリを切り替える要求(サブアプリ終了時など)に対して、実際の切り替え部を実装していて、この関数をsetup()
中で呼び出すthe_app.setup()
のパラメータとして渡しておきます。loop()
中にはthe_app.loop()
を記述しておきます。
サブアプリの定義
void setup()
とvoid loop()
を記述しておきます。setup()はサブアプリが初期化されるときに1度だけ呼び出されます。loop()
はthe_app.loop()
関数中で呼び出されます。
サブアプリの切り替え
実行中のサブアプリが終了する場合に呼び出されます。サブアプリは1以上のIDにより種別を管理し、切り替えのために関数int (*)(THE_APP_MGR&, int, int, int)
を実装しておきます。
パラメータn_appsel
には、切り替えたいアプリケーションのIDが渡されます。パラメータはthe_app.exit()
で指定した値です。-1
が渡された場合は、デフォルトのサブアプリに切り替えるよう記述しておきます。
パラメータ
prev_app
には、直前のサブアプリIDです。直前が無ければ0となります。パラメータ
exit_id
は、サブアプリがthe_app.exit()
で指定した値です。
n_appsel
で次に動作させるサブアプリを指定し、exit_id
はサブアプリに対するパラメータとして用います。
サブアプリ切り替え関数内部ではthe_app.new_app<T>()
(T
は次に生成するサブアプリのクラス)を呼び出しています。この呼び出しによりクラスT
のオブジェクトが生成され、同時にこれまで動作していたサブアプリのオブジェクトは破棄されます。
the_app.new_app<T>()
setup()
とサブアプリ切り替え関数内で呼び出し、サブアプリのオブジェクトを生成し、同時に現在のサブアプリを破棄します。
最初に動作させるサブアプリはsetup()
で生成します。
テンプレートパラメータ
T
はサブアプリ定義クラスでAPP_DEF
を継承している必要があります。テンプレートパラメータ
args
は、コンストラクタに渡されるパラメータです。オブジェクトはnew T(std::forward(args)...)
で生成されます。サブアプリがパラメータを指定しないデフォルトコンストラクタで構築される場合は何も指定しません。パラメータを指定する場合はサブアプリのコンストラクタの定義が必要です(例えばApp_Foo::App_Bar(int)
を定義しておいてthe_app.new_app<App_Bar>(exit_id)
)。
the_app.exit()
サブアプリのloop()
内で呼び出し、loop()
を抜けた直後にサブアプリを切り替えます。
上記の例のように大本のloop()
からはthe_app.loop()
が呼び出され、さらにサブアプリのloop()
が呼び出されます。サブアプリのloop()
中でthe_app.exit()
が呼び出されると、the_app.loop()
内でアプリケーションの切り替えが行われます。実際に切り替えているのはユーザが定義するサブアプリ切り替え関数です。
パラメータ
exit_code
は、サブアプリの終了コードを指定します。任意の数を指定できます。パラメータ
next_app
は、次のサブアプリを明示的に指定する意味合いの識別子です。-1
はデフォルトのサブアプリ、0
は切り替えを行わない、1
以上が次のサブアプリの識別子と意味付けをしています。
exit_code
とnext_app
は、そのままサブアプリ切り替え関数に渡されます。サブアプリ切り替え関数は、その2つのパラメータを参照して次のサブアプリを決定します。
the_app.query_appobj()
大本のloop()
からthe_app
で生成したサブアプリのクラスオブジェクトを参照し、さらにサブアプリが指定したvoid*
型のポインタデータを取得します。
例えば、サブアプリ構築時に共通のクラスオブジェクトを持っていて(例えばメイン画面となるターミナル画面)、大本のloop()
から共通のオブジェクトにアクセスします(例えばメイン画面上の文字列を取得)。
アプリハンドラ
アプリハンドラAPP_HNDLR
は、サブアプリ中での簡易的なsetup()
,loop()
処理の切り替えを行います。例えば、リスト一覧→リスト選択後、エラーといった複数の画面を切り替えるような動作です。
サブアプリの実装ではAPPDEF
を継承しましたが、さらにAPP_HNDLR<S>
(Sはサブアプリクラスを指定します)を継承します。サブアプリ中に処理関数(アプリハンドラ)を実装し、アプリハンドラを切り替えます。
上記がアプリハンドラを用いたときのソース構造の例です。
hndr_scr_def()
とhndr_scr_next()
がアプリハンドラ関数です。パラメータとしてev
とarg
が与えられ、ev
はEV_SETUP
,EV_LOOP
,EV_EXIT
のいずれかになり、setup()
,loop()
と終了処理に対応します。
アプリハンドラ関数
アプリハンドラ関数はsetup()
,loop()
に相当する処理を行います。加えてハンドラが切り替えられる、つまり現在登録されているハンドラが終了するときの処理を記述できます。
パラメータev
に対応した処理を記述します。
| setup()に相当し、アプリハンドラが登録・切替時に呼び出される。 |
| loop()に相当し、都度呼び出される。 |
| アプリハンドラの切り替えられ、現在のハンドラが終了する際に呼び出される。 |
パラメータarg
は、APP_HNDLR::new_hnbdlr()
やAPP_HNDLR::loop()
でパラメータが与えられたときに、値が格納されます。
APP_HNDLR::new_hndlr()
アプリハンドラを初回生成する(サブアプリクラスのsetup()
)、またはアプリハンドラ内から次のアプリハンドラへ切り替えます。
パラメータ
hnd_next
はアプリハンドラ関数(メンバー関数のポインタ)を指定します。パラメータ
arg
はアプリハンドラ関数のEV_SETUP
呼び出し時のパラメータを指定します。(EV_SETUP
による初期化時の処理を分岐したいときに利用します)
APP_HNDLR::loop()
サブアプリのloop()
から毎回呼び出します。
パラメータ
arg
を指定するとアプリハンドラにその値が渡されます。
APP_HNDLR::on_close()
サブアプリのデストラクタから呼び出すようにしてください。
この記述を省略した場合、サブアプリのオブジェクトが破棄されるときに、アプリハンドラも破棄されますが EX_EXIT
メッセージが呼び出されません。
アプリハンドラごとの独自データ
アプリハンドラは、サブアプリ内でsetup(),loop()コンテキスト切り替えの仕組みで、サブアプリ下でのメンバー関数の実行という携帯です。そのため、アクセスできるデータはサブアプリのメンバー変数となります。
ここでは、アプリハンドラ独自のデータを保持するクラスを追加します。
データを格納するクラスの準備
APPHNDLR_DCを継承したクラスを準備します。このクラスにはアプリハンドラ内で使用するデータやメンバー関数を定義します。以下の例ではDC_MyAppHndlr
というクラスを定義しています。
以下の定義を行う必要があります。
static const int CLS_ID
: データ格納クラス種別を識別するための個別のIDです。int get_class_id()
:CLS_ID
を戻すメンバ関数です。コンストラクタ : サブアプリクラスオブジェクトをパラメータとします。このオブジェクトは上記の例ではappに保存し
DC_MyAppHndlr
からサブアプリにアクセスする目的で利用します。継承したAPPHNDLR_DCを
APPHNDLR_DC(CLS_ID)
として初期化します。
サブアプリでのfriend宣言
DC_MyAppHndlr
はサブアプリのメンバー変数や関数にアクセスすることも多いためfriend
宣言しておきます。もちろん一般のクラス設計のようにpublicインタフェースを用意してカプセル化してもよいのですが、この場合、カプセル化の利点があまりなく記述が煩雑になるためです。
アプリハンドラ内での利用
アプリハンドラではAPP_HNDLR::use<>()
を用いてデータオブジェクトを取得します。オブジェクトの生成・破棄は暗黙に行われます。別のアプリハンドラに切り替えられたときにデータオブジェクトが破棄されます。
上記の例では、アプリハンドラ関数の最初でdc
を取得しています。初回呼び出しにオブジェクトが生成され、それ以降は生成されたオブジェクトを参照します。
あとは生成されたdc
オブジェクトを操作します。
最終更新