3

My C library compiles and gets packaged into an xcframework without a problem, but my Swift project that uses this through a Swift Package then has some errors...

Namely, I'm getting

Token is not a valid binary operator in a preprocessor subexpression

This error shows up, for example, for the or in #if defined(__ANDROID__) or defined(__iOS__).

This header file has #include <iso646.h> (which defines or), and I tried #include "iso646.h" for if SWIFT_PACKAGE, but that didn't help. I also tried to hard-code the contents of this system header into my header, without luck.

Why is my Swift project choking on these?

Edit:

I tried a minimal reproducible example, and I get the same error message.

Here's the code:

Minimis.c:

#include "stdio.h"
void f(void) {
  printf("f called from within xcframework");
}

Minimis.h:

#import <Foundation/Foundation.h>

//! Project version number for Minimis.
FOUNDATION_EXPORT double MinimisVersionNumber;

//! Project version string for Minimis.
FOUNDATION_EXPORT const unsigned char MinimisVersionString[];

// In this header, you should import all the public headers of your framework using statements like #import <Minimis/PublicHeader.h>

#include "iso646.h"
#if A and B
    #pragma message ("A and B are defined")
#else
    #warning Neither A nor B are defined
#endif

void f(void);

Creating an xcframework and a Swift Package from that causes the failure on the #if A and B header line when trying to use the Swift Package in a Swift Project.

I tried modifying the module.modulemap file that gets included with my xcframework to include the iso646 header, like:

framework module Minimis {
  umbrella header "Minimis.h"

  export *
  module * { export * }
  
  link "iso646"
}

... but that doesn't resolve the error. I also tried to create a Swift Package Library that just wraps some C standard libraries, but then I didn't see how to include that in my other Swift Package (I'm using a binary target to pull in my xcframework, and the binaryTarget method doesn't take dependencies).

1
  • 1
    Does that message occur in your header or somebody else’s? Reduce the problem to a minimal reproducible example and edit the post to show that. Commented Jul 25, 2023 at 21:10

2 Answers 2

2
+100

After checking the content of <iso646.h>, I found that and is defined here.

And using some trick I found __ISO646_H was already defined in command line to deliberately prevent someone from using it.

The code is here and only Apple will know why.

https://github.com/apple/swift/blob/c251b3cf56ab107480a8fb1149a788aa101d29e2/lib/ClangImporter/ClangImporter.cpp#L556

Reply from pre-Apple employees https://forums.swift.org/t/including-c-system-headers-through-a-swift-package/66543/7

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you! This explains why this wasn't working, exactly what I needed to move on.
0

You can fix this by using && and || operators instead of and | or. The traditional logical && operator have a higher precedence than the the the more-human readable form or writing and.

So basically preprocessors works with replacing the macros with their values. In this case, and is treated as an identifier cause it can't find a macro named and and leads to the invalid binary operator error.

In your case, something else Kyle pointed out in the forum is that the swift compiler avoids including the iso646 header.

The solution to fix this issue would be to instead use:

#if defined(__ANDROID__) || defined(__iOS__)
// ... rest of the code

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.