Reputation: 21
I have an Erlang program that can be compiled for multiple platforms.
What I want is to separate code for the different platforms. What would be the most Erlang way to go about this?
I want to achieve something like this:
-module(platform_1).
-export([platform_init/0, platform_do_work/1, platform_stop/0]).
...
-module(platform_2).
-export([platform_init/0, platform_do_work/1, platform_stop/0]).
...
-module(main).
-export([start/0]).
main() ->
platform::init(),
platform::do_work("The work"),
platform::stop().
(Obviously the above code does not work, it's missing the part I'm asking about.)
platform
and provide only one of them during compilation.-ifdefs
in a platform
module to wrap the platform specific modules.-behavior
to specify a common contract.-export
macros to provide a common contract.I'm sure there are other solutions out there. I just didn't find any idioms out there for this general use case.
Upvotes: 2
Views: 70
Reputation: 14042
One simple way to achieve what you want is to store the module in a variable and use this variable to call the right module/function.
For example you could start your application with an extra parameter which select the right platform:
erl [your parameters] -extra "platform=platform_1"
Define your platform dependant modules as you want
-module(platform_1).
-export([platform_init/0, platform_do_work/1, platform_stop/0]).
...
and
-module(platform_2).
-export([platform_init/0, platform_do_work/1, platform_stop/0]).
...
and retrieve in the main function the Platform parameter
-module(main).
-export([start/0]).
main() ->
Args = init:get_plain_arguments().
[Platform] =
[list_to_atom(lists:nthtail(9,Y))
|| Y <- Args, string:prefix(Y,"platform=") =/= nomatch].
Platform:init(),
Platform:do_work("The work"),
Platform:stop().
I am not convinced at all that is the best way, mainly because you need to know, when you use a module if it is platform dependent or not (and the code to get the platform is not clean).
I think it would be better if the modules themselves were responsible for the platform choice. I will try to complete this answer as soon as I have some spare time.
Upvotes: 1
Reputation: 1958
My first instinct here would be to define a new behavior, like:
-module platform.
-type state() :: term().
-type work() :: {some, work} | ….
-callback init() -> {ok, state()}.
-callback do_work(work(), state()) -> {ok, state()}.
-callback stop(state()) -> ok.
-export([run/1]).
-spec run(module()) -> ok.
run(Module) ->
{ok, State0} = Module:init(),
{ok, State1} = Module:do_work({some, work}, State0),
ok = Module:stop(State1).
Then, your modules can implement the behavior (I would probably place them in a folder like src/platforms
and they'll look something like…
-module first_platform.
-behavior platform.
-export [init/0, do_work/2, stop/1].
-spec init() -> {ok, platform:state()}.
init() –>
…,
{ok, State}.
…
And your main module can have a macro or an environment variable or something where it can retrieve the platform to use, and look something like…
-module main.
-export [start/0].
-spec start() -> ok.
start() ->
Platform =
application:get_env(your_app, platform, first_platform),
platform:run(Platform).
or even…
-module main.
-export [start/0].
-spec start() -> ok.
start() ->
platform:run().
…and you do the figuring out of which platform to use within platform
itself.
Upvotes: 3