Difference between revisions of "GHC/Using the FFI"
(Created initial page of examples of using FFI with Visual C++) |
(Solution Files to Source Files and added freeHaskellFunPtr stuff) |
||
Line 11: | Line 11: | ||
Description: Copying duma.dll |
Description: Copying duma.dll |
||
Excluded From Build: No |
Excluded From Build: No |
||
− | * In the Solution Pane, rename the folder <code> |
+ | * In the Solution Pane, rename the folder <code>Source Files</code> to <code>Code Files</code> and adjust the properties of this folder so that <code>.h</code> files are also filtered into it. (This is a good idea for any C++ project because it keeps the <code>.cpp</code> and <code>.h</code> together in the same list which makes editing a C++ unit a lot easier.) |
* Also rename the folder <code>Header Files</code> to <code>Haskell Files</code> and change its filter to select <code>.hs</code> files. |
* Also rename the folder <code>Header Files</code> to <code>Haskell Files</code> and change its filter to select <code>.hs</code> files. |
||
* In the <code>Code Files</code> folder, create a text file called <code>Duma.def</code>, then select Properties → Custom Build Step → General and enter the following: |
* In the <code>Code Files</code> folder, create a text file called <code>Duma.def</code>, then select Properties → Custom Build Step → General and enter the following: |
||
Line 164: | Line 164: | ||
# <code>block</code> is used to make the creation of the font atomic with respect to asynchronous exceptions |
# <code>block</code> is used to make the creation of the font atomic with respect to asynchronous exceptions |
||
# <code>withCString</code> marshalls a Haskell <code>String</code> to a null terminated C string, and <code>newForeignPtr</code> takes the <code>Ptr RawFont</code> returned by <code>duma_createFont</code> and returns a <code>ForeignPtr RawFont</code> so that <code>duma_releaseFont</code> will be called on the underlying <code>Ptr RawFont</code> when the <code>ForeignPtr RawFont</code> value is no longer needed. Finally we wrap the <code>ForeignPtr RawFont</code> in a newtype to hide all this from the end user. |
# <code>withCString</code> marshalls a Haskell <code>String</code> to a null terminated C string, and <code>newForeignPtr</code> takes the <code>Ptr RawFont</code> returned by <code>duma_createFont</code> and returns a <code>ForeignPtr RawFont</code> so that <code>duma_releaseFont</code> will be called on the underlying <code>Ptr RawFont</code> when the <code>ForeignPtr RawFont</code> value is no longer needed. Finally we wrap the <code>ForeignPtr RawFont</code> in a newtype to hide all this from the end user. |
||
+ | |||
+ | ==Calling Haskell from C== |
||
+ | As mentioned above, when building a standalone C library, we can't link our C code to the C functions like <code>hs_free_fun_ptr</code>. However we can solve this problem by passing a <code>FunPtr</code> to the Haskell function <code>freeHaskellFunPtr</code> to our library at runtime. |
||
+ | |||
+ | In the following code, we provide a Haskell function to try to initialize our C library (eg the DLL), run some Haskell code which would set up some call backs, enter some kind of message loop (eg for a Windows app), then deinitialize the C library: |
||
+ | module Duma |
||
+ | ( run |
||
+ | , module Duma.Font |
||
+ | ) where |
||
+ | |||
+ | import Foreign.Ptr |
||
+ | import Foreign.ForeignPtr |
||
+ | import Control.Exception (bracket) |
||
+ | import Duma.Font |
||
+ | |||
+ | foreign import ccall duma_begin :: FunPtr (FunPtr a -> IO ()) -> IO Bool |
||
+ | foreign import ccall duma_end :: IO () |
||
+ | foreign import ccall duma_run :: IO () -- implements a Windows message loop |
||
+ | |||
+ | run :: IO a -> IO () |
||
+ | run f = bracket |
||
+ | (mkFreeFunPtr freeHaskellFunPtr |
||
+ | ) |
||
+ | (\freeFunPtrFn -> do |
||
+ | duma_end |
||
+ | freeHaskellFunPtr freeFunPtrFn |
||
+ | ) |
||
+ | (\freeFunPtrFn -> do |
||
+ | initialized <- duma_begin freeFunPtrFn |
||
+ | if (initialized) |
||
+ | then f >> duma_run |
||
+ | else return () |
||
+ | ) |
||
+ | |||
+ | An example of C code would be: |
||
+ | |||
+ | typedef void (*FunPtrFn)(HsFunPtr fn); |
||
+ | FunPtrFn freeFunPtrFn = NULL; |
||
+ | |||
+ | __declspec(dllexport) HsBool duma_begin(HsFunPtr freeFunPtrFnRaw){ |
||
+ | freeFunPtrFn = reinterpret_cast<FunPtrFn>(freeFunPtrFnRaw); |
||
+ | return HS_BOOL_TRUE; |
||
+ | } |
||
+ | |||
+ | Then within some C function which needs to release a FunPtr, you can just write: |
||
+ | (*freeFunPtrFn)(the_fun_ptr_to_free); |
||
+ | |||
+ | Note that it is not safe to use the <code>freeFunPtrFn</code> to free itself, because some implementations of <code>FunPtr</code> store exit code (as well as entry code) in the <code>FunPtr</code> thus a <code>FunPtr</code> needs to be thought of as a function ''holder'' rather than just a function pointer (this is why the definition of <code>run</code> above frees the pointer from the Haskell side once we've already deinitialized the C library). |
||
+ | |||
+ | A scavenger pattern ([http://www.haskell.org//pipermail/glasgow-haskell-users/2006-March/009907.html]) can be used to avoid the danger of a <code>FunPtr</code> being freed while the function it points to (actually ''holds'') is still being executed. |
Revision as of 14:36, 15 April 2006
Preliminary note
The examples below show how to call C++ functions in a Microsoft Windows Visual Studio DLL from Haskell, and Haskell functions from the C++ DLL. This illustrates that it is possible to link Haskell built with GHC to code produced by a different C compiler (GHC uses GCC), and also illustrates some workarounds to the specific problems encountered when building a standalone library (in this case a Windows DLL) that is to be called from Haskell. The examples are also relevant for the simpler case where you just want to statically link against C that is compiled with GCC.
Setting up your build environment (Visual Studio Specific)
- Create a directory eg
c:\dll
to store all your DLLs - Add this to the
PATH
environment variable so that Windows will find your DLL at runtime - Create a new Visual Studio DLL project. For the purposes of this example, we will call this
Duma
. - In the Solution pane, right click on
Duma
then select Properties → Build Events → Post-Build Event and add the following event:
Command Line: copy "$(TargetDir)Duma.dll" c:\dll Description: Copying duma.dll Excluded From Build: No
- In the Solution Pane, rename the folder
Source Files
toCode Files
and adjust the properties of this folder so that.h
files are also filtered into it. (This is a good idea for any C++ project because it keeps the.cpp
and.h
together in the same list which makes editing a C++ unit a lot easier.) - Also rename the folder
Header Files
toHaskell Files
and change its filter to select.hs
files. - In the
Code Files
folder, create a text file calledDuma.def
, then select Properties → Custom Build Step → General and enter the following:
Command Line: C:\ghc\ghc-6.4\gcc-lib\dlltool -d "$(InputDir)Duma.def" -l c:\dll\libDuma.a Description: Creating libDuma.a from .def Outputs: c:\dll\libDuma.a Additional Dependencies:
This makes the functions you will export from your DLL visible to GHC and GCC. (The path to dlltool given above will need to be changed if you have installed GHC in a different place.)
- Also in the
Code Files
folder, create a text file calledghc.bat
with the following content:
echo off REM Optimized build C:\ghc\ghc-6.4\bin\ghc.exe -fglasgow-exts -fffi -I. -#include Duma.h --make main.hs -O2 -optl-lDuma -optl-L"c:\dll" if errorlevel 1 goto failed echo Success :-) goto end :failed echo Failure :-( :end
(The above batch file is based on that kindly supplied by Neil Mitchell on the GHC mailing list.)
You may also want to use -optl-mwindows
if you don't want Haskell to create a console window.
- In Tools → External Tools, click
Add
and specify the following:
Title: &ghc Command: ghc.bat Arguments: Initial directory: $(ProjectDir)
Remember to tick the Use Output Window
box so you can see what's happening on the output pane of Visual Studio.
After you've done all this, you can build your DLL as usual by hitting Control-Shift-B
and then make your Haskell program that uses the DLL by using Alt-t-g
(assuming that your main.hs
is in the same directory as the C files). When you just make changes to your DLL, you can use F5
as normal (the first time you do this you will have to type main.exe
into the box where it asks you for the main executable to use).
Including the FFI header
The typedefs needed by foreign C functions are in HsFFI.h
. However, this file includes various other files, which in turn include other files, and makes use of various definitions which would need to be defined somewhere etc, and doesn't compile under Visual C++ (for example).
All in all, the simplest solution is to just make your own header with just the typedefs that are actually needed, and put this in your project directory, for example as FFI.h
:
#ifndef FFI_H #define FFI_H
typedef unsigned int HsChar; // on 32 bit machine typedef int HsInt; typedef unsigned int HsWord;
typedef void *HsPtr; typedef void (*HsFunPtr)(void); typedef void *HsForeignPtr; typedef void *HsStablePtr;
#define HS_BOOL_FALSE 0 #define HS_BOOL_TRUE 1 #endif // FFI_H
Note that there is no point including prototypes for the functions hs_free_fun_ptr
etc, because there is no way to link a standalone C library (such as a DLL) to them. However this is not a problem because your Haskell code can explicitly pass the Haskell versions of these functions (wrapped in a FunPtr
) to your C library if you need them.
Example of aquiring and using a foreign resource
Suppose you are writing a DLL to provide a nice windowing environment for Haskell, and you want to let Haskell obtain a text font. Suppose a font is implemented by the C++ class Font, defined by:
// Duma_Font.h namespace Duma { class Font { public: Font(const char *name) : refcount(0){/* details omitted! */} ~Font(){}
void AddRef() const{++refcount;} void Release() const{if (!--refcount) delete this;}
private: mutable unsigned int refcount; }; } // Duma
Continuing with our example DLL called Duma
, we now edit Duma.h
to add the following prototypes:
// Duma.h HsPtr duma_createFont(const char *name); void duma_releaseFont(HsPtr fontRaw);
We need to tell GHC about these, so we edit Duma.def
:
LIBRARY Duma EXPORTS duma_createFont duma_releaseFont
Then we implement them in Duma.cpp
:
// Duma.cpp #include "stdafx.h" #include "FFI.h" #include "Duma_Font.h"
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ){ // Note this is a *very* dangerous function so do nothing at all in here // It is not known which DLLs are currently loaded // If you need to do initialization/deinitialization, you should do // this explicitly by exporting init/deinit functions to be called from // Haskell return TRUE; }
using namespace Duma;
#ifdef __cplusplus extern "C" { #endif
__declspec(dllexport) HsPtr duma_createFont(const char *name){ Font *fontRaw = new Font(name); fontRaw->AddRef(); return fontRaw; }
__declspec(dllexport) void duma_releaseFont(HsPtr fontRaw){ reinterpret_cast<Font *>(fontRaw)->Release(); }
#ifdef __cplusplus } #endif
Note that we do not put #include "Duma.h"
in this file since this header is just for the benefit of GHC, and if we did include it in Duma.cpp
(or anywhere else in our DLL) we would get compilation errors from Visual Studio "redefinition: different linkage". We can't specify the linkage in Duma.h
because __declspec(dllexport)
is Microsoft specific...
The next thing is to create a Haskell module which will allow us to treat fonts as values which are automatically released when they are no longer needed, so for this we edit a module eg Duma.Font
by creating a directory called Duma
in the same directory as your C++ files, then creating a file Font.hs
in this directory:
module Duma.Font ( createFont , Font ) where
import Foreign.C.String import Foreign.Ptr import Foreign.ForeignPtr import Control.Exception (block)
data RawFont -- corresponds to the C++ Font newtype Font = Font (ForeignPtr RawFont) deriving (Eq, Ord)
foreign import ccall duma_createFont :: CString -> IO (Ptr RawFont) foreign import ccall "&duma_releaseFont" -- note the ampersand duma_releaseFont :: FunPtr (Ptr RawFont -> IO ())
createFont :: String -> IO Font createFont name = block $ do f <- (withCString name $ \cname -> duma_createFont cname) >>= newForeignPtr duma_releaseFont return $ Font f
There are several points to note about this code:
- We are using three types of pointers:
Ptr
- Corresponds to a plain C pointer
FunPtr
- Points to a Haskell or (in this case) C function, and encapsulates marshalling details necessary both before entry and after leaving the function. For this reason a
FunPtr
must never be destroyed while the function it points to is still being executed ForeignPtr
- Associates a plain C pointer with a finalizer function which will be invoked when the ForeignPtr value is garbage collected
- It is important to realise that the type declaration of a foreign function shows the type of the Haskell value, not the type of the corresponding C value that the Haskell value is bound to. Thus the declaration for
duma_releaseFont
specifies a Haskell value which is a pointer to a function which takes a pointer to a font, and the ampersand tells the FFI that the Haskell valueduma_releaseFont
is to be bound to the address of the C valueduma_releaseFont
ie the address of the C functionduma_releaseFont
not the function itself. It is worth taking the time to achieve clarity about the difference between a function and a pointer to a function, especially since this distinction is blurred in C by the implicit casting of a function name (which is not a first class value in C) to the address of a function. Luckily, if you are building with an optimized build, or use the-fviaC
option, GCC will give an error message if you forget the ampersand, but beware: GHC native compilation will silently succeed and your app will mysteriously crash. block
is used to make the creation of the font atomic with respect to asynchronous exceptionswithCString
marshalls a HaskellString
to a null terminated C string, andnewForeignPtr
takes thePtr RawFont
returned byduma_createFont
and returns aForeignPtr RawFont
so thatduma_releaseFont
will be called on the underlyingPtr RawFont
when theForeignPtr RawFont
value is no longer needed. Finally we wrap theForeignPtr RawFont
in a newtype to hide all this from the end user.
Calling Haskell from C
As mentioned above, when building a standalone C library, we can't link our C code to the C functions like hs_free_fun_ptr
. However we can solve this problem by passing a FunPtr
to the Haskell function freeHaskellFunPtr
to our library at runtime.
In the following code, we provide a Haskell function to try to initialize our C library (eg the DLL), run some Haskell code which would set up some call backs, enter some kind of message loop (eg for a Windows app), then deinitialize the C library:
module Duma ( run , module Duma.Font ) where
import Foreign.Ptr import Foreign.ForeignPtr import Control.Exception (bracket) import Duma.Font
foreign import ccall duma_begin :: FunPtr (FunPtr a -> IO ()) -> IO Bool foreign import ccall duma_end :: IO () foreign import ccall duma_run :: IO () -- implements a Windows message loop
run :: IO a -> IO () run f = bracket (mkFreeFunPtr freeHaskellFunPtr ) (\freeFunPtrFn -> do duma_end freeHaskellFunPtr freeFunPtrFn ) (\freeFunPtrFn -> do initialized <- duma_begin freeFunPtrFn if (initialized) then f >> duma_run else return () )
An example of C code would be:
typedef void (*FunPtrFn)(HsFunPtr fn); FunPtrFn freeFunPtrFn = NULL;
__declspec(dllexport) HsBool duma_begin(HsFunPtr freeFunPtrFnRaw){ freeFunPtrFn = reinterpret_cast<FunPtrFn>(freeFunPtrFnRaw); return HS_BOOL_TRUE; }
Then within some C function which needs to release a FunPtr, you can just write:
(*freeFunPtrFn)(the_fun_ptr_to_free);
Note that it is not safe to use the freeFunPtrFn
to free itself, because some implementations of FunPtr
store exit code (as well as entry code) in the FunPtr
thus a FunPtr
needs to be thought of as a function holder rather than just a function pointer (this is why the definition of run
above frees the pointer from the Haskell side once we've already deinitialized the C library).
A scavenger pattern ([1]) can be used to avoid the danger of a FunPtr
being freed while the function it points to (actually holds) is still being executed.