Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/saleyn/eixx
Erlang C++ Interface
https://github.com/saleyn/eixx
Last synced: 14 days ago
JSON representation
Erlang C++ Interface
- Host: GitHub
- URL: https://github.com/saleyn/eixx
- Owner: saleyn
- License: apache-2.0
- Created: 2010-11-19T16:12:11.000Z (almost 14 years ago)
- Default Branch: master
- Last Pushed: 2022-10-26T09:14:41.000Z (about 2 years ago)
- Last Synced: 2024-10-13T18:43:17.288Z (27 days ago)
- Language: C++
- Homepage:
- Size: 690 KB
- Stars: 137
- Watchers: 19
- Forks: 26
- Open Issues: 9
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
## eixx - Erlang C++ Interface Library ##
This library provides a set of classes for convenient marshaling
of Erlang terms between processes as well as connecting to other
distributed Erlang nodes from a C++ application.The marshaling classes are built on top of ei library included in
http://www.erlang.org/doc/apps/erl_interface.The library consists of two separate parts:
- Term marshaling (included by eterm.hpp or eixx.hpp).
- Distributed node connectivity (included by connect.hpp or eixx.hpp)The term marshaling part of the library has following features:
- Encoding/decoding of nested terms using a single function call
(eterm::encode() and eterm::eterm() constructor).
- Global atom table for fast manipulation of atoms.
- Ability to provide custom memory allocators.
- Use of reference-counted smart pointers for complex terms and
by-value copied simple terms (e.i. integers, doubles, bool, atoms).If you are simply doing term marshalling only the eterm.hpp header along
with one of the alloc headers is needed. This allows for header-only
usage of the marshalling capabilities.The connectivity library implements a richer set of features than
what's available in erl_interface - it fully supports process
linking and monitoring. The library is fully asynchronous and allows
handling many connections and mailboxes in one OS thread.Ths library is dependend on http://www.boost.org project and
erl_interface, which is a part of the www.erlang.org distribution.### Downloading ###
Repository location: http://github.com/saleyn/eixx
$ git clone [email protected]:saleyn/eixx.git
### Building ###
This library is dependent on BOOST.
If you need to customize location of BOOST or installation prefix, create a file called
`.cmake-args.${HOSTNAME}`. Alternatively if you are doing multi-host build with
identical configuration, create a file call `.cmake-args`. E.g.:There are three sets of variables present in this file:
1. Build and install locations.
* `DIR:BUILD=...` - Build directory
* `DIR:INSTALL=...` - Install directoryThey may contain macros:
* `@PROJECT@` - name of current project (from CMakeList.txt)
* `@VERSION@` - project version number (from CMakeList.txt)
* `@BUILD@` - build type (from command line)
* `${...}` - environment variable2. `ENV:VAR=...` - Environment var set before running cmake
3. `VAR=...` - Variable passed to cmake with -D prefix
Example:
```
$ cat > .cmake-args.${HOSTNAME}
DIR:BUILD=/tmp/@PROJECT@/build
DIR:INSTALL=/opt/pkt/@PROJECT@/@VERSION@
ENV:BOOST_ROOT=/opt/pkg/boost/current
ENV:BOOST_LIBRARYDIR=/opt/pkg/boost/current/gcc/lib
PKG_ROOT_DIR=/opt/pkg
```
* Run:
```
$ make bootstrap [toolchain=gcc|clang] [build=Debug|Release] \
[generator=make|ninja] [prefix=/usr/local] [verbose=true]
$ make [verbose=true]
$ make install # Default install path is /usr/local
```
There is a slight difference between installing release and debug builds. The release
build installer also tries to install a "debug version" of eixx (`libeixx_d.so`) that
must be build using build type `"debug"`. So for a release build do:
```
$ make bootstrap build=debug
$ make src/libeixx_d.so
$ make rebootstrap build=release
$ make
$ make install
```
After running `make bootstrap` two local links are created `build` and `inst`
pointing to build and installation directories.* To do a full cleanup of the current build and rerun bootstrap with
previously chosen options, do:
```
$ make distclean
$ make rebootstrap [toolchain=gcc|clang] [build=Debug|Release]
```
Note that the `rebootstrap` command remembers previous bootstrap options, but
if you give it arguments they will override the old ones.### Author ###
Serge Aleynikov
### LICENSE ###
Apache Public License v2.0
### Example ###
Manipulating Erlang terms is quite simple:
```cpp
eterm i = 10;
eterm s = "abc";
eterm a = atom("ok");
eterm t = {20.0, i, s, a}; // Constructs a tuple
eterm e = list{}; // Constructs an empty list
eterm l = list{i, 100.0, s, {a, 30}, list{}}; // Constructs a list
```
A convenient eterm::format() function implements an expression parser:```cpp
eterm t1 = eterm::format("{ok, 10}");
eterm t2 = eterm::format("[1, 2, ok, [{one, 10}, {two, 20}]]");
eterm t3 = eterm::format("A::int()"); // t3 is a variable that can be matched
```Pattern matching is done by constructing a pattern, and matching a term
against it. If varbind is provided, it'll store the values of all matched variables:```cpp
static const eterm s_pattern = eterm::format("{ok, A::string()}");eterm value = {atom("ok"), "abc"};
varbind binding;
if (value.match(s_pattern, &binding))
std::cout << "Value of variable A: " << binding["A"].to_string() << std::endl;
```
Erlang terms manipulation is pretty efficient. Creation/copying times of polymorphic
eterm's are shown below (project compiled in the `release` mode):
```
$ build/test/test-perf
1000000 iterations
Integer | latency: 6ns, speed: 150015001/s
Double | latency: 3ns, speed: 300030003/s
Bool | latency: 3ns, speed: 300030003/s
String | latency: 56ns, speed: 17646955/s
Atom1 | latency: 33ns, speed: 30000300/s
Atom2 | latency: 36ns, speed: 27272479/s
Binary1 | latency: 46ns, speed: 21428877/s
Binary2 | latency: 6ns, speed: 150015001/s
Tuple1 | latency: 73ns, speed: 13636239/s
Tuple2 | latency: 20ns, speed: 50000000/s
List1 | latency: 73ns, speed: 13636239/s
List2 | latency: 23ns, speed: 42857755/s
Apply speed | latency: 1190ns, speed: 840336/s
Apply/Create speed | latency: 966ns, speed: 1034482/s
Nested lists/tuples (1) speed | latency: 566ns, speed: 1764695/s
Nested lists/tuples (2) speed | latency: 466ns, speed: 2142887/s
Simple pattern match (1) | latency: 33ns, speed: 30003000/s
Nested pattern match (2) | latency: 333ns, speed: 2999940/s
```
The last four tests illustrate various ways of creating nested terms with tuples,
lists and other types. The other tests illustrate creation times of primitive
eterm types.Aside from providing functionality that allows to manipulate Erlang terms, this
library implements Erlang distributed transport that allows a C++ program to connect
to an Erlang node, exchange messages, make RPC calls, and receive I/O requests.
Here's an example use of the eixx library:```cpp
void on_message(otp_mailbox& a_mbox, boost::system::error_code& ec) {
// On timeout ec == boost::asio::error::timeout
if (ec == boost::asio::error::timeout) {
std::cout << "Mailbox " << a_mbox.self() << " timeout: "
<< ec.message() << std::endl;
} else {
// The mailbox has a queue of transport messages.
// Dequeue next message from the mailbox.
std::unique_ptr dist_msg;while (dist_msg.reset(a_mbox.receive())) {
std::cout << "Main mailbox got a distributed transport message:\n "
<< *dist_msg << std::endl;// Compile the following pattern into the corresponding abstract tree.
// The expression must be a valid Erlang term
static const eterm s_pattern = eterm::format("{From, {command, Cmd}}");varbind binding;
// Pattern match the message and bind From and Cmd variables
if (dist_msg->msg().match(s_pattern, &binding)) {
const eterm* cmd = binding->find("Cmd");
std::cout << "Got a command " << *cmd << std::endl;
// Process command, e.g.:
// process_command(binding["From"]->to_pid(), *cmd);
}
}
}// Schedule next async receive of a message (can also provide a timeout).
a_mbox.async_receive(&on_message);
}void on_connect(otp_connection* a_con, const std::string& a_error) {
if (!a_error.empty()) {
std::cout << a_error << std::endl;
return;
}// Illustrate creation of Erlang terms.
eterm t1 = eterm::format("{ok, ~i}", 10);
eterm t2 = tuple::make(10, 1.0, atom("test"), "abc");
eterm t3("This is a string");
eterm t4(tuple::make(t1, t2, t3));otp_node* node = a_con->node();
otp_mailbox* mbox = node->get_mailbox("main");// Send a message: {{ok, 10}, {10, 1.0, 'test', "abc"}, "This is a string"}
// to an Erlang process named 'test' running
// on node "abc@myhost"node->send(mbox.self(), a_con->remote_node(), atom("test"), t4);
}int main() {
use namespace eixx;otp_node node("abc");
otp_mailbox* mbox = node.create_mailbox("main");
node.connect(&on_connect, "abc@myhost");
// Asynchronously receive a message with a deadline of 10s:
mbox->async_receive(&on_message, 10000);node.run();
}
```### Testing distributed transport ###
$ make
Run tests:
$ LD_LIBRARY_PATH=$LD_LIBRARY_PATH:. ./test_eterm
Test distributed transport:
$ cd src
# In this example we assume the host name of "fc12".
[Shell A]$ erl -sname abc
(abc@fc12)1> register(test, self()).[Shell B]$ ./test_node -n a@fc12 -r abc@fc12
Connected to node: abc@fc12
I/O server got a message:
#DistMsg{
type=SEND,
cntrl={2,'',#Pid},
msg={io_request,#Pid,#Pid,
{put_chars,<<"This is a test string">>}}}The message above is a result of the `on_connect()` handler in `test_node.cpp`
issuing an rpc call to the abc@fc12 node of `io:put_chars("This is a test
string")'. This the call selects a locally registered process called
'io_server' as the group leader for this rpc call, the I/O output is sent
to that mailbox.Now you can try to send a message from Erlang to the 'main' mailbox
registered on the C++ node:[Shell A]
(abc@fc12)2> {main, a@fc12} ! "This is a test!".[Shell B]
Main mailbox got a message:
#DistMsg{
type=REG_SEND,
cntrl={6,#Pid,'',main},
msg="This is a test!"}