Skip to content

Do not assume the PRI macros are still available - #191

Merged
sheredom merged 2 commits into
sheredom:mainfrom
bernardladenthin:harden-format-macros
Aug 19, 2026
Merged

Do not assume the PRI macros are still available#191
sheredom merged 2 commits into
sheredom:mainfrom
bernardladenthin:harden-format-macros

Conversation

@bernardladenthin

Copy link
Copy Markdown
Contributor

__STDC_FORMAT_MACROS only has an effect when nothing has included
<inttypes.h> yet. NetBSD reaches the PRI macros through <sys/inttypes.h>:

#ifndef _SYS_INTTYPES_H_
#define _SYS_INTTYPES_H_
#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) || (__cplusplus >= 201103L)
#include <machine/int_fmtio.h>
#endif
#endif

Behind an include guard, so whichever header gets there first settles it and
the define here arrives too late. <sys/param.h> includes it, and so does
anything else that reaches <inttypes.h> — a consumer including its own
headers before utest.h is enough.

That is not hypothetical. It surfaced in sheredom/subprocess.h#113 on NetBSD
9.4 and 10.1, where test98.cpp includes subprocess.h first:

utest.h:234:22: error: expected ')' before 'PRIu64'

What this does

Requires PRId64 and PRIu64 to actually be defined before using them, and
takes the existing double fallback otherwise — a capability test rather than
a proxy for one, so include order stops mattering.

test/inttypes_include.cpp covers it the way stdint_include.c covers the
stdint case: <inttypes.h> first, then utest.h, compiled as C++98.

Verified against a stub <inttypes.h> that exposes no PRI macros, which is
what NetBSD looks like in C++98:

utest.h result
this branch compiles
main error: expected ')' before 'PRIu64'

Second commit, unrelated but small

test20.cpp was compiled with -std=c++20 unconditionally. GCC 8 and clang 9
only accept -std=c++2a, and GCC 7 and older accept neither, so on those the
build stops before running anything — NetBSD 9.4 ships GCC 7.5.0. It now drops
the standard flag when neither spelling is available, which is what already
happens for test11.c without c_std_11 and on MSVC.

Happy to split that into its own PR if you would rather.

Not included

A portability matrix here, which is what found this. It needs the vendored
test/subprocess.h refreshed first — that copy predates
sheredom/subprocess.h#104, so main does not build against current glibc
without _GNU_SOURCE. Separate PR, if you want it.

__STDC_FORMAT_MACROS only has an effect when nothing has included
<inttypes.h> yet. NetBSD reaches the PRI macros through <sys/inttypes.h>,
which gates them on that macro for C++ before C++11 and sits behind an
include guard, so a header included earlier settles the question and the
define here arrives too late.

The condition now requires PRId64 and PRIu64 to actually be defined before
using them, and takes the double fallback otherwise. That is a capability
test rather than a proxy, and it does not depend on include order.

test/inttypes_include.cpp covers it the way stdint_include.c covers the
stdint case: <inttypes.h> first, then utest.h, compiled as C++98.
test20.cpp was compiled with -std=c++20 unconditionally. GCC 8 and clang 9
only accept the pre-release spelling -std=c++2a, and GCC 7 and older accept
neither, so on those compilers the build stops before running anything.

The file now drops the standard flag when neither spelling is available and
is compiled at the compiler default, which is what already happens for
test11.c without c_std_11 and on MSVC, where no -std flag is passed at all.
@sheredom
sheredom merged commit 59f3f13 into sheredom:main Aug 19, 2026
28 checks passed
@bernardladenthin
bernardladenthin deleted the harden-format-macros branch August 20, 2026 08:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants