From 110de905cbb89313ecae7ffd2dfacd8f8d2a3f7a Mon Sep 17 00:00:00 2001 From: Jakob Bornecrantz Date: Thu, 9 Jul 2026 21:42:33 +0100 Subject: [PATCH] g/all: Ensure that Base is non-virtual Signed-off-by: Jakob Bornecrantz Part-of: --- src/xrt/auxiliary/glue/g_device.hpp | 3 + src/xrt/auxiliary/glue/g_hand_tracker.hpp | 3 + src/xrt/auxiliary/glue/g_traits.hpp | 33 +++++++++ tests/CMakeLists.txt | 2 + tests/tests_glue_traits.cpp | 82 +++++++++++++++++++++++ 5 files changed, 123 insertions(+) create mode 100644 src/xrt/auxiliary/glue/g_traits.hpp create mode 100644 tests/tests_glue_traits.cpp diff --git a/src/xrt/auxiliary/glue/g_device.hpp b/src/xrt/auxiliary/glue/g_device.hpp index fce30eb55..7aa72297e 100644 --- a/src/xrt/auxiliary/glue/g_device.hpp +++ b/src/xrt/auxiliary/glue/g_device.hpp @@ -13,6 +13,7 @@ #include "xrt/xrt_device.h" #include "util/u_device.h" #include "g_catch_guard.hpp" +#include "g_traits.hpp" #include @@ -116,6 +117,8 @@ public: // Members { static_assert(std::is_standard_layout_v, "glue base must be standard layout for pointer recovery"); + static_assert(is_non_virtual_base_v, + "glue base must be a non-virtual base of T for pointer recovery"); // Setup function for the device. auto &xdev = *getXDev(); diff --git a/src/xrt/auxiliary/glue/g_hand_tracker.hpp b/src/xrt/auxiliary/glue/g_hand_tracker.hpp index 7ec2c3ed6..2549b40d0 100644 --- a/src/xrt/auxiliary/glue/g_hand_tracker.hpp +++ b/src/xrt/auxiliary/glue/g_hand_tracker.hpp @@ -10,6 +10,7 @@ #include "xrt/xrt_hand_tracker.h" #include "g_catch_guard.hpp" +#include "g_traits.hpp" #include @@ -29,6 +30,8 @@ public: // Methods { static_assert(std::is_standard_layout_v, "glue base must be standard layout for pointer recovery"); + static_assert(is_non_virtual_base_v, + "glue base must be a non-virtual base of T for pointer recovery"); auto &xht = *getXHT(); diff --git a/src/xrt/auxiliary/glue/g_traits.hpp b/src/xrt/auxiliary/glue/g_traits.hpp new file mode 100644 index 000000000..165227372 --- /dev/null +++ b/src/xrt/auxiliary/glue/g_traits.hpp @@ -0,0 +1,33 @@ +// Copyright 2026, NVIDIA CORPORATION. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief Shared type traits for glue classes. + * @ingroup aux_util + */ + +#pragma once + +#include + + +namespace xrt::util { + +/*! + * True when `Base` is an accessible, non-virtual base of `Derived`, that is a + * `Base *` can be `static_cast` down to `Derived *`. That cast is ill-formed + * for a virtual base, so this is false for virtual inheritance. The glue + * wrappers use it to reject virtual inheritance of the glue base, which would + * break pointer recovery. + * + * @todo C++17 has no direct trait for "is virtual base"; revisit with a + * cleaner formulation if reflection (C++26) makes one available. + */ +template inline constexpr bool is_non_virtual_base_v = false; + +template +inline constexpr bool + is_non_virtual_base_v(static_cast(nullptr)))>> = + true; + +} // namespace xrt::util diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 63eaa0cc4..9a4c4880b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -8,6 +8,7 @@ set(tests tests_deque tests_generic_callbacks tests_glue_device + tests_glue_traits tests_history_buf tests_id_ringbuffer tests_json @@ -60,6 +61,7 @@ endforeach() target_link_libraries(tests_cxx_wrappers PRIVATE xrt-interfaces) target_link_libraries(tests_glue_device PRIVATE xrt-interfaces aux_glue) +target_link_libraries(tests_glue_traits PRIVATE xrt-interfaces aux_glue) target_link_libraries(tests_history_buf PRIVATE aux_math) target_link_libraries(tests_lowpass_float PRIVATE aux_math) target_link_libraries(tests_lowpass_integer PRIVATE aux_math) diff --git a/tests/tests_glue_traits.cpp b/tests/tests_glue_traits.cpp new file mode 100644 index 000000000..075125f41 --- /dev/null +++ b/tests/tests_glue_traits.cpp @@ -0,0 +1,82 @@ +// Copyright 2026, NVIDIA CORPORATION. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief Tests for glue/g_traits.hpp + * @author Jakob Bornecrantz + */ + +#include "glue/g_traits.hpp" + +#include "catch_amalgamated.hpp" + + +using xrt::util::is_non_virtual_base_v; + +namespace { + +struct Base +{ + int a; +}; + +struct NonVirtual : Base +{ + int b; +}; + +struct Virtual : virtual Base +{ + int b; +}; + +//! Grandchild reached through a non-virtual chain. +struct Grandchild : NonVirtual +{ + int c; +}; + +//! Grandchild where the shared @ref Base is a virtual base further up. +struct VirtualGrandchild : Virtual +{ + int c; +}; + +struct Unrelated +{ + int x; +}; + +//! Mirrors how the glue wrappers use the trait, `T` inherits `CrtpBase`. +template struct CrtpBase +{ + int base; +}; + +struct CrtpUser : CrtpBase +{ + int user; +}; + +} // namespace + + +TEST_CASE("is_non_virtual_base_v true for non-virtual bases") +{ + STATIC_REQUIRE(is_non_virtual_base_v); + STATIC_REQUIRE(is_non_virtual_base_v); + STATIC_REQUIRE(is_non_virtual_base_v); + STATIC_REQUIRE(is_non_virtual_base_v, CrtpUser>); +} + +TEST_CASE("is_non_virtual_base_v false for virtual bases") +{ + STATIC_REQUIRE_FALSE(is_non_virtual_base_v); + STATIC_REQUIRE_FALSE(is_non_virtual_base_v); +} + +TEST_CASE("is_non_virtual_base_v false for unrelated types") +{ + STATIC_REQUIRE_FALSE(is_non_virtual_base_v); + STATIC_REQUIRE_FALSE(is_non_virtual_base_v); +} -- 2.51.2