-
-
Notifications
You must be signed in to change notification settings - Fork 37
Description
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: