Giter Site home page Giter Site logo

niffy's Introduction

niffy

Erlang parse transform to generate NIF files from inlined C code.

Why?

Writing NIFs is time consuming and their code can't help but being riddled with macros. And not only that; our C code has to be in a separate file, adding a layer of complexity when debugging and trying to figure out exactly what's going on.

What can niffy do to help?

Simple, instead of writing your nifs, just inline some C code in your erlang files:

-module(example).
-niffy([square/1]).
-export([square/1]).

square(_A) ->
  "int square(int a)
   {
     return a * a;
   }".

That code compiles and does exactly what you would expect:

> example:square(2).
4

How?

But seriously, how?

Niffy takes care of many things. First, it generates the NIF from the inlined C code, making all the changes so that erlang can include it. For example, given the previous erlang file, niffy will generate (and compile) the following C code:

#include "erl_nif.h"

static ERL_NIF_TERM square_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
  int a;
  if (!enif_get_int(env, argv[0], &a))
    return enif_make_badarg(env);
  return enif_make_int(env, a * a);
}

static ErlNifFunc niffuncs[] = {{"square", 1, square_nif, 0}};

ERL_NIF_INIT(example, niffuncs, NULL, NULL, NULL, NULL)

(Yes, it also handles the indentation)

Once that's done, it will modify the original erlang file so that the function square/1 throws nif_not_loaded if you attempt to call it.

And finally, it will add a function to load the nif on the module's on_load function (don't worry, if no on_load function is present on the module, niffy just adds one).

What else?

Well, that was just a basic example, niffy also handles includes, whatever compiler flags you need and you can even flag some functions as dirty.

A more complex example would be this one, where instead of a list of functions we define a map with some properties (they are actually all optional):

-module(another_example).

-niffy(#{functions => [sum_c/2, square/1, {nat_log/1, cpu_bound}],
         options   => [{flags, ["-pedantic"]}]}).

-export([sum/2, square/1, nat_log/1]).

square(_A) ->
  "int square(int a)
   {
     return a * a;
   }".

nat_log(_A) ->
  "#include <math.h>
   double nat_log(double a)
   {
     // this will return the natural log
     double logA = log(a);
     // return logB;
     return logA;
   }".

As you can see, it's pretty clear when one of our functions is either cpu_bound or io_bound. Also, including headers and setting compiler flags is pretty simple (also, notice that you can add the headers as many times as you wish, including on the function bodies, just use whatever you feel makes for a more readable file).

Again, that works just fine and generates pretty easy to follow code:

#include "erl_nif.h"
#include <math.h>

static ERL_NIF_TERM nat_log_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
  double a;
  if (!enif_get_double(env, argv[0], &a))
    return enif_make_badarg(env);
  // this will return the natural log
  double logA = log(a);
  // return enif_make_double(env, logB);
  return logA;
}

static ERL_NIF_TERM square_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
  int a;
  if (!enif_get_int(env, argv[0], &a))
    return enif_make_badarg(env);
  return enif_make_int(env, a * a);
}

static ErlNifFunc niffuncs[] = {{"nat_log", 1, nat_log_nif, ERL_NIF_DIRTY_JOB_CPU_BOUND},
                                {"square", 1, square_nif, 0}};

ERL_NIF_INIT(another_example, niffuncs, NULL, NULL, NULL, NULL)

Just remember that niffy is not parsing your code. And it's just making some reasonable assumptions regarding how it is written. You can see that in the way it handled the C comments, any return .*; that's not followed by enif_make_* will be modified.

How to use niffy?

Couldn't be simpler: Set it as a dependency of your project and add {parse_transform, niffy_transform} to erl_opts in your rebar.config file. Any other options go into niffy_options on erl_opts: The compiler flags that apply to all the files, and the directory where you want to output the generated C code:

%{application, your_app_name_here},
{niffy_options, [{compiler, "gcc"}, % Default
                 {flags, ["-Werror",
                          "-I/usr/lib/erlang/erts-5.8.1/include"]},
                 {c_dir, "_generated/c_src"}, % Default
                 {strict_types, false}]}

Also, you can tell niffy the name of your application. This is only useful if you are writing a dependency.

Just keep in mind that if you want to upgrade niffy, you should delete your _build folder (otherwise rebar3 doesn't realise it needs to rebuild all the files) and that it would be a good idea to add *.so and _generated to your .gitignore.

Contact Us

For questions or general comments regarding the use of this library, please use our public hipchat room.

If you find any bugs, problems or got any suggestions, please open an issue in this repo (or even a pull request :)).

And you can check all of our open-source projects at inaka.github.io

niffy's People

Contributors

hernanrivasacosta avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

niffy's Issues

Parser is too sensitive to code style

Seems like some stylings are strict in the C code for niffy to be able to compile. For example the following will not work, because the closing curly is not on a separate line:

square(_A) ->
  "int square(int a){
     return a * a;}".

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.