the_app
アプリ記述setup(),loop()の切り替え
アプリケーションはsetup(),loop()の2つの関数で記述されますが、例えば1画面ごとに大きく振る舞いが違うもの(ここではサブアプリと呼びます)を複数定義して、メニューで切り替えるといった動作を行う場合は、そのままsetup(),loop()内に記述しては煩雑になります。また変数についても管理する必要があります。
setup(), loop(), 関連する変数の切り替えを簡素化するためMWM5ライブラリでは以下の2つの仕組みを実装しています。
サブアプリ :
setup(),loop(),関連変数をクラスとしてまとめ、切り替えるアプリハンドラ: サブアプリ内で
setup(),loop()を切り替える
サブアプリ
#include <mwm5.h>
// サブアプリ1
class App_Foo : public class APP_DEF {
public:
void setup() { ... }
void loop() { ... }
}
// サブアプリ2
class App_Bar : public class APP_DEF {
public:
void setup() { ... }
void loop() { ... }
}
// サブアプリ切り替え関数
static int s_change_app(
TWE::APP_MGR& the_app, int n_appsel
, int prev_app, int exit_id) {
if (n_appsel == 1) the_app.new_app<App_Foo>();
else if (n_appsel == 2) the_app.new_app<App_Bar>();
else n_appsel = 0;
return n_appsel;
}
// setup
void setup() {
the_app.setup(s_change_app); // サブアプリ切り替え関数の登録
the_app.new_app<App_Foo>(); // 最初に起動するサブアプリ
}
// loop()
void loop() {
the_app.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()で指定した値です。
サブアプリ切り替え関数内部ではthe_app.new_app<T>()(Tは次に生成するサブアプリのクラス)を呼び出しています。この呼び出しによりクラスTのオブジェクトが生成され、同時にこれまで動作していたサブアプリのオブジェクトは破棄されます。
the_app.new_app<T>()
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以上が次のサブアプリの識別子と意味付けをしています。
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に対応した処理を記述します。
EV_SETUP
setup()に相当し、アプリハンドラが登録・切替時に呼び出される。
EV_LOOP
loop()に相当し、都度呼び出される。
EV_EXIT
アプリハンドラの切り替えられ、現在のハンドラが終了する際に呼び出される。
パラメータ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オブジェクトを操作します。
最終更新