1

私は、さまざまなイベントハンドラーを使用するイベントマネージャーを作成しています。このイベントマネージャーには、さまざまなイベントが通知されます。各ハンドラーは特定のイベントのみを処理し、残りは無視します。各ハンドラーは、状況に基づいて他の特定のイベントをトリガーすることもできます。

たとえば、最初に処理するハンドラーEvent1

-module (first_handler).
-behavior (gen_event).

...

handle_event(Event1, State) -> {ok, State};
handle_event(_, State) -> {ok, State}.

処理する2番目のハンドラーEvent2

-module (second_handler).
-behavior (gen_event).

...

handle_event(Event2, State) -> 
  gen_event:notify(self(), Event1),
  {ok, State};
handle_event(_, State) -> {ok, State}.

イベントのトリガーはハンドラーのgen_event:notify(self(), NewEvent)内で呼び出すことで実行handle_eventできますが、イベントマネージャーから呼び出すことができるように、抽象化してエクスポートしたいと思います。

パターンマッチング、イベントの無視、イベントのトリガーはすべてのハンドラーに共通しているので、とにかくgen_event動作を拡張してそれらを組み込みとして提供することはできますか?

カスタム動作を作成するデフォルトの方法から始めます。

-module (gen_new_event).
-behaviour (gen_event).

behaviour_info(Type) -> gen_event:behaviour_info(Type).

次に何をすべきかわかりません。

4

2 に答える 2

3

あなたは正確に何をしようとしていますか?あなたが提供した例からは理解できませんでした。second_handlerhandle_event/2では、Event1バインドされていません。また、使用はself()機能しますか?それは管理者の登録名ではないはずです。マネージャまたは各ハンドラ プロセスによって実行されるかどうかhandle_event/2は不明です (ただし、後者の方が理にかなっています)。

モジュールを実装することによりgen_new_event、イベント マネージャーではなく、ハンドラー (つまり、コールバック モジュール) を実装することになります。を持っているということは、 によってリストされたすべての関数を実際に実装している-behaviour(gen_event)ことを確認するようにコンパイラに依頼していることを意味します。gen_new_eventgen_event:behaviour_info(callbacks)gen_new_eventgen_event:add_handler(manager_registered_name, gen_new_event, [])

を削除すると、次の関数を実装する必要-behaviour (gen_event)gen_new_eventなくなります。

35> gen_event:behaviour_info(callbacks).   
[{init,1},
 {handle_event,2},
 {handle_call,2},
 {handle_info,2},
 {terminate,2},
 {code_change,3}]

実装gen_new_eventに使用するモジュールを必要とする関数を追加することで、動作 (つまり、インターフェイス) を作成できます。-behaviour(gen_new_event)

-module (gen_new_event).
-export([behaviour_info/1]).

behaviour_info(callbacks) -> 
    [{some_fun, 2}, {some_other_fun, 3} | gen_event:behaviour_info(callbacks)].

ここで、たとえば-module(example)、一部のモジュールで属性を追加する-behaviour(gen_new_event)場合、モジュールexampleはすべてのgen_eventコールバック関数 +some_fun/2およびを実装する必要がありますsome_other_fun/3

それがあなたが探していたものだとは思えませんが、最後の例は、動作を実装したいことを示唆しているように見えました. 動作を実装することによって行っていることは、他のモジュールが使用する必要がある特定の関数を実装することを要求することだけであることに注意してください-behaviour(your_behaviour)

(また、私があなたを正しく理解していれば、拡張したい場合は、gen_eventいつでもコードをコピーして拡張することができますgen_event.erl...私は推測しますが、これはあなたがやろうとしていることにとって本当に必要ですか?)。

編集

目的: gen_event 実装から共通コードを抽出します。たとえば、gen_events ごとに必要な handle_event/2 節があります。

1 つの方法: パラメータ化されたモジュールを使用できます。このモジュールは gen_event 動作を実装しますが、すべての gen_event コールバック モジュールが持つべき共通の動作のみです。「共通」でないものはすべて、モジュールのパラメーターに委譲できます (gen_event コールバックの「カスタム」実装を含むモジュール名にバインドします)。

例えば

-module(abstract_gen_event, [SpecificGenEvent]).
-behaviour(gen_event).
-export(... all gen_event functions).

....

handle_event({info, Info}, State) ->
    %% Do something which you want all your gen_events to do.
handle_event(Event, State) ->
    %% Ok, now let the particular gen_event take over:
    SpecificGenEvent:handle_event(Event, State).

%% Same sort of thing for other callback functions
....

次に、abstract_gen_event にプラグインする 1 つ以上の gen_event モジュールを実装します。それらの 1 つが a_gen_event であるとしましょう。

次に、次のことができるはずです。

AGenEvent = abstract_gen_event:new(a_gen_event). %% Note: the function new/x is auto-generated and will have arity according to how many parameters a parameterized module has.

次に、AGenEvent を gen_event:add_handler(some_ref, AGenEvent, []) に渡すことができると思いますが、うまくいくはずですが、これを試したことがないことに注意してください。

おそらく、マクロを使用してこれを回避することもできますし、(これは少しやり過ぎです) parse_transform/2 を使用してコンパイル時にいくつかのことをいじることもできます。ただの考えです。このパラメーター化されたソリューションが最初にどのように機能するかを確認してください。

2回目の編集

(注: このセクションに記載されている内容の前にすべてを削除する必要があるかどうかはわかりません。お知らせください。または、何をしているのかわかっている場合は削除してください)。

わかりましたので、自分で試してみました。はい、パラメーター化されたモジュールの戻り値は、gen_event:add_handler/3 の 2 番目の引数に渡すとクラッシュします... あまりにも悪い :(

これについては、a) マクロを使用する、b) parse_transform/2 を使用する以外の方法は考えられません。

a)

-module(ge).
-behaviour(gen_event).

-define(handle_event, 
handle_event({info, Info}, State) ->
    io:format("Info: ~p~n", [Info]),
    {ok, State}).

?handle_event;
handle_event(Event, State) ->
    io:format("got event: ~p~n", [Event]),
    {ok, State}.

したがって、基本的には、この共通機能を使用するすべての gen_event に含めるヘッダー ファイルのマクロ定義で定義された共通機能のすべてのコールバック関数句を使用します。次に、共通の機能を使用する各コールバック関数の前/後に ?X を実行します...それほどきれいではないことはわかっています。また、マクロを自分で使用することに一般的にうんざりしていますが、問題が本当にあなたを悩ませている場合は、それが 1 つの方法です。それについて行きます。

b) Erlang での parse_transform/2 の使用に関する情報については、Google で検索してください。コールバックの特定のケースはあるが一般的なケース (上記のマクロの ({info, Info}, State) のような節) を持たないgen_event モジュールでコールバック関数を探す parse_transform を実装できます。次に、一般的なケースを構成するフォームを追加するだけです。

次のようなことをお勧めします(エクスポートを追加します):

-module(tmp).
 parse_transform(Forms, Options) ->
     io:format("~p~n", [Forms]),
     Forms.

-module(generic).
gen(Event, State) ->
    io:format("Event is: ~p~n", [Event]),
    {ok, State}.

これで、次のようにコンパイルできます。

c(tmp).
c(generic, {parse_transform, tmp}).
[{attribute,1,file,{"../src/generic.erl",1}},
 {attribute,4,module,generic},
 {attribute,14,compile,export_all},
 {function,19,gen,2,
       [{clause,19,
                [{var,19,'Event'},{var,19,'State'}],
                [],
                [{call,20,
                       {remote,20,{atom,20,io},{atom,20,format}},
                       [{string,20,"Event is: ~p~n"},
                        {cons,20,{var,20,'Event'},{nil,20}}]},
                 {tuple,21,[{atom,21,ok},{var,21,'State'}]}]}]},
 {eof,28}]
 {ok,generic}

そうすれば、注入するフォームをコピーして貼り付けることができます。それらを適切な parse_transform/2 にコピーすると、印刷するだけでなく、実際にソースのコードを調べて、必要な場所に必要なコードを挿入します。

-compile({parse_transform, tmp})補足として、一般的な機能を追加するために、この方法で parse_transformed する必要があるすべての gen_event モジュールに属性を含めることができます(つまり、これを自分でコンパイラに渡す必要がなくなります)。tmpparse_transform を含むモジュールがパス上のディレクトリにロードまたはコンパイルされていることを確認してください。

b) 私が知っている多くの仕事のように思えます...

于 2012-06-29T22:23:03.723 に答える
2

インストールされたハンドラーは、開始してからハンドラーをインストールするイベントマネージャーのコンテキストで既に実行されています。したがって、ハンドルイベント関数がデータをスローする場合、それらはすでにあなたが望むことをしています.

イベント動作を拡張する必要はありません。あなたがすることは次のとおりです。

 handle_event(Event, State) ->
   generic:handle_event(Event, State).

次に、genericモジュールに汎用パーツを処理させます。generic必要に応じて、特殊なハンドラーの動作のためにこのハンドラー モジュールにコールバックする方法を提供できることに注意してください。例えば:

   generic:handle_event(fun ?MODULE:callback/2, Event, State)...

等々。

于 2012-06-29T12:12:42.593 に答える