From 84adefa331c4159d432d22840663c38f155cd4c1 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Fri, 20 Nov 2009 14:54:40 +0000 Subject: The R13B03 release. --- system/doc/getting_started/records_macros.xml | 328 ++++++++++++++++++++++++++ 1 file changed, 328 insertions(+) create mode 100644 system/doc/getting_started/records_macros.xml (limited to 'system/doc/getting_started/records_macros.xml') diff --git a/system/doc/getting_started/records_macros.xml b/system/doc/getting_started/records_macros.xml new file mode 100644 index 0000000000..45617f0183 --- /dev/null +++ b/system/doc/getting_started/records_macros.xml @@ -0,0 +1,328 @@ + + + + +
+ + 20032009 + Ericsson AB. All Rights Reserved. + + + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + + + Records and Macros + + + + + record_macros.xml +
+

Larger programs are usually written as a collection of files with + a well defined interface between the various parts.

+ +
+ The Larger Example Divided into Several Files +

To illustrate this, we will divide the messenger example from + the previous chapter into five files.

+ + mess_config.hrl + header file for configuration data + mess_interface.hrl + interface definitions between the client and the messenger + user_interface.erl + functions for the user interface + mess_client.erl + functions for the client side of the messenger + mess_server.erl + functions for the server side of the messenger + +

While doing this we will also clean up the message passing + interface between the shell, the client and the server and define + it using records, we will also introduce macros.

