How will the callers know the callbacks defined in ct_netconfc w/o being declared as behaviour?

We all know that in order to implement a module based on a behaviour say gen_server, we need to declare the module as gen_server behavior and implement the following callbacks.

-behaviour(gen_server).

%% gen_server callbacks
-export([init/1,
         handle_call/3,
         handle_cast/2,
         handle_info/2,
         terminate/2,
         code_change/3]).

ct_netconfc has defined a few ct-gen_conn callbacks without declaring any behaviour. Why is it? How will ct_gen_conn know that it has to call these callbacks?

-module(ct_netconfc).

%% ct_gen_conn callbacks
-export([init/3,
        handle_msg/3,
        handle_msg/2,
        terminate/2,
        close/1]).

Similarly, ct_netconfc has defined a few ct_conn_log callbacks without declaring any behaviour.

%% ct_conn_log callback
-export([format_data/2]).

Another query; who calls ct_netconfc:close? Is it ct_util_server as per code comment?

%% Called by ct_util_server to close registered connections before terminate.
close(Client) ->
    ct_util_server:module_info
    case get_handle(Client) of
	{ok,Pid} ->
	%% ......
1 Like

Please have a look at
https://www.erlang.org/doc/reference_manual/modules.html#behaviour-module-attribute

It is written It is possible to specify, it does not suggest you can’t have function callbacks in code without the behavior concept.

If you grep “Mod:” in ct_gen_conn module, you will get something like this:

7 matches for “Mod:” in buffer: ct_gen_conn.erl
381: try Mod:init(Name, Addr, InitData) of
421: {Reply, NewState} = Mod:handle_msg(Msg, State),
428: {Reply, NewState} = Mod:handle_msg(Msg, State),
434: case Mod:handle_msg(Msg, From, State) of
487: case Mod:handle_msg(Msg, State) of
511: Mod:terminate(Pid, State);
524: Mod:reconnect(Addr, State).

Those lines will try to call functions in a module found in Mod variable. Behaviors are not needed for that. This will be done in the runtime.

Function names are hardcoded and they’re kind of a contract between generic and specific code implementing the callbacks.

Behavior concept can be seen as a way of officially specifying the contract (and the behavior) to make it easier if someone is not fluent with generic part - so for example you can get a nice warnings when some of the callbacks were not implemented.

You can read more here: Clients and Servers | Learn You Some Erlang for Great Good!
I recommend writing your own behavior as described there - it is a great exercise to understand how it works and might be useful in future.

I would speculate it is ct_util module:

lib/common_test/src/ct_util.erl: catch CB:close(Pid),
lib/common_test/src/ct_util.erl: CB:close(Handle),

3 Likes