Search code examples
erlang

Erlang: New utility function which gets a record's field names from a known set of records


I want to create a function which is able to return a list of a records field names. There are a set number of records in the project but defined in different modules. However at compile time they haven't been created yet.

print_record(Record) ->
  [Name|Values] = tuple_to_list(Record),
  case Name of
    test_record_one ->
      record_info(fields, test_record_one);
    test_record_two ->
      record_info(fields, test_record_two);
    ...
    Else ->
      Else
  end.

The issue with this code is when compiling, the compiler returns with the records being undefined. This is true however I was wondering if there is a way to get around this.


Solution

  • I don't quite understand on what you mean by at compile time they haven't been created yet, because record definition must be at least defined at compile time.

    Let's say the record definition is defined in a header file (.hrl) which is accessible by your utility module.

    %%Filename: records.hrl
    
    -record(test_record_one, {field1, field2}).
    -record(test_record_two, {field3, field4}).
    

    And the utility module

    -module(test_util). 
    
    -include("records.hrl"). %%assuming this hrl file is in the same directory, otherwise need to use include_lib directive
    -export([print_record/1]).
    -export([test_1/0]).
    -export([test_2/0]).
    
    print_record(Record) ->
      [Name| Values] = tuple_to_list(Record), 
      case Name of
        test_record_one -> record_info(fields, test_record_one);
        test_record_two -> record_info(fields, test_record_two);
                   Else -> ok
      end.
    
    test_1() ->
      test_util:print_record(#test_record_one{}).
    
    test_2() ->
      test_util:print_record(#test_record_two{field3 = "abc", field4 = 100}).
    

    Testing directly from shell

    %%Must inform Erlang shell on the record definition
    > rd(test_record_one, {field1, field2}).
    test_record_one
    > rd(test_record_two, {field3, field4}).
    test_record_two
    > test_util:print_record(#test_record_one{}). 
    [field1,field2]
    > test_util:print_record(#test_record_two{field3 = "abc", field4 = 100}).
    [field3,field4]
    

    Testing by calling the test functions

    > test_util:test_1().
    [field1,field2]
    > test_util:test_2().
    [field3,field4]