Date: Tue, 15 Sep 2026 12:13:14 -0700
An example such as this seems to be handled badly by the current rules in C:
const char str[] = {
#embed "/maybe/empty/file" if_empty("error: \"resource file empty!\"")
};
In particular, 6.4.1/4 says:
> There is one exception to this rule: header name preprocessing tokens are
recognized only within #include and #embed preprocessing directives, in
__has_include and __has_embed expressions, as well as in
implementation-defined locations within #pragma directives. In such
contexts, a sequence of characters that could be either a header name or a
string literal is recognized as the former.
So the first preprocessing token in the `if_empty` ends at the first `\"`,
forms a header name, and the program is invalid if the given file is empty
because it tries to convert a header name preprocessing token to a token
(violation of constraint in 6.4.1/2).
I don't think this was intended. C++ has a different rule that doesn't have
this problem. In its [lex.pptoken]/5:
"""
[...C++-specific deviations from max-munch...]
Otherwise, the next preprocessing token is the longest sequence of
characters that could constitute a preprocessing token, even if that would
cause further lexical analysis to fail, except that
-- a string-literal token is never formed when a header-name token can be
formed, and
-- a header-name ([lex.header]) is only formed
-- immediately after the include or embed preprocessing token in a
#include ([cpp.include]) or #embed ([cpp.embed]) directive, respectively, or
-- immediately after an import preprocessing token that is at the start
of a logical source line, or
-- immediately after a preprocessing token sequence of __has_include or
__has_embed immediately followed by ( in a #if, #elif, or #embed directive
([cpp.cond], [cpp.embed]).
A preprocessing token is considered to be immediately after another
preprocessing token if the preprocessing tokens are on the same logical
source line and there are no intervening preprocessing tokens.
"""
The "import preprocessing token" part is for C++ modules, but I think the
rest would be directly applicable to C. Perhaps a similar rule should be
adopted, at least for #embed and __has_embed (though ideally for includes
as well)?
Note that this would affect the validity and meaning of cases where there
is an empty macro expansion between the directive-introducing token and the
name of the header:
#define EMPTY
// Works in C++, not in C.
#include EMPTY "foo\"bar.h"
// Works in C, not in C++.
#include EMPTY "baz\quux.h"
Other fun cases include:
#define MAKE_HEADER(prefix, suffix) prefix/bar/suffix
#include MAKE_HEADER(<foo,baz.h>)
... which the C++ rules map into an inclusion of the reconstituted
<foo/bar/baz.h>, whereas the C rules reject because the MAKE_HEADER macro
was not given enough arguments.
For what it's worth: the GCC preprocessor appears to implement the C++ rule
in all languages (for both #embed and #include), and has done at least all
the way back to version 3; the Clang preprocessor implements the C rule in
all languages up until it reaches the end of the filename for `#embed`,
then switches to a "no header-names" mode. I did not find any
implementation of #embed that follows the C rule as written.
const char str[] = {
#embed "/maybe/empty/file" if_empty("error: \"resource file empty!\"")
};
In particular, 6.4.1/4 says:
> There is one exception to this rule: header name preprocessing tokens are
recognized only within #include and #embed preprocessing directives, in
__has_include and __has_embed expressions, as well as in
implementation-defined locations within #pragma directives. In such
contexts, a sequence of characters that could be either a header name or a
string literal is recognized as the former.
So the first preprocessing token in the `if_empty` ends at the first `\"`,
forms a header name, and the program is invalid if the given file is empty
because it tries to convert a header name preprocessing token to a token
(violation of constraint in 6.4.1/2).
I don't think this was intended. C++ has a different rule that doesn't have
this problem. In its [lex.pptoken]/5:
"""
[...C++-specific deviations from max-munch...]
Otherwise, the next preprocessing token is the longest sequence of
characters that could constitute a preprocessing token, even if that would
cause further lexical analysis to fail, except that
-- a string-literal token is never formed when a header-name token can be
formed, and
-- a header-name ([lex.header]) is only formed
-- immediately after the include or embed preprocessing token in a
#include ([cpp.include]) or #embed ([cpp.embed]) directive, respectively, or
-- immediately after an import preprocessing token that is at the start
of a logical source line, or
-- immediately after a preprocessing token sequence of __has_include or
__has_embed immediately followed by ( in a #if, #elif, or #embed directive
([cpp.cond], [cpp.embed]).
A preprocessing token is considered to be immediately after another
preprocessing token if the preprocessing tokens are on the same logical
source line and there are no intervening preprocessing tokens.
"""
The "import preprocessing token" part is for C++ modules, but I think the
rest would be directly applicable to C. Perhaps a similar rule should be
adopted, at least for #embed and __has_embed (though ideally for includes
as well)?
Note that this would affect the validity and meaning of cases where there
is an empty macro expansion between the directive-introducing token and the
name of the header:
#define EMPTY
// Works in C++, not in C.
#include EMPTY "foo\"bar.h"
// Works in C, not in C++.
#include EMPTY "baz\quux.h"
Other fun cases include:
#define MAKE_HEADER(prefix, suffix) prefix/bar/suffix
#include MAKE_HEADER(<foo,baz.h>)
... which the C++ rules map into an inclusion of the reconstituted
<foo/bar/baz.h>, whereas the C rules reject because the MAKE_HEADER macro
was not given enough arguments.
For what it's worth: the GCC preprocessor appears to implement the C++ rule
in all languages (for both #embed and #include), and has done at least all
the way back to version 3; the Clang preprocessor implements the C rule in
all languages up until it reaches the end of the filename for `#embed`,
then switches to a "no header-names" mode. I did not find any
implementation of #embed that follows the C rule as written.
Received on 2026-09-15 19:13:28
