Swift fails to compile enum with #include in C header

I'm using Swift to call some functions in a C library, but some error messages showed when building these C headers.

In my C header, enum type imports value through #include, but Swift compiler shows some syntax errors. Here's the code snippet:

In error.h

enum error_code {
    NOT_IMPL = -1,
    #define ERROR_CODE(name, val) name = val,
    #include "error_codes.h"  // it makes some error
}

In error_codes.h

#ifdef ERROR_CODE
   ERROR_CODE( OK,                         0 )
   ERROR_CODE( BAD_HANDLE,                 1 )
#undef ERROR_CODE
#endif

My current solution is to move the code from error_codes.h to error.h without using C's include, but it has to modify the code.

Is there any other solutions without modifying the code?

Swift doesn't handle complex macros in C. Your ERROR_CODE macro is a complex macro.

But it is weird. In my C header, there are still some complex macros can be compiled. This is another complex macros code in C, but they can be compiled.

typedef struct ion_type *ION_TYPE;
#define ION_TYPE_INT(x) ((intptr_t) (x))

#define tid_BOOL ((ION_TYPE) tid_BOOL_INT)
#define tid_INT ((ION_TYPE) tid_INT_INT)

#define tid_BOOL_INT 0x100
#define tid_INT_INT 0x200

Those are not complex. Those are just type aliases. What makes you original macro complex is the value assignment. Swift's handling of C macros does not seem to allow procedural code substitution, even if it's not really procedural (you're defining an enum). The Swift compiler does not operate the same as the C-preprocessor, it's not just a strict text substitution pass like it is in C.

If you include error.h from Swift, it should work fine. Swift doesn't have to deal with ERROR_CODE directly. What's the actual error you're getting?

(EDIT: I have a suspicion, but I don't want to go down the wrong path if it's not that.)

mainly some parse and semantic issues

In ion_error.h

in ion_error_codes.h

Hmm. Well, it could still be what I'm thinking of, though those are some obtuse errors. Swift expects C libraries to be modules in the Clang sense, with a module map that's generated by Xcode or SwiftPM. But you can also provide one explicitly, which will let you mark ion_error_codes.h as a textual header, rather than one that's meant to be included directly. That'll look something like this:

module Ion {
  umbrella header "ion.h"
  textual header "ion_error_codes.h"
  export *
  module * { export * }
}

(If your library is being built as a framework, you should use framework module Ion instead of just module Ion.)

1 Like

I'll take a look about the module map part. Thanks for your reply!