With heartfelt thanks to the many people who have already tried hs-bindgen and given us feedback, we have steadily been working towards the first official release (see Contributors for the full list). In case you missed the announcement of the first alpha, hs-bindgen is a tool for automatic construction of Haskell bindings for C libraries: just point it at a C header and let it handle the rest. Because we have fixed some critical bugs in this alpha release, but we’re not quite ready yet for the first full official release, we have tagged a second alpha release. In the remainder of this blog post we will briefly highlight the most important changes; please refer to the CHANGELOG.md of hs-bindgen and of hs-bindgen-runtime for the full list of changes, as well as for migration hints where we have introduced some minor backwards incompatible changes.

Bugfixes

The most important fixes for bugs in the generated code are:

  • The implementation of peek and poke for bitfields was broken, which could lead to segfaults.
  • Duplicate record fields are now usable also in Template Haskell mode.
  • Patterns for unsigned enums now get the right value.

We have also resolved a number of panics during code generation, but those would not have resulted in incorrect generated code (merely in no code being generated at all).

New features

  • Implicit fields arise when one struct (or union) is nested in another, without any field name or tag:

    struct outer {
      int x;
      struct {
        int y;
        int z;
      };
    };

    We now support such implicit fields; both the inner (anonymous) struct as well as the corresponding field of the outer struct will be named after the first field of the inner struct1:

    data Outer = Outer {
        x :: CInt
      , y :: Outer_y
      }
    
    data Outer_y = Outer_y {
        y :: CInt
      , z :: CInt
      }

    For this particular case we could also have chosen to flatten the structure and add y and z directly to Outer, but that does not work in all cases (for example, when we have an anonymous struct inside a union), so instead we opt for consistency and always generate an explicit type for the inner struct.

  • Unnamed bit-field declarations, which are used to control padding, are now supported:

    struct bar {
      signed char x : 3;
      signed char   : 3;  // Explicit padding
      signed char y : 2;
    };
  • We used to distinguish between parse predicates (which files should hs-bindgen parse at all?) and selection predicates (for which C declarations should we generate Haskell declarations?). This was confusing, and as we are getting better at skipping over declarations with unsupported features (and that list is dwinding anyway), parse predicates are not that useful anymore. Parse predicates therefore have been removed entirely; we simply always parse everything (selection predicates are still very much an important feature of course).

  • Some infrastructure for and around binding specifications has been improved. For example, we now distinguish between macros and non-macros of the same name, and our treatment of arrays has changed slightly. For example, given

    typedef char T [];
    void foo (T xs);

    we now generate

    foo :: Ptr (Elem T) -> IO ()

    We do not use Ptr CChar, because T might have an existing binding in another library (with an external binding specification), and we don’t know what the type of the elements of T are (it could for example be some newtype around CChar). Elem is a member of a new IsArray class, part of the hs-bindgen-runtime.

  • Top-level anonymous enums are now supported. For example,

    enum {A, B};

    results in

    pattern A :: CUInt
    pattern A = 0
    
    pattern B :: CUInt
    pattern B = 1

    (Normally an enum results in a newtype around the enum’s underlying type, and the patterns are for that newtype instead.)

  • We now generate bindings for static global variables (such globals are sometimes used in headers that also contain static function bodies).

  • All definitions required by the generated code are now (re-)exported from hs-bindgen-runtime, so that it becomes the only package dependency that needs to be declared (no need for ghc-prim or primitive anymore).

This list is not complete; some other less common edge cases have also been implemented.

Conclusions

Although we are still working on some finishing touches before we can release the first official version of hs-bindgen, it is already being put to good use on various projects. There are only a handful of missing C features left, all of which low priority edge cases (though if you have a specific use case for any of these, do let us know!). So if you are interested, please do try it out, and let us know if you find any problems. There should be no major breaking changes between now and the first official release.


  1. This is the version that uses the --omit-field-prefixes option, which generates code that relies on DuplicateRecordFields and OverloadedRecordDot.↩︎