Skip to content

Fix build where major() and minor() are macros - #338

Open
guicybercode wants to merge 1 commit into
sdatkinson:mainfrom
guicybercode:fix/304-sys-types-macro-collision
Open

guicybercode wants to merge 1 commit into
sdatkinson:mainfrom
guicybercode:fix/304-sys-types-macro-collision

Conversation

@guicybercode

Copy link
Copy Markdown

Fixes #304.

The problem

<sys/types.h> on FreeBSD and <sys/sysmacros.h> on glibc define major() and minor() as function-like macros, and unlike Apple's headers they stay macros in C++. nam::Version's member-initializer list read major(major), which expanded, so NAM/get_dsp.h failed to compile for any consumer that included one of those headers first:

NAM/get_dsp.h:30:5: error: expected class member or base class name
   30 |   : major(major)
      |     ^
note: expanded from macro 'major'

The fix

Brace-init the members. A function-like macro only expands when the identifier is followed by (, so major{major} is left alone, while int major;, parsed.major > latest.major and std::to_string(major) never expanded in the first place. Grepping NAM/ and tools/ for major *( / minor *( confirms lines 30-31 were the only two expansion sites in the tree, which is why this is a two-line change.

The public major/minor/patch members keep their names, so this is not an API break and no consumer needs to change.

@DeimosLabs suggested renaming the members to major_version/minor_version instead. That works too, but it breaks the public API and would need the eight sites in tools/test/test_get_dsp.cpp:293-363 that mutate .minor/.patch updated. Happy to switch to the rename if you would rather have the more obvious spelling — just say the word.

The test

tools/test/test_get_dsp_sys_types.cpp defines the two macros itself rather than including <sys/types.h>. That matters: Apple's header deliberately uses inline functions instead of macros in C++ (see the comment in sys/types.h), so including it would make the test a no-op on macOS. Defining them directly reproduces the FreeBSD/glibc preprocessor state on every platform, including in CI.

Two details worth flagging for review:

  • The test is included first in run_tests.cpp. If an earlier test had already pulled in NAM/get_dsp.h, #pragma once would make the include a no-op and the test would prove nothing.
  • The macros are defined after get_dsp.h's dependency closure and #undef'd at the end. Expanding them inside libc++'s own headers is a different, unrelated failure, and leaving them defined would leak into every test included afterwards.

Validation

On macOS / AppleClang, Debug build:

  • Against the unmodified header the new test fails to compile with exactly the error above; with the fix it passes. (Verified by stashing only NAM/get_dsp.h and rebuilding.)
  • run_tests — all pass.
  • benchmodel example_models/wavenet.nam, benchmodel example_models/lstm.nam, and render on example_audio/input.wav — all fine.

I do not have a FreeBSD box, so the FreeBSD build itself is unverified; the macro simulation is what I could test directly.

FreeBSD's <sys/types.h> and glibc's <sys/sysmacros.h> define major() and
minor() as function-like macros and, unlike Apple's headers, keep them as
macros in C++. nam::Version's member-initializer list read `major(major)`,
which expanded, so NAM/get_dsp.h failed to compile for any consumer that
included one of those headers first:

    NAM/get_dsp.h:30:5: error: expected class member or base class name

Brace-init the members instead. A function-like macro only expands when the
identifier is followed by `(`, so `major{major}` is left alone; `int major;`,
`parsed.major` and `std::to_string(major)` never expanded to begin with, which
makes lines 30-31 the only two expansion sites in the tree. The public members
keep their names, so this is not an API break.

The regression test defines the two macros itself rather than including
<sys/types.h>, so it reproduces the FreeBSD/glibc preprocessor state on every
platform, and is included first in run_tests.cpp so that NAM/get_dsp.h has not
already been parsed by an earlier test.

Validated on macOS/AppleClang: the new test fails to compile against the
unmodified header with the error above, and the full run_tests suite, both
benchmodel models and render all pass with the fix.

Fixes sdatkinson#304
Copilot AI lite review requested due to automatic review settings September 20, 2026 16:22

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@DeimosLabs

Copy link
Copy Markdown

The "fix" of { } init members is more of a workaround than an actual fix... the real fix would be to force apple to do things right in the first place.

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.

Build on FreeBSD: "minor" and "major" in get_dsp.h cause build error

3 participants