9

I am trying to build freetype2 using my own build system (I do not want to use Jam, and I am prepared to put the time into figuring it out). I found something odd in the headers. Freetype defines macros like this:

#define FT_CID_H  <freetype/ftcid.h>

and then uses them later like this:

#include FT_CID_H 

I didn't think that this was possible, and indeed Clang 3.9.1 complains:

error: expected "FILENAME" or <FILENAME>
#include FT_CID_H
  • What is the rationale behind these macros?
  • Is this valid C/C++?
  • How can I convince Clang to parse these headers?

This is related to How to use a macro in an #include directive? but different because the question here is about compiling freetype, not writing new code.

Community
  • 1
  • 1
sdgfsdh
  • 24,047
  • 15
  • 89
  • 182
  • That is an unusual way to do things - normally the `#define` would be used with `#ifdef` to conditionally include different header files. – Chris Turner Mar 17 '17 at 12:53
  • Possible duplicate of [How to use a macro in an #include directive?](http://stackoverflow.com/a/40063340/27678) – AndyG Mar 17 '17 at 12:54
  • So far as I can see, this is valid (at least in C99 - see paragraph 4 of section 6.10.2). – Oliver Charlesworth Mar 17 '17 at 12:55
  • 4
    Cannot reproduce: https://godbolt.org/g/2DiIL0 –  Mar 17 '17 at 12:55
  • Take a look at: http://stackoverflow.com/questions/32066204/construct-path-for-include-directive-with-macro What you are trying to "should" be possible, but it is pretty bad practice. (I am not 100% sure on what standard says, but I am guessing it is implementation dependent, based upon the order that the preprocessor reads it.) – Chris Britt Mar 17 '17 at 12:55
  • 1
    CLang 3.5.0 does not complain, GCC 4.9.2 neither. – alk Mar 17 '17 at 13:08
  • 3
    @ChrisBritt It's actually perfectly legal and well-defined in the standard, not implementation-dependent. – Angew is no longer proud of SO Mar 17 '17 at 13:09
  • 2
    My bet goes on a single standalone `#include`, probably due to a typo or another missing `#include`. You want to look a the pre-processor's output. – alk Mar 17 '17 at 13:13
  • My bet goes on: did you include the header that define FT_CID_H before using it? – LPs Mar 17 '17 at 13:17
  • This isn't an answer, but you may also be interested in using __has_include since it sounds like you are trying to include headers based on their availability. – Trevor Hickey Mar 18 '17 at 15:05

2 Answers2

11

I will address your three questions out of order.

Question 2

Is this valid C/C++?

Yes, this is indeed valid. Macro expansion can be used to produce the final version of a #include directive. Quoting C++14 (N4140) [cpp.include] 16.2/4:

A preprocessing directive of the form

# include pp-tokens new-line

(that does not match one of the two previous forms) is permitted. The preprocessing tokens after include in the directive are processed just as in normal text (i.e., each identifier currently defined as a macro name is replaced by its replacement list of preprocessing tokens). If the directive resulting after all replacements does not match one of the two previous forms, the behavior is undefined.

The "previous forms" mentioned are #include "..." and #include <...>. So yes, it is legal to use a macro which expands to the header/file to include.

Question 1

What is the rationale behind these macros?

I have no idea, as I've never used the freetype2 library. That would be a question best answered by its support channels or community.

Question 3

How can I convince Clang to parse these headers?

Since this is legal C++, you shouldn't have to do anything. Indeed, user @Fanael has demonstrated that Clang is capable of parsing such code. There must be some problem other problem in your setup or something else you haven't shown.

Angew is no longer proud of SO
  • 156,801
  • 13
  • 318
  • 412
6

Is this valid C/C++?

The usage is valid C, provided that the macro definition is in scope at the point where the #include directive appears. Specifically, paragraph 6.10.2/4 of C11 says

A preprocessing directive of the form

# include pp-tokens new-line

(that does not match one of the two previous forms) is permitted. The preprocessing tokens after include in the directive are processed just as in normal text. (Each identifier currently defined as a macro name is replaced by its replacement list of preprocessing tokens.) The directive resulting after all replacements shall match one of the two previous forms.

(Emphasis added.) Inasmuch as the preprocessor has the same semantics in C++ as in C, to the best of my knowledge, the usage is also valid in C++.

What is the rationale behind these macros?

I presume it is intended to provide for indirection of the header name or location (by providing alternative definitions of the macro).

How can I convince Clang to parse these headers?

Provided, again, that the macro definition is in scope at the point where the #include directive appears, you shouldn't have to do anything. If indeed it is, then Clang is buggy in this regard. In that case, after filing a bug report (if this issue is not already known), you probably need to expand the troublesome macro references manually.

But before you do that, be sure that the macro definitions really are in scope. In particular, they may be guarded by conditional compilation directives -- in that case, the best course of action would probably be to provide whatever macro definition is needed (via the compiler command line) to satisfy the condition. If you are expected to do this manually, then surely the build documentation discusses it. Read the build instructions.

John Bollinger
  • 121,924
  • 8
  • 64
  • 118
  • 2
    Thanks for the answer, it led me to a solution. It turns out the include path macros need to be defined at the command-line. These usually are generated by flags passed to make, so I simply needed to copy the flags from the `Makefile`. What a needlessly crazy build system! – sdgfsdh Mar 17 '17 at 14:36
  • 2
    Just an extra note for people trying to build freetype2: not every file *should* be compiled (e.g. `aflatin2.c`) and not every header actually gets included (e.g. `ftconfig.h`)! – sdgfsdh Mar 17 '17 at 14:38