Next Previous Contents

3.2 Calling Haskell from C

[ The example in this section is closely based on a hybrid example application suggested by Conal Elliott on the ffi mailing list, March 1998. ]

Suppose you have developed a graphical editor framework in a language like C or C++. It presents a standard-looking environment for editing documents (e.g., an application that implements MDI under Win32), defining toolbars, menus, keyboard accelerators, and a workspace. Multiple document windows can be created inside the workspace window, each editing a separate document. Selecting a command from a menu causes a command to be forwarded to the document currently being edited.

The editing framework defines the set of commands it can perform on its editable documents (e.g., Save() to store the document), allowing you to plug in any editor that implements these commands. Now, suppose you want to embed an editor you have implemented using Fran (or some other Haskell library) inside this generic editor framework - what do you do?

H/Direct can be used here to generate stubs that provide C interfaces to Haskell functions, provided you specify the functions you want to `export' from Haskell in IDL.

A systematic solution to the problem of combining code written in different languages is to use a language neutral software component technology like COM. We'll look at how H/Direct supports this in the next couple of sections, but will just remark here that there are cases where buying into a general component technology may not be worth it (e.g., for reasons of performance, effort, religion etc).
For the editor framework application, here's some of the operations a plug-in editor will need to support:

module Editor {

import "win32.idl";
import "hdirect.idl";

typedef  HaskellObject HEditor;

HEditor  New([in,string]char* nm);

bool     SetView ( [in]HEditor hed
                 , [in]HWND hwnd
                 );

void     Save( [in]HEditor hed
             , [in, string]char* fname
             );

void     Quit( [in]HEditor hed );

};

The operations perform the following tasks:

Given the above specification as input, the H/Direct IDL compiler will generate a number of input files, Figure Generated Haskell stubs shows the relationships between the different files:

Calling Haskell from C

In the case of Editor, the following EditorProxy source will be generated:

-- EditorProxy.hs  --
module EditorProxy where

import HDirect    ( HaskellObject )
import Win32      ( HWND )
import EditorTypes ( HEditor )
import Editor
    (
      new     -- :: String -> IO HEditor
    , setView -- :: HEditor -> HWND -> IO Int32
    , save    -- :: HEditor -> String -> IO ()
    , quit    -- :: HEditor -> IO ()
    )

foreign export stdcall "New" primNew :: Addr -> IO HaskellObject
primNew :: Addr -> IO HaskellObject
primNew ptr = ...

foreign export stdcall "SetView" primSetView :: HaskellObject -> Addr -> IO Int32
primSetView :: HEditor -> HWND -> IO Int32
primSetView hed hwnd = ...

foreign export stdcall "Save" primSave :: HaskellObject -> Addr -> IO ()
primSave :: HEditor -> Addr -> IO ()
primSave hed ptr = ...

foreign export stdcall "Quit" primQuit :: HaskellObject -> IO ()
primQuit :: HEditor -> IO ()
primQuit hed = ...

The stubs corresponding to the Editor actions are exported using foreign export FFI declarations. The implementation of a stub calls upon its corresponding Editor action once it has marshalled all the parameters into an appropriate form.

The generated EditorTypes module contains just a single a type synonym:

module EditorTypes where
import HDirect ( HaskellObject )

type HEditor = HaskellObject

The generated Haskell source can now be compiled with GHC to produce object files that has got entry points that are callable from a language like C or C++. To help out on the C side, the IDL compiler will also generate a header file that contains the type declarations and function prototypes corresponding to the contents of the Editor module declaration:

/* editor.h */
#include "win32.h"
#include "hdirect.h"
typedef HaskellObject HEditor;
extern HEditor New (/*[in,string]*/ char* nm);
extern int    SetView ( /*[in]*/HEditor hed
                      , /*[in]*/HWND hwnd
                      );

extern void    Save( /*[in]*/HEditor hed
                   , /*[in, string]*/char* fname
                   );

extern void     Quit( /*[in]*/ HEditor hed );

Assuming that the Haskell run-time environment has been packaged up in such a way that you can link in the stubs and your Haskell code with the rest of application, you should now be all set to go hybrid.

Typing/safety considerations

The EditorTypes module generated by the IDL compiler doesn't convey too much type information to the Haskell programmer:

module EditorTypes where
import HDirect ( HaskellObject )

type HEditor = HaskellObject

i.e., an HEditor is implemented by a stable pointer to some Haskell heap object. This is extremly unsafe since Editor's exported IO actions could be passed any HaskellObject value, and the stubs would have no way of telling if it was a stable pointer to a heap object of the correct type, so we'd really like to impose some checking on HaskellObjects being passed from C to Haskell. There's a couple of ways in which to do this, all centred around annotating HaskellObject typedefs:


Next Previous Contents