diff --git a/NAM/get_dsp.h b/NAM/get_dsp.h index 4783d71e..a49da706 100644 --- a/NAM/get_dsp.h +++ b/NAM/get_dsp.h @@ -26,10 +26,14 @@ class IVersionSupportChecker class Version { public: + // Brace-init the members rather than `major(major)`: (FreeBSD) + // and (glibc) define major() and minor() as function-like + // macros that stay macros in C++, and they expand on `major(`, breaking this + // header for any consumer that includes them first. `major{` does not expand. Version(int major, int minor, int patch) - : major(major) - , minor(minor) - , patch(patch) + : major{major} + , minor{minor} + , patch{patch} { } diff --git a/tools/run_tests.cpp b/tools/run_tests.cpp index ee827818..2895107d 100644 --- a/tools/run_tests.cpp +++ b/tools/run_tests.cpp @@ -2,6 +2,9 @@ // See the GitHub Action for a demo of how to build and run tests. #include +// Must come first: it checks that NAM/get_dsp.h parses while major()/minor() are +// macros, which only means anything if no earlier test has already included it. +#include "test/test_get_dsp_sys_types.cpp" #include "test/test_activations.cpp" #include "test/test_activations_realtime_safe.cpp" #include "test/test_conv1d.cpp" @@ -44,6 +47,7 @@ int main() { std::cout << "Running tests..." << std::endl; + test_get_dsp_sys_types::test_version_builds_with_major_minor_macros_defined(); test_get_dsp_wav::test_formats_and_configuration(); test_get_dsp_wav::test_invalid_files(); // TODO Automatically loop, catch exceptions, log results diff --git a/tools/test/test_get_dsp_sys_types.cpp b/tools/test/test_get_dsp_sys_types.cpp new file mode 100644 index 00000000..2ac7a6a0 --- /dev/null +++ b/tools/test/test_get_dsp_sys_types.cpp @@ -0,0 +1,45 @@ +// Regression test for issue #304. +// +// FreeBSD's and glibc's define major() and +// minor() as function-like macros, and unlike Apple's headers they keep them as +// macros in C++. Any consumer that includes one of those before NAM/get_dsp.h +// used to fail to compile, because nam::Version's member-initializer list read +// `major(major)` and the macro expanded. +// +// The macros are defined here rather than by including so the +// test reproduces the FreeBSD/glibc preprocessor state on every platform. They +// must go after get_dsp.h's dependency closure -- expanding them inside libc++'s +// own headers is a different, unrelated failure -- and are undefined at the end +// so they do not leak into the other tests included by run_tests.cpp. + +#include +#include +#include +#include +#include + +#include "NAM/dsp.h" +#include "NAM/nam_file.h" + +#define major(x) ((int)(((unsigned)(x) >> 24) & 0xff)) +#define minor(x) ((int)((x) & 0xffffff)) + +#include "NAM/get_dsp.h" + +#undef major +#undef minor + +namespace test_get_dsp_sys_types +{ +// Compiling this translation unit at all is the test; the assertions just keep +// the constructor from being optimized away and confirm the fields still land +// in the right order. +void test_version_builds_with_major_minor_macros_defined() +{ + const nam::Version version(1, 2, 3); + assert(version.major == 1); + assert(version.minor == 2); + assert(version.patch == 3); + assert(version.toString() == "1.2.3"); +} +} // namespace test_get_dsp_sys_types