Search code examples
chaskellc-preprocessorpetscc2hs

do..while(0) function-like C macro wrapper in c2hs


I'd like to wrap a function-like C macro in a C function (and in turn wrap it in Haskell with a {#fun ... #} block), but the c2hs preprocessor chokes on the do.. while(0) syntax; here's the code:

module TestMacro where
#c

#define TestF1(n) do{if n==0 return 0;else return 1; } while(0)

int c_testF1(int x)
{ return ( TestF1(x) ); }

#endc

and here's the error:

c2hs TestMacro.chs
c2hs: C header contains errors:

TestMacro.chs.h:6: (column 12) [ERROR]  >>> Syntax error !
  The symbol `do' does not fit here.

make: *** [main] Error 1

What am I doing wrong? My goal is wrapping the CHKERRQ macro of the PETSc library, defined as follows in petscerror.h(split onto multiple lines for readability):

#define CHKERRQ(n)             
    do {if (PetscUnlikely(n)) 
        return PetscError(PETSC_COMM_SELF,__LINE__,PETSC_FUNCTION_NAME,__FILE__,n,PETSC_ERROR_REPEAT," ");} 
    while (0)

Solution

  • Remember that #define is text replacement. So

    { return ( TestF1(c) ); }
    

    becomes

    { return ( do{if c==0 return 0;else return 1; } while(0) ); }
    

    and you can neither use do { .. } while() nor other return statements as return parameters (and the brackets around the ìf - condition are missing). For your macro to work at that place it could be simply defined as

    #define TestF1(n)    ((n)==0 ? 0 : 1)
    

    Edit

    A function that uses CHKERRQ could look like this

    int my_chkerrq( int n )
    {
         CHKERRQ(n);
         return( whatever_you_want_to_return_here );
    }
    

    but I would suggest to directly call what CHKERRQ() calls:

    int my_chkerrq( int n )
    {
         if (PetscUnlikely(n)) 
             return( PetscError(PETSC_COMM_SELF,__LINE__,PETSC_FUNCTION_NAME,__FILE__,n,PETSC_ERROR_REPEAT," ") );
         else 
             return( whatever_you_want_to_return_here );
    }
    

    Of course, in both cases __LINE__ and __FILE__ are replaced by those of the C code and may not be very useful in the Haskell environment