Record introspection at compile time
For AMF implementation, I have to define the mapping between Actionscript classes with something equivalent in Erlang. First thing came into my mind is record. However, I immediately encountered some of the difficulties when using records in Erlang
- Read: can’t use reflection easily after parsing AMF, I received the object with properties/values and I need to map to the corresponding record
- Write: Need to introspect the record and write its properties into binaries
I found this post from trapexit. However, I need specifically more than that.
Normally records are defined in a header file “.hrl”. And the idea is to generate utility codes (getter, setter, record meta data ….) at compile time. The heart of it is epp:parse_file/3 function. The generator (or the helper) reads header files, parses all records and create a compilable .erl file which contains all functions you need to do all magic with records:
- set(Obj, PropertyName, Value) -> {ok, NewObj, {PropertyName, Value}}
- get(Obj, PropertyName) -> {ok, Value}
- fields(record_name) -> Fields = [string()]
- fields_atom(record_name) -> Fields = [term()]
- type(Obj) -> record_name
messages.hrl – the source
record_helper.erl – the generator
record_utils.erl – the generated source
Below is the demonstration of how to generate and use the record utils
1> c(record_helper.erl). {ok,record_helper} 2> record_helper:make(["messages.hrl"], "."). ok 3> ls(). messages.hrl record_helper.beam record_helper.erl record_utils.erl ok 6> c(record_utils). {ok,record_utils} 7> rr("messages.hrl"). [abstract_message,async_message] 8> Obj = #async_message{}. #async_message{ parent = #abstract_message{ clientId = undefined,destination = undefined, messageId = undefined,timestamp = undefined, timeToLive = undefined,headers = undefined,body = undefined}, correlationId = undefined,correlationIdBytes = undefined} 9> record_utils:set(Obj, correlationId, "ADF-123-DDF-543"). {ok,#async_message{ parent = #abstract_message{ clientId = undefined,destination = undefined, messageId = undefined,timestamp = undefined, timeToLive = undefined,headers = undefined,body = undefined}, correlationId = "ADF-123-DDF-543", correlationIdBytes = undefined}, {correlationId,"ADF-123-DDF-543"}} 10> {ok,NewObj, _} = record_utils:set(Obj, correlationId, "ADF-123-DDF-543"). {ok,#async_message{ parent = #abstract_message{ clientId = undefined,destination = undefined, messageId = undefined,timestamp = undefined, timeToLive = undefined,headers = undefined,body = undefined}, correlationId = "ADF-123-DDF-543", correlationIdBytes = undefined}, {correlationId,"ADF-123-DDF-543"}} 11> record_utils:get(NewObj, correlationId). {ok,"ADF-123-DDF-543"} 12> record_utils:type(NewObj). async_message
record_helper.erl is not so clean as there’re all string concatenations … further improvements can be: