Ripster
I am writing an application that reads incoming packets and I’d like to decode them and route them to the appropriate handler.
The header of each packet is what defines its type.
So for example a user login packet may look like this:
<<
header::integer-little-8,
ulen::unsigned-little-integer-32,
user::binary-size(ulen),
plen::unsigned-little-integer-32,
pass::binary-size(plen)
>>
I’d like to be able to do something like this in my receive function:
<<
header::integer-little-8,
data::bytes
>> = received
{:ok, packet} = Packet.parse(header, data)
:ok = packet.run()
Where Packet.parse/2 is defined at compile time.
defmodule Packet do
defmacro __using__(header) do
# track __CALLER__ and header?
@behaviour Packet
end
@callback parse(BitString) :: {:ok, Map}
@callback def run() :: :ok | {:error, reason}
# Generate these at compile time
def parse(1, data), do: Packets.Login.parse(data)
def parse(2, data), do: Packets.Chat.parse(data)
end
defmodule Packets.Login do
use Packet, header: 1
defstruct :username, :password
def parse(packet_data), do: # decode packet
def run, do: # run code to handle packet
end
Is there a way to do this using macros or a better way that wouldn’t require macros?
Trending in Questions
Hello!
Suppose you are building workflow (order / task / payment) processing system with the following requirements:
Each workflow con...
New
I’m in search of an Elixir library that offers PDF generation capabilities similar to Ruby’s Prawn. While there have been discussions abo...
New
I’m looking to build a personal workflow to quickly deploy web applications written in elixir/phoenix, for local consumption (ie not on t...
New
Before I dive in myself, did anyone successfully sprinkle Hologram into their existing LiveView app?
Looking for hints regarding:
Addi...
New
Kia ora,
We have been using elixir-google-api to connect to Google Drive. However, with the updates to Tesla due to CVEs this is now bro...
New
Hi all, I wanted to ask how the community is dealing with post-release steps.
Today we have Ecto migrations, which make sure that the db...
New
Hello,
I have an Elixir backend that implements a custom protocol over TCP. I want to load test the backend and assess the performance o...
New
Other Trending Topics
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve.
They are GUI (Emerge) and State management (S...
New
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #blog-post
- #phoenix_html
- #iex
- #graphql
- #ai
- #genstage
- #elixirconf-us
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #security
- #hex










First 10 of 14 Posts
mischov
My current favorite library to read for learning about neat code generation is NimbleParsec. It might give you some ideas.
Edit: The Elixir Forum thread for the library is also good- searching it for “quoted expressions” might give you a few more ideas.
kip
I don’t think you need a macro for this. Something like this should do it. Of course you would probably
encapsulate the header->function mapping differently in real use rather than a module attribute.
Ripster
Part of my goal is to avoid creating a map of header => Module manually. I want the packet header to be part of the packet definition and all the parse functions to be generated.
I just can’t seem to figure out how to create that list at compile time. Heck if I could even just extend the Packet module that would be great.
I just can’t seem to figure out a way to do this. Even if I keep a module attribute around using
persist: trueI will end up with an error because the module gets compiled and I can no longer continue toModule.put_attributeI really just don’t want to end up with a separate file to touch every time a new packet type is created and I also don’t want to have a module that is just a massive list of mappings if this kind of thing can be generated.
kip
Um, so how do you know at compile type what the packet types are, and what the processing should be for each packet? I’m surely misunderstanding you but it sounds like you’re saying something like this, which pattern matches on the header. These can easily be generated at compile time - but you’d still need to know what the processing-per-packet would be?
Ripster
You’re right that I want to pattern match on the header. I want the header to be defined in the module that handles the actual packet though. Otherwise you end up with a module that is nothing but a large list mapping the header numbers to modules. I don’t want to separate that information.
If I was able to get every module that has
use Packet, header: 1or implements aPacketbehaviour, or every module with a@packet_headerattribute, then I could build the list without having to list them all out manually.kip
Here’s some code that will introspect the known modules and identify if they implement a behaviour. I’ve shown two ways to get the known modules, one is Elixir release dependent, the other uses the Mix api. Both are really only available in a build environment but I guess thats OK for your use. The second issue is that they introspect artefacts that are already compiled which may introduce some dependency issues.
kip
Using the code above you could then generate the functions you’re after with something like:
OvermindDL1
I really should release my library of mine that does that at compile-time… >.>
Hmm, actually you could use my ProtocolEx (it’s like the Protocol Library built into elixir, except it is based on matchers and guards instead of types, and has the ability to be faster too by inlining and more) library if you don’t mind putting your
parsefunctions in implementation modules (you can of course put multiples of the parse functions in a single implementation if you want, so you can group like-functionality.Ripster
For some reason the functions are not getting generated unless I compile the app, modify the router
I have defined 2 modules
packet.ex
router.ex
Now if I use this by defining
server/packet/login.ex
server/packet/router.ex
The router does not end up with any parse functions in it.
If I compile the app and then modify server/packet/router.ex (add or remove a blank line) then recompile the parse function does get generated.
Have I done something wrong here or did I miss something important?
OvermindDL1
That just means the compile cannot figure out the dependency chain properly, adding some
require’s would help that but that would miss ‘new’ ones, or making the module as always dirty would fix it but would have to recompile it every time, or a compiler plugin (as ProtocolEx does) would always work instead too.Basically the issue is that the Elixir compiler doesn’t know when it needs to recompile a module, which
require’s fixes for known things, but for unknown things it doesn’t know when to add the dependency chain.