问题描述
我试图编写一个宏来保存用于数据结构的手动键入序列化/反序列化功能。
目标是能够调用宏
Macros.implement_message(TheMessage,{:field_1,unsigned-size(8)},{:field_2,unsigned-size(32)})
defmodule TheMessage do
defstruct [
field_1: 0,field_2,]
def serialize(%TheMessage{field_1: field_1,field_2: field_2}) do
<<field_1::unsigned-size(8),field_2::unsigned-size(32)>>
end
def deserialize(<<field_1::unsigned-size(8),field_2::unsigned-size(32)>>) do
%TheMessage{field_1: field_1,field_2: field_2}
end
end
这是我所拥有的,需要一些帮助来填写序列化和反序列化功能:
defmodule Macros do
@moduledoc false
@doc """
Implement a serializable structure,including serialize and a deserialize functions. This
will defmodule the module,defstruct the fields and their defaults,and generate serialize
and deserialize functions that function-head-match the module.
defmodule MyModule do
Macros.implement_message(TheMessage,[
{:field_1,unsigned-size(32)},])
end
# Equivalent code:
defmodule MyModule do
defmodule TheMessage do
defstruct [
field_1: 0,]
def serialize(%MyModule{field_1: field_1,field_2: field_2}) do
<<field_1::unsigned-size(8),field_2::unsigned-size(32)>>
end
def deserialize(<<field_1::unsigned-size(8),field_2::unsigned-size(32)>>) do
%MyModule{field_1: field_1,field_2: field_2}
end
end
end
## Parameters
- module The module atom
- field_tuples [{field_atom,default_value,bin_rep}]
"""
defmacro implement_message(module,field_tuples) do
module_def_kwl = Enum.map(field_tuples,fn({field_atom,_bin_rep}) -> {field_atom,default_value} end)
fields_assignments = Enum.map(module_def_kwl,_default_value}) -> {field_atom,Atom.to_string(field_atom)} end)
ast = quote do
defmodule unquote(module) do
defstruct unquote(module_def_kwl)
def serialize(%unquote(module){unquote(fields_assignments)}) do
# ?
end
def deserialize(_how_to_pattern_match?) do
# ?
end
end
end
[ast]
end
end
解决方法
关键是要使用unquote_splicing
和Macro.var
。
defmodule Serializer do
defmacro defserialize(_,fields) do
map_fields =
fields
|> Keyword.keys()
|> Enum.map(&{&1,Macro.var(&1,nil)})
binary_fields =
fields
|> Enum.map(fn {name,bits} ->
var_name = Macro.var(name,nil)
quote do
unquote(var_name) :: unquote(bits)
end
end)
quote do
def serialize(%{unquote_splicing(map_fields)}) do
<<unquote_splicing(binary_fields)>>
end
def deserialize(<<unquote_splicing(binary_fields)>>) do
%{unquote_splicing(map_fields)}
end
end
end
end
defmodule Foo do
import Serializer
defserialize Bar,[
x: 4,y: 5,z: 7
]
end