[erlang-questions] Protocol Mapping
Jachym Holecek
freza@REDACTED
Wed Dec 19 13:54:10 CET 2012
# Steve Davis 2012-12-19:
> I'm frequently facing situations with binary protocol translations/transforms where I need to map binary codes to e.g. atoms and back.
>
> This is of course "easy" in Erlang, but suffers some inconveniences. I've attached a code snippet distilled down to the simplest case to explain my issue.
>
> I'm sure the first codec pattern below is familiar, and for sure is efficient.
> However, there are multiple places where updating is required to add new message types.
>
> The second pattern is much less usual, but handy as one line achieves the addition of a new message type.
> It helps particularly when there is more than one pairing involved (e.g. {atom, code, status_message}).
>
> For sure this cannot be something nobody has seen/thought about. I'm wondering if anyone has comment on this, and maybe suggestions for approaches that I haven't thought of.
>
> /s
> -------
> -module(mapping).
>
> -compile(export_all).
>
> %% traditional
>
> -define(HELLO, 100).
> -define(HELP, 101).
> -define(DONE, 102).
>
> encode(hello) -> ?HELLO;
> encode(help) -> ?HELP;
> encode(done) -> ?DONE.
>
> decode(?HELLO) -> hello;
> decode(?HELP) -> help;
> decode(?DONE) -> done.
>
> %% alternative
>
> -define(MAP, [
> {hello, 100},
> {help, 101},
> {done, 102}
> ]).
>
> encode0(X) ->
> {X, Y} = lists:keyfind(X, 1, ?MAP),
> Y.
>
> decode0(X) ->
> {Y, X} = lists:keyfind(X, 2, ?MAP),
> Y.
Option 3:
You already have macros in option one, can just use them everywhere. Ugly
but the fastest you can get (no translation = no cost). Decoded messages
will be very non-user-friendly though.
Option 4:
-record(foo_map, {key, val}).
%% Installation procedure, only run once.
install() ->
%% XXX create the table with appropriate options if nonexistent.
[install_one(K, C) || {K, C} <- fields()],
ok.
install_one(Key, Code) ->
mnesia:dirty_write(#foo_map{key = {enc, Key}, val = Code}),
mnesia:dirty_write(#foo_map{key = {dec, Code}, val = Key}).
fields() ->
[{hello, 1},
{help, 2},
{done, 3}].
%% Runtime interface.
encode0(Key) ->
ets:lookup_element(foo_map, {enc, Key}, #foo_map.val).
decode0(Code) ->
ets:lookup_element(foo_map, {dec, Code}, #foo_map.val).
Option 5:
Like option (1), only you write fun code that writes the boring code for you
when needed (these tables tend to change infrequently). All definitions would
be in your codegen module in a function similar to fields() above, there
would be a few functions converting that to AST and using erl_pp to write
out resulting source file. You than keep this under source control and
remember to run codegen when you change master table (or teach it to some
Makefile).
HTH,
-- Jachym
More information about the erlang-questions
mailing list