Cure

A small library that interfaces Elixir-code with C/C++ programs using Erlang/Elixir Ports. Provides Mix tasks to kickstart the development process.

Example

The following example loads a program called "program" which is located in the ./c_src/ directory of your project.

# Open the Port to the C/C++ program:
{:ok, server} = Cure.load "./c_src/program"
# Depending on the kind of communication you need, there are several modes for
# sending and receiving messages:
# Option 1 (once, asynchronous):
# Useful for messages that generate a single response
# without callback function
server |> Cure.send_data "any binary can be transmitted to the C/C++ side!", :once
receive do
{:cure_data, response} ->
# Process response here..
end
# with callback function
server |> Cure.send_data <<1, 2, 3>>, :once, fn(response) ->
# Process response here..
end
# Option 2 (noreply, asynchronous):
# Useful if you don't need a response from the C/C++ side or if you are already subscribed to the Cure process.
server |> Cure.send_data "more data..", :noreply
# Option 3 (permanent, asynchronous)
# Useful when you want to keep processing responses after you send an initial message
# (NOTE: After this function is used once, you can use :noreply and still keep getting responses)
# without callback function
server |> Cure.send_data "abcdef", :permanent
receive do
{:cure_data, msg} ->
# Process response here...
end
# with callback function
server |> Cure.send_data "...", :permanent, fn(response) ->
# Process response here..
end
# Option 4 (synchronous):
result1 = server |> Cure.send_data "testdata", :sync # a timeout can also be added as last argument
server |> Cure.send_data <<1,2,3>>, :sync, fn(response) ->
IO.inspect response
end
# Close the program:
server |> Cure.stop # stops the supervised server

By default, Cure starts a supervisor which supervises all of its children (a child in this case is a GenServer that communicates with a C/C++ program). A child is added to the supervision tree with Cure.load(program_name). If you don't want this behaviour, you can also directly start a server with one of the following lines of code:

# Option 1:
{:ok, server} = Cure.Server.start_link "program_name"
# Option 2:
{:ok, server} = Cure.Server.start "program_name"
# Stopping the server:
:ok = Cure.Server.stop(server)

A process can also (un)subscribe to responses coming from the C/C++ side using the following functions:

# Option 1: receives responses as {:cure_data, ...}
server |> Cure.subscribe
server |> Cure.unsubscribe
# Option 2: passes every response to a function that processes it
fun = fn(response) -> IO.inspect response end
server |> Cure.subscribe fun
server |> Cure.unsubscribe fun

Examples that use Cure can be found at the following links:

Getting started

Add the Cure dependency to your mix.exs file:

def deps do
[{:cure, "~> 0.4.0"}]
end

Fetch & compile dependencies

mix deps.get
mix deps.compile

Start developing in C/C++

mix cure.bootstrap
mix compile.cure
mix compile.cure.deps

Another option is to add the last 2 tasks to your mix.exs to compile all code automatically when you type mix.compile:

def project do
[...,
compilers: Mix.compilers ++ [:cure, :"cure.deps"],
...]
end

C/C++ code

C/C++ code is currently placed in the c_src directory of your application. It can interface with Elixir-code based on 2 important functions:

  1. read_msg to read data coming from Elixir;
  2. send_msg to send data to Elixir.
#include <elixir_comm.h>

Makefile

The command "mix cure.bootstrap" generates a basic Makefile (in ./c_src/) that handles the compilation of all your C-code. This file is only generated if it doesn't exist yet so it's safe to add modifications for when your C-files need extra includes to compile properly.

The command "mix cure.make" uses the Makefile to compile all your C/C++ code.

More information regarding Ports