3

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)
ocramz
  • 816
  • 6
  • 18
  • 2
    The definition of `CHKERRQ` misses the `\\` at its line-ends. – alk Apr 23 '15 at 17:31
  • 2
    I feel coding a `return` statement in a marco makes the code using it unreadable and with this unmaintainable, ugly, ... - all in all a bad thing. – alk Apr 23 '15 at 17:34
  • CHKERRQ is a logger utility in a large library, can't do much about it .. – ocramz Apr 23 '15 at 17:53
  • As it stands CHKERRQ wont't "compile". – alk Apr 23 '15 at 18:01
  • It does not compile within this small example, no. But it certainly does when the PETSc headers are linked. But perhaps I haven't understood your remark..? – ocramz Apr 24 '15 at 00:49
  • "Multiline"-macro need to carry a trailing \ at every line's end (but the last). Those \ are mising in the code you show. – alk Apr 24 '15 at 07:34

2 Answers2

9

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

Ingo Leonhardt
  • 9,435
  • 2
  • 24
  • 33
  • 4
    ...with emphasis on the fact that the original version's `return` *does not return from the macro*, it jumps out of the function containing the macro; the second one (with `?:`) is the one to use in general for a macro that evaluates to a value. (the definition of `TestF1` makes it look like this is a point of confusion here) – Alex Celeste Apr 23 '15 at 17:47
5
{ return ( TestF1(c) ); }

The syntax of return requires an (optional) expression: you cannot use a statement instead of the expression. The do {} while (0) is a statement.

(C11, 6.8.6 Jump statements)

Syntax

return expression_opt ;

What you can do is to use instead:

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

As I put in the comments this would work but I won't advise to do that, it is bad coding style. The ?: could be used in the example TestF1 macro (as written in another answer) but it cannot be used for your CHKERRQ real use case (you can use a macro for the PetscError function call though).

Community
  • 1
  • 1
ouah
  • 142,963
  • 15
  • 272
  • 331