Reputation: 326
I've run into a somewhat unexpected behavior in Xcode/Objective-C. I know it's probably not advised, but if I want to make my own struct in_addr
in a .m file, it seems I can't. This implies something rather strange about namespaces and symbol pollution in Objective-C. The same seems to apply for many other networking types and perhaps other POSIX-y things as well.
I came up with a very basic example that demonstrates this behavior. Note that this snippet is the entire contents of the .m file.
#define _SYS_SOCKET_H_
#define _NETINET_IN_H_
#include <stdint.h>
struct in_addr {
uint32_t foo;
};
which yields the build error Redefinition of 'in_addr'
.
This implies some fairly strange things about Objective-C. For starters, I wouldn't expect <stdint.h>
to bring in any networking types. But even allowing that it might, defining _NETINET_IN_H_
first should prevent the definition of struct in_addr
. And yet even still, this code refuses to build.
Is it possible to somehow forgo this forced symbol visibility? Is there a list of symbols that are included, no matter what? Is there a good reason for this behavior?
edit: Stranger still, if i remove <stdint.h>
and change the uint32_t
to int
, this actually does compile.
Upvotes: 3
Views: 224
Reputation: 17040
If you go into the Report navigator and read the full error emitted by the clang
tool, you'll see a big hint:
In module 'Darwin' imported from /Users/csrstka/Desktop/asdfasdf/asdfasdf/main.m:1:
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk/usr/include/netinet/in.h:302:12: note: field has name 's_addr' here
in_addr_t s_addr;
As you can see, the existing in_addr
is coming from the Darwin
module, which is implicitly imported due to your #include
of stdint.h
, which is part of the Darwin
module. You can see this if you go to Product > Perform Action > Preprocess in Xcode—instead of copying in all the headers you've imported, there's just one line about importing Darwin.C.stdint
.
Basically, there are a few purposes for modules; they improve compile times by cutting down on redundant compilation tasks, and they prevent people from messing with library headers via #define
s like you're trying to do. ;-) For more on Objective-C modules, how they work, and the rationale behind them, see this link:
https://clang.llvm.org/docs/Modules.html#introduction
Of particular interest to your question are the following excerpts:
The primary user-level feature of modules is the import operation, which provides access to the API of software libraries. However, today’s programs make extensive use of #include, and it is unrealistic to assume that all of this code will change overnight. Instead, modules automatically translate #include directives into the corresponding module import. For example, the include directive
#include <stdio.h>
will be automatically mapped to an import of the module std.io. Even with specific import syntax in the language, this particular feature is important for both adoption and backward compatibility: automatic translation of #include to import allows an application to get the benefits of modules (for all modules-enabled libraries) without any changes to the application itself. Thus, users can easily use modules with one compiler while falling back to the preprocessor-inclusion mechanism with other compilers.
And later on:
If any submodule of a module is imported into any part of a program, the entire top-level module is considered to be part of the program. As a consequence of this, Clang may diagnose conflicts between an entity declared in an unimported submodule and an entity declared in the current translation unit, and Clang may inline or devirtualize based on knowledge from unimported submodules.
Or, if you'd prefer to turn them off and get more traditional C-like behavior, you can simply set Enable Modules (C and Objective-C)
to No in Xcode's Build Settings, or compile without the -fmodules
flag if you're using the command line.
Upvotes: 2