Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions NAM/get_dsp.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,14 @@ class IVersionSupportChecker
class Version
{
public:
// Brace-init the members rather than `major(major)`: <sys/types.h> (FreeBSD)
// and <sys/sysmacros.h> (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}
{
}

Expand Down
4 changes: 4 additions & 0 deletions tools/run_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
// See the GitHub Action for a demo of how to build and run tests.

#include <iostream>
// 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"
Expand Down Expand Up @@ -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
Expand Down
45 changes: 45 additions & 0 deletions tools/test/test_get_dsp_sys_types.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Regression test for issue #304.
//
// FreeBSD's <sys/types.h> and glibc's <sys/sysmacros.h> 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 <sys/types.h> 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 <cassert>
#include <memory>
#include <optional>
#include <string>
#include <vector>

#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