+ +%%%----FILE mess_config.hrl---- + +%%% Configure the location of the server node, +-define(server_node, messenger@super). + +%%%----END FILE---- + +%%%----FILE mess_interface.hrl---- + +%%% Message interface between client and server and client shell for +%%% messenger program + +%%%Messages from Client to server received in server/1 function. +-record(logon,{client_pid, username}). +-record(message,{client_pid, to_name, message}). +%%% {'EXIT', ClientPid, Reason} (client terminated or unreachable. + +%%% Messages from Server to Client, received in await_result/0 function +-record(abort_client,{message}). +%%% Messages are: user_exists_at_other_node, +%%% you_are_not_logged_on +-record(server_reply,{message}). +%%% Messages are: logged_on +%%% receiver_not_found +%%% sent (Message has been sent (no guarantee) +%%% Messages from Server to Client received in client/1 function +-record(message_from,{from_name, message}). + +%%% Messages from shell to Client received in client/1 function +%%% spawn(mess_client, client, [server_node(), Name]) +-record(message_to,{to_name, message}). +%%% logoff + +%%%----END FILE---- + +%%%----FILE user_interface.erl---- + +%%% User interface to the messenger program +%%% login(Name) +%%% One user at a time can log in from each Erlang node in the +%%% system messenger: and choose a suitable Name. If the Name +%%% is already logged in at another node or if someone else is +%%% already logged in at the same node, login will be rejected +%%% with a suitable error message. + +%%% logoff() +%%% Logs off anybody at at node + +%%% message(ToName, Message) +%%% sends Message to ToName. Error messages if the user of this +%%% function is not logged on or if ToName is not logged on at +%%% any node. + +-module(user_interface). +-export([logon/1, logoff/0, message/2]). +-include("mess_interface.hrl"). +-include("mess_config.hrl"). + +logon(Name) -> + case whereis(mess_client) of + undefined -> + register(mess_client, + spawn(mess_client, client, [?server_node, Name])); + _ -> already_logged_on + end. + +logoff() -> + mess_client ! logoff. + +message(ToName, Message) -> + case whereis(mess_client) of % Test if the client is running + undefined -> + not_logged_on; + _ -> mess_client ! #message_to{to_name=ToName, message=Message}, + ok +end. + +%%%----END FILE---- + +%%%----FILE mess_client.erl---- + +%%% The client process which runs on each user node + +-module(mess_client). +-export([client/2]). +-include("mess_interface.hrl"). + +client(Server_Node, Name) -> + {messenger, Server_Node} ! #logon{client_pid=self(), username=Name}, + await_result(), + client(Server_Node). + +client(Server_Node) -> + receive + logoff -> + exit(normal); + #message_to{to_name=ToName, message=Message} -> + {messenger, Server_Node} ! + #message{client_pid=self(), to_name=ToName, message=Message}, + await_result(); + {message_from, FromName, Message} -> + io:format("Message from ~p: ~p~n", [FromName, Message]) + end, + client(Server_Node). + +%%% wait for a response from the server +await_result() -> + receive + #abort_client{message=Why} -> + io:format("~p~n", [Why]), + exit(normal); + #server_reply{message=What} -> + io:format("~p~n", [What]) + after 5000 -> + io:format("No response from server~n", []), + exit(timeout) + end. + +%%%----END FILE--- + +%%%----FILE mess_server.erl---- + +%%% This is the server process of the messenger service + +-module(mess_server). +-export([start_server/0, server/0]). +-include("mess_interface.hrl"). + +server() -> + process_flag(trap_exit, true), + server([]). + +%%% the user list has the format [{ClientPid1, Name1},{ClientPid22, Name2},...] +server(User_List) -> + io:format("User list = ~p~n", [User_List]), + receive + #logon{client_pid=From, username=Name} -> + New_User_List = server_logon(From, Name, User_List), + server(New_User_List); + {'EXIT', From, _} -> + New_User_List = server_logoff(From, User_List), + server(New_User_List); + #message{client_pid=From, to_name=To, message=Message} -> + server_transfer(From, To, Message, User_List), + server(User_List) + end. + +%%% Start the server +start_server() -> + register(messenger, spawn(?MODULE, server, [])). + +%%% Server adds a new user to the user list +server_logon(From, Name, User_List) -> + %% check if logged on anywhere else + case lists:keymember(Name, 2, User_List) of + true -> + From ! #abort_client{message=user_exists_at_other_node}, + User_List; + false -> + From ! #server_reply{message=logged_on}, + link(From), + [{From, Name} | User_List] %add user to the list + end. + +%%% Server deletes a user from the user list +server_logoff(From, User_List) -> + lists:keydelete(From, 1, User_List). + +%%% Server transfers a message between user +server_transfer(From, To, Message, User_List) -> + %% check that the user is logged on and who he is + case lists:keysearch(From, 1, User_List) of + false -> + From ! #abort_client{message=you_are_not_logged_on}; + {value, {_, Name}} -> + server_transfer(From, Name, To, Message, User_List) + end. +%%% If the user exists, send the message +server_transfer(From, Name, To, Message, User_List) -> + %% Find the receiver and send the message + case lists:keysearch(To, 2, User_List) of + false -> + From ! #server_reply{message=receiver_not_found}; + {value, {ToPid, To}} -> + ToPid ! #message_from{from_name=Name, message=Message}, + From ! #server_reply{message=sent} + end. + +%%%----END FILE--- +
+ +
+ Header Files +

You will see some files above with extension .hrl. These + are header files which are included in the .erl files by:

+ +-include("File_Name"). +

for example:

+ +-include("mess_interface.hrl"). +

In our case above the file is fetched from the same directory as + all the other files in the messenger example. (*manual*).

+

.hrl files can contain any valid Erlang code but are most often + used for record and macro definitions.

+
+ +
+ Records +

A record is defined as:

+ +-record(name_of_record,{field_name1, field_name2, field_name3, ......}). +

For example:

+ +-record(message_to,{to_name, message}). +

This is exactly equivalent to:

+ +{message_to, To_Name, Message} +

Creating record, is best illustrated by an example:

+ +#message_to{message="hello", to_name=fred) +

This will create:

+ +{message_to, fred, "hello"} +

Note that you don't have to worry about the order you assign + values to the various parts of the records when you create it. + The advantage of using records is that by placing their + definitions in header files you can conveniently define + interfaces which are easy to change. For example, if you want to + add a new field to the record, you will only have to change + the code where the new field is used and not at every place + the record is referred to. If you leave out a field when creating + a record, it will get the value of the atom undefined. (*manual*)

+

Pattern matching with records is very similar to creating + records. For example inside a case or receive:

+ +#message_to{to_name=ToName, message=Message} -> +

is the same as:

+ +{message_to, ToName, Message} +
+ +
+ Macros +

The other thing we have added to the messenger is a macro. + The file mess_config.hrl contains the definition:

+ +%%% Configure the location of the server node, +-define(server_node, messenger@super). +

We include this file in mess_server.erl:

+ +-include("mess_config.hrl"). +

Every occurrence of ?server_node in mess_server.erl + will now be replaced by messenger@super.

+

The other place a macro is used is when we spawn the server + process:

+ +spawn(?MODULE, server, []) +

This is a standard macro (i.e. defined by the system, not + the user). ?MODULE is always replaced by the name of + current module (i.e. the -module definition near the start + of the file). There are more advanced ways of using macros with, + for example parameters (*manual*).

+

The three Erlang (.erl) files in the messenger example are + individually compiled into object code file (.beam). + The Erlang system loads and links these files into the system + when they are referred to during execution of the code. In our + case we simply have put them in the same directory which is our + current working directory (i.e. the place we have done "cd" to). + There are ways of putting the .beam files in other + directories.

+

In the messenger example, no assumptions have been made about + what the message being sent is. It could be any valid Erlang term.

+
+
+ -- cgit v1.2.3