Skip to content

Question about c++26 reflection — main() before test cases works unexpectedly #536

@sefyan0hack

Description

@sefyan0hack

Repository: https://github.com/sefyan0hack/reflection-c-26

Short version: I have a small auto-discovering unit-test framework that uses the (experimental) C++ reflection facilities (std::meta) to enumerate test namespaces and test-case functions. When I place main() before the test case function definitions in the same translation unit, everything still gets discovered and runs. I expected the reflection-based discovery to require the test functions to be defined before main() (or at least before run_tests<> is instantiated/called). Is this behavior intentional / defined by the reflection proposal, or is it a compiler-specific artifact/bug?

// g++ -std=c++26 -freflection
#include <cstdio>
#include <meta>
#include <cstring>
#include <ranges>
#include <generator>

namespace tests {
    using Test = std::generator<const char*>;
}

// (trimmed) helper that returns nested namespaces of a namespace
consteval auto inner_namespaces(std::meta::info namesp) {
    using namespace std::meta;
    return members_of(namesp, access_context::current())
        | std::views::filter(is_namespace)
        | std::ranges::to<std::vector>();
}

template<std::meta::info namesp>
auto run_tests() -> void {
    using namespace std::meta;
    // ... (omitted: printing, coloring, iterating test suites)
    template for (constexpr auto test_suite : [:reflect_constant_array(inner_namespaces(namesp)):]) {
        // iterate members_of(test_suite, access_context::current()) and run functions that yield test failures
    }
}

// **main placed before test-case function definitions**
int main() {
    run_tests<^^tests>();
}

// simple test-case macro defined `note using just a implementation define thing`
#define expect_eq(x, y) do{ if ((x) != (y)) co_yield "-> failed"; } while(false);

auto add(int a, int b) -> int { return a + b; }

namespace tests::addition {
    Test add_random_tests() {
        expect_eq(add(2,2), 4);
    }
}

Observed: The test(s) defined in namespace tests::... (which are declared after main) are still discovered and executed by run_tests<^^tests>().

Expected: I thought reflection via members_of(..., access_context::current()) would only see declarations that exist at the point where run_tests is instantiated / where access_context::current() is evaluated. Therefore I expected either:

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions