Skip to content

♻️ Redesign QDMI device management - #1901

Draft
burgholzer wants to merge 2 commits into
codex/qdmi-configuration-foundationfrom
agent/qdmi-integration-redesign
Draft

♻️ Redesign QDMI device management#1901
burgholzer wants to merge 2 commits into
codex/qdmi-configuration-foundationfrom
agent/qdmi-integration-redesign

Conversation

@burgholzer

@burgholzer burgholzer commented Jul 14, 2026

Copy link
Copy Markdown
Member

🤖 AI text below 🤖

Summary

This PR completes the QDMI object-model redesign on top of the configuration
foundation in #1912. It replaces the overlapping FoMaC and client-driver layers
with one public flow:

DeviceRegistry → DeviceManager → Device

The same objects are used by C++, Python, Qiskit, and the neutral-atom adapter.

What changed

  • DeviceRegistry discovers configured definitions and supports idempotent
    fallback registration without loading device code.
  • DeviceManager owns an immutable registry snapshot and opens an independent
    device session for every call. openAll() isolates failures by stable device
    ID.
  • Device, Site, Operation, and Job share the session state they need, so
    derived objects and jobs may safely outlive the manager and their parent
    wrappers.
  • Device implementations are loaded through a process-wide weak cache keyed by
    canonical library path and symbol prefix. A library remains initialized while
    any live session needs it and unloads after the final session is released.
    Reopening the same implementation waits for the prior generation to finish
    finalization and unloading.
  • Python DeviceDefinition accepts path-like libraries and returns
    pathlib.Path. SessionParameters has one keyword-only constructor for its
    optional fields, including path-like auth_file.
  • Qiskit and neutral-atom integrations now use the same manager/object model.
  • The former FoMaC and qdmi::Driver C++/Python APIs are removed; UPGRADING.md
    documents the migration.

Relationship to #1912

This branch is based on exact #1912 head
7769bdde0a710b340c5f3a291a509562131f505b. It reuses #1912's qdmi.json,
[tool.qdmi], stable-ID merge semantics, disabled-ID reservations, relocatable
CMake metadata, and runtime packaging. It does not duplicate configuration
parsing or add another discovery mechanism.

Integration feedback

The reconstructed API was exercised from local QDMI-on-IQM, Amazon Braket, and
QDMI template/example branches. That iteration removed manager mutation and
singleton state, eliminated separate session-ownership members, preserved
path-like inputs, and added the concise SessionParameters constructor.

Independently installed device wheels remain explicit: they expose their
library path and register a fallback definition before constructing a manager.
User configuration and disabled definitions retain precedence.

Validation

  • 192 focused native device, manager, registry, neutral-atom, and imported-CMake
    tests
  • deterministic finalization/reopen regression repeated 10 times
  • 260 focused installed-package Python tests
  • Python stub generation
  • warning-as-error documentation build
  • uvx nox -s lint
  • git diff --check

Depends on #1912.

@burgholzer burgholzer self-assigned this Jul 14, 2026
@burgholzer burgholzer added refactor Anything related to code refactoring major Major version update QDMI Anything related to QDMI labels Jul 14, 2026
@codecov

codecov Bot commented Jul 14, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.52430% with 35 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/qdmi/Device.cpp 96.2% 17 Missing ⚠️
src/qdmi/DeviceState.cpp 88.2% 8 Missing ⚠️
include/mqt-core/na/qdmi/Device.hpp 71.4% 6 Missing ⚠️
python/mqt/core/plugins/qiskit/provider.py 86.6% 2 Missing ⚠️
src/qdmi/DeviceManager.cpp 94.4% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@MatthiasReumann MatthiasReumann left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a great start into the QDMI rewrite! 🚀

I've gone through the code a couple of times now and the following comments are the result of that.

However, currently this can't construct devices at runtime, right? As of now this simply loads dynamic libraries, I think 🤔.

Comment thread include/mqt-core/qdmi/DeviceManager.hpp Outdated
Comment thread include/mqt-core/qdmi/Device.hpp Outdated
Comment thread include/mqt-core/qdmi/Device.hpp Outdated
Comment thread src/qdmi/DeviceManager.cpp Outdated
Comment thread src/qdmi/DeviceManager.cpp Outdated
Comment on lines +659 to +667
{
const std::scoped_lock lock(impl_->mutex);
library = impl_->libraries[key].lock();
if (!library) {
library =
detail::makeV1DeviceApi(definition->library, definition->prefix);
impl_->libraries[key] = library;
}
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something is fishy here.

impl_->libraries[key] is a std::weak_ptr - a non-owning reference to a shared_ptr. The DeviceManager (or the DeviceManager::Impl) class should be owning the device shared_ptr. .lock()ing and assigning the newly created shared_ptr only works (as far as I can tell) because the Device class below holds the same shared_ptr. I am pretty sure this function wouldn't work anymore if the Device takes a const pointer instead of a smart pointer.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The map in DeviceManager::Impl should probably store the unique_ptr mentioned above (or below).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am also not really sure if I like the mutex here. I get where this is coming from - thread safety - but I think one really has to think this through if this would be enough.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@coderabbitai Could you please evaluate why a weak_ptr is used here and if this is correct?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -e
fd -HI "DeviceManager.hpp" 
fd -HI "Device.hpp" src/qdmi || true
fd -HI "DeviceApi.hpp" || true
echo "---DeviceManager.hpp---"
fd "DeviceManager.hpp" -x cat -n {}
echo "---Device.hpp (qdmi)---"
fd "Device.hpp" -p "qdmi" -x cat -n {}
echo "---DeviceApi.hpp---"
fd "DeviceApi.hpp" -x cat -n {}

Length of output: 4767


🏁 Script executed:

#!/bin/bash
set -e
fd "Device.hpp" -x cat -n {}
echo "---DeviceApi.hpp---"
cat -n src/qdmi/DeviceApi.hpp
echo "---DeviceApi.cpp (if exists)---"
fd "DeviceApi.cpp" -x cat -n {}

Length of output: 89512


@MatthiasReumann Good catch — let me walk through what's actually happening and where I agree/disagree.

Why weak_ptr is used (the intent)

impl_->libraries is meant to be a non-owning cache keyed by library path + prefix, so that:

  • Multiple open() calls for the same underlying .so/.dll (e.g., two sessions of the same device, or child devices sharing a parent library) reuse a single dlopen'd DeviceApi instance instead of loading it multiple times.
  • The library is automatically unloaded (dlclose) once nothing needs it anymore — DeviceManager doesn't want to force-keep every library loaded forever just because it was opened once.

The real owner of the shared_ptr<DeviceApi> is not DeviceManager at all — it's detail::DeviceState::api (see DeviceApi.hpp Line 97), which is reached via Device::state_DeviceState. So the chain is:

Device --(shared_ptr)--> DeviceState --(shared_ptr)--> DeviceApi

DeviceManager::Impl::libraries[key] only ever holds a weak_ptr, so as soon as the last Device/Job using that library is destroyed, DeviceState's destructor releases its shared_ptr<DeviceApi>, the refcount hits zero, the library is dlclose'd, and the cache entry silently becomes expired (lazily replaced on the next open()).

Is this correct?

Today, yes — mechanically it works because DeviceState is the sole strong owner. But your instinct is right that this is fragile: correctness here depends entirely on an invariant that isn't enforced or even documented anywhere near DeviceManager.cpp — it just happens to hold because of how DeviceState/Device are implemented elsewhere. If someone later changes Device/DeviceState to not hold the shared_ptr (e.g., a raw pointer optimization, or if DeviceState::api were made a weak_ptr too "for symmetry"), this cache would break with no compile-time signal — library in open() would be constructed, immediately go out of scope with no other owner, and be unloaded from under the returned Device. I'd suggest adding a comment above the libraries member (in DeviceManager::Impl) stating explicitly: "non-owning cache; DeviceState/Device must hold the real shared_ptr<DeviceApi>." That at least makes the contract discoverable for future refactors.

A more concrete, separate bug your mutex comment surfaced

The mutex in DeviceManager::Impl only guards libraries. It does not guard registry (DeviceRegistry::definitions_, a plain std::vector):

  • DeviceManager::registerDevice / unregisterDevice call straight into impl_->registry.registerDevice/unregisterDevice with no lock at all.
  • DeviceManager::open reads impl_->registry.definitions() unguarded (the scoped_lock is taken only afterward, around the libraries map access).

So if one thread calls open() while another calls registerDevice()/unregisterDevice() on the same DeviceManager, that's an unguarded concurrent read/write on std::vector, which is a real data race/UB, not just a theoretical concern. If DeviceManager is meant to be safely usable from multiple threads (which the presence of a mutex implies), this needs to be fixed too, e.g.:

const std::vector<DeviceDefinition>& DeviceManager::definitions() const {
  const std::scoped_lock lock(impl_->mutex);
  return impl_->registry.definitions();  // NB: returning a ref out of a locked scope is itself unsafe under mutation
}

void DeviceManager::registerDevice(DeviceDefinition definition, const bool replace) {
  const std::scoped_lock lock(impl_->mutex);
  impl_->registry.registerDevice(std::move(definition), replace);
}

Note that even this isn't quite enough for definitions()/open(), since returning a reference to (or iterating) a mutable vector after releasing the lock is unsafe if another thread can mutate it concurrently — you'd likely want open() to copy out the matching DeviceDefinition while holding the lock, rather than holding an iterator/reference into definitions_ past the lock's scope.

So, to directly answer your question: the weak_ptr choice for the library cache is intentional and works today, but it's an implicit, undocumented invariant rather than a locally-verifiable one — worth a comment at minimum. The mutex, as currently scoped, doesn't make DeviceManager thread-safe overall since it never protects registry/definitions_, which is the concrete issue underlying your third comment.

Want me to open a follow-up issue for the registry/definitions_ locking gap, or would you rather fold it into this PR?


✏️ Learnings added
Learnt from: MatthiasReumann
Repo: munich-quantum-toolkit/core PR: 1901
File: src/qdmi/DeviceManager.cpp:659-667
Timestamp: 2026-07-15T08:07:16.502Z
Learning: In munich-quantum-toolkit/core, `src/qdmi/DeviceManager.cpp`'s `DeviceManager::Impl::libraries` map intentionally stores `std::weak_ptr<detail::DeviceApi>` as a non-owning cache keyed by (library path, prefix); the true strong owner of the loaded library is `detail::DeviceState::api` (a `std::shared_ptr<DeviceApi>`, defined in `src/qdmi/DeviceApi.hpp`), reached transitively via `Device::state_` -> `DeviceState`. This lets the library auto-unload via `dlclose`/`FreeLibrary` once the last `Device`/`Job` referencing it is destroyed. This is a fragile, non-local invariant: if `DeviceState`/`Device` ever stop holding a strong `shared_ptr<DeviceApi>`, the weak cache in `DeviceManager` would break silently.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@burgholzer I think this is quite interesting. Maybe something to think about whether the DeviceManager should be the only instance managing devices. Personally, I am not 100% sure if I would like to keep the shared_ptr; although the arguments are somewhat convincing. In the end it's a design decision.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add some more reasoning from my iteration later. Right now, I am rather convinced this is a good design as it is.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

@burgholzer burgholzer Jul 15, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@coderabbitai @MatthiasReumann The weak ownership is intentional: the cache is only a reuse index, while live device and job state owns the loaded library. This lets multiple sessions reuse an already loaded provider without forcing the library to remain loaded after the final live object disappears.

I did find a real problem in the original implementation: the cache was manager-local, so separate managers could initialize the same library independently. It is now process-wide, keyed by canonical library path and symbol prefix, and guarded during lookup and construction. If no live object remains, the weak reference expires and reopening the provider is the expected behavior. A regression test covers reuse across simultaneously live managers and equivalent library paths.

Following the lifetime reasoning above, I did not put manager-owned unique_ptrs into this map. Doing so would couple library lifetime to one manager and conflict with the requirement that returned devices and jobs can outlive it.

The map has instead moved out of DeviceManager into the process-wide library loader and contains weak references. Strong ownership remains exclusively with live device and job state, so the final such object controls unloading.

Agreed that the original mutex was not a complete synchronization design. The responsibilities are now separated:

  • a manager-local registry mutex protects definition snapshots, registration, unregistration, and lookup;

  • definitions() returns a value snapshot instead of exposing a reference;

  • a separate process-wide cache mutex protects canonical library lookup and initialization across all managers.

Session creation remains independent after the shared provider library has been obtained. Tests cover concurrent opens, concurrent registry access, independent sessions, and process-wide library reuse. This defines the manager’s guarantees without attempting to serialize all calls made through independent provider sessions.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the rationale is getting clearer for me now, thanks! I have some question / thoughts though:

The weak ownership is intentional: the cache is only a reuse index, while live device and job state owns the loaded library.

Now I get why the DeviceApi is being used as a shared_ptr - this makes totally sense!

I did find a real problem in the original implementation: the cache was manager-local, so separate managers could initialize the same library independently. It is now process-wide, keyed by canonical library path and symbol prefix, and guarded during lookup and construction. If no live object remains, the weak reference expires and reopening the provider is the expected behavior. A regression test covers reuse across simultaneously live managers and equivalent library paths.

Just to make sure that I understand you fully here: Alternatively we could have made the DeviceManager a singleton, right? Probably depends on if we ever want to create DeviceManager's using different registries.

Following the lifetime reasoning above, I did not put manager-owned unique_ptrs into this map. Doing so would couple library lifetime to one manager and conflict with the requirement that returned devices and jobs can outlive it.

I think we should then probably find a more suitable name than DeviceManager. I don't know, something like DeviceExplorer? Following we should document how to best use the DeviceManager. Currently, I imagine something like

auto findAllDevices(....) {
  DeviceManager m(...); 
  return m.openAll(...);
}

Comment thread include/mqt-core/qdmi/DeviceManager.hpp Outdated
@mergify mergify Bot added the conflict label Jul 15, 2026
@burgholzer
burgholzer force-pushed the agent/qdmi-integration-redesign branch 2 times, most recently from 66b0be6 to b050c37 Compare July 15, 2026 11:54
@mergify mergify Bot removed the conflict label Jul 15, 2026
@burgholzer
burgholzer force-pushed the agent/qdmi-integration-redesign branch from 78f00ef to 330c111 Compare July 15, 2026 14:13
@burgholzer

Copy link
Copy Markdown
Member Author

@MatthiasReumann I am through with another iteration here. Feel free to take another look.
Note that this does not yet touch the SC and NA devices and makes them configurable. That would be a follow-up.
I still believe this is shaping up quite nicely.

I may try to split this into two changes, one that is v3.x compatible but adds the configurability and a separate one that contains the breaking change of dropping the client interface and combining driver and FoMaC.
That also might be a nice separation of concerns.

Also pinging @marcelwa @flowerthrower @ystade here for awareness.
If you want a very quick overview of the changes, read https://github.com/munich-quantum-toolkit/core/blob/agent/qdmi-integration-redesign/UPGRADING.md#qdmi-device-management-has-been-redesigned.

@MatthiasReumann MatthiasReumann left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solid improvements! 🚀

I've left two minor requests and some questions / thoughts in the open discussion above.

Otherwise, I've nothing to complain than we should probably add a bit more documentation on the caching, shared_ptr, lifetime logic to the respective classes. I am pretty sure Codex can help with that ;)

Comment thread src/qdmi/DeviceApi.h

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.hpp file in src folder

Comment thread src/qdmi/DeviceApi.cpp

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file still uses trailing return types.

@mergify mergify Bot added the conflict label Jul 25, 2026
@burgholzer
burgholzer force-pushed the agent/qdmi-integration-redesign branch from 81dd10e to fc24a9c Compare July 27, 2026 14:56
@mergify mergify Bot removed the conflict label Jul 27, 2026
@burgholzer
burgholzer force-pushed the agent/qdmi-integration-redesign branch from fc24a9c to 768b78c Compare July 27, 2026 15:31
Replace the overlapping FoMaC and client-driver layers with one registry, manager, and object model shared by C++, Python, Qiskit, and the neutral-atom adapter.

Assisted-by: GPT-5.6 via Codex
Describe configuration, registry ownership, fresh-session opening, migration, and validation for the consolidated public API.

Assisted-by: GPT-5.6 via Codex
@burgholzer
burgholzer force-pushed the agent/qdmi-integration-redesign branch from 768b78c to 466e58e Compare July 27, 2026 15:52
@github-actions

Copy link
Copy Markdown
Contributor

Cpp-Linter Report ⚠️

Some files did not pass the configured checks!

clang-tidy (v22.1.8) reports: 30 concern(s)
  • src/qdmi/DeviceApi.cpp:164:11: warning: [cppcoreguidelines-owning-memory]

    deleting a pointer through a type that is not marked 'gsl::owner<>'; consider using a smart pointer instead

      164 |           delete instance;
          |           ^      ~~~~~~~~
    /home/runner/work/core/core/src/qdmi/DeviceApi.cpp:163:26: note: variable declared here
      163 |         api, [finalized](const DeviceApi* instance) noexcept {
          |                          ^~~~~~~~~~~~~~~~~~~~~~~~~
  • src/qdmi/DeviceApi.cpp:168:48: warning: [modernize-use-designated-initializers]

    use designated initializer list to initialize 'DeviceApiCacheEntry'

      168 |                             DeviceApiCacheEntry{loaded, std::move(finalized)});
          |                                                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          |                                                 .api=   .finalized=
    /home/runner/work/core/core/src/qdmi/DeviceApi.cpp:33:1: note: aggregate type is defined here
       33 | struct DeviceApiCacheEntry {
          | ^
  • src/qdmi/DeviceApi.cpp:168:62: warning: [misc-include-cleaner]

    no header providing "std::move" is directly included

       24 |                             DeviceApiCacheEntry{loaded, std::move(finalized)});
          |                                                              ^
  • src/qdmi/DeviceRegistry.cpp:22:1: warning: [misc-include-cleaner]

    included header memory is not used directly

       22 | #include <memory>
          | ^~~~~~~~~~~~~~~~~
       23 | #include <optional>
  • test/qdmi/device/lifecycle_device.cpp:19:12: warning: [cppcoreguidelines-avoid-non-const-global-variables]

    variable 'stateMutex' is non-const and globally accessible, consider making it const

       19 | std::mutex stateMutex;
          |            ^
  • test/qdmi/device/lifecycle_device.cpp:20:25: warning: [cppcoreguidelines-avoid-non-const-global-variables]

    variable 'stateChanged' is non-const and globally accessible, consider making it const

       20 | std::condition_variable stateChanged;
          |                         ^
  • test/qdmi/device/lifecycle_device.cpp:21:6: warning: [cppcoreguidelines-avoid-non-const-global-variables]

    variable 'blockFinalize' is non-const and globally accessible, consider making it const

       21 | bool blockFinalize = false;
          |      ^
  • test/qdmi/device/lifecycle_device.cpp:22:6: warning: [cppcoreguidelines-avoid-non-const-global-variables]

    variable 'finalizeStarted' is non-const and globally accessible, consider making it const

       22 | bool finalizeStarted = false;
          |      ^
  • test/qdmi/device/lifecycle_device.cpp:23:6: warning: [cppcoreguidelines-avoid-non-const-global-variables]

    variable 'releaseFinalize' is non-const and globally accessible, consider making it const

       23 | bool releaseFinalize = false;
          |      ^
  • test/qdmi/device/lifecycle_device.cpp:24:13: warning: [cppcoreguidelines-avoid-non-const-global-variables]

    variable 'initializeCount' is non-const and globally accessible, consider making it const

       24 | std::size_t initializeCount = 0;
          |             ^
  • test/qdmi/device/lifecycle_device.cpp:25:13: warning: [cppcoreguidelines-avoid-non-const-global-variables]

    variable 'finalizeCount' is non-const and globally accessible, consider making it const

       25 | std::size_t finalizeCount = 0;
          |             ^
  • test/qdmi/device/lifecycle_device.cpp:26:13: warning: [cppcoreguidelines-avoid-non-const-global-variables]

    variable 'overlappingInitializeCount' is non-const and globally accessible, consider making it const

       26 | std::size_t overlappingInitializeCount = 0;
          |             ^
  • test/qdmi/device/lifecycle_device.cpp:31:33: warning: [readability-identifier-naming]

    invalid case style for function 'TEST_LIFECYCLE_prepare_blocking_finalize'

       31 | TEST_LIFECYCLE_QDMI_EXPORT void TEST_LIFECYCLE_prepare_blocking_finalize() {
          |                                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          |                                 testLifecyclePrepareBlockingFinalize
  • test/qdmi/device/lifecycle_device.cpp:42:1: warning: [readability-identifier-naming]

    invalid case style for function 'TEST_LIFECYCLE_wait_for_finalize'

       42 | TEST_LIFECYCLE_wait_for_finalize(const std::size_t timeoutMs) {
          | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          | testLifecycleWaitForFinalize
  • test/qdmi/device/lifecycle_device.cpp:49:1: warning: [readability-identifier-naming]

    invalid case style for function 'TEST_LIFECYCLE_wait_for_initializations'

       49 | TEST_LIFECYCLE_wait_for_initializations(const std::size_t expected,
          | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          | testLifecycleWaitForInitializations
  • test/qdmi/device/lifecycle_device.cpp:57:33: warning: [readability-identifier-naming]

    invalid case style for function 'TEST_LIFECYCLE_release_finalize'

       57 | TEST_LIFECYCLE_QDMI_EXPORT void TEST_LIFECYCLE_release_finalize() {
          |                                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          |                                 testLifecycleReleaseFinalize
  • test/qdmi/device/lifecycle_device.cpp:65:40: warning: [readability-identifier-naming]

    invalid case style for function 'TEST_LIFECYCLE_initialize_count'

       65 | TEST_LIFECYCLE_QDMI_EXPORT std::size_t TEST_LIFECYCLE_initialize_count() {
          |                                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          |                                        testLifecycleInitializeCount
  • test/qdmi/device/lifecycle_device.cpp:70:40: warning: [readability-identifier-naming]

    invalid case style for function 'TEST_LIFECYCLE_finalize_count'

       70 | TEST_LIFECYCLE_QDMI_EXPORT std::size_t TEST_LIFECYCLE_finalize_count() {
          |                                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          |                                        testLifecycleFinalizeCount
  • test/qdmi/device/lifecycle_device.cpp:76:1: warning: [readability-identifier-naming]

    invalid case style for function 'TEST_LIFECYCLE_overlapping_initialize_count'

       76 | TEST_LIFECYCLE_overlapping_initialize_count() {
          | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          | testLifecycleOverlappingInitializeCount
  • test/qdmi/device/test_device_api.cpp:33:33: warning: [readability-identifier-naming]

    invalid case style for function 'TEST_LIFECYCLE_prepare_blocking_finalize'

       33 | TEST_LIFECYCLE_QDMI_EXPORT void TEST_LIFECYCLE_prepare_blocking_finalize();
          |                                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          |                                 testLifecyclePrepareBlockingFinalize
  • test/qdmi/device/test_device_api.cpp:35:1: warning: [readability-identifier-naming]

    invalid case style for function 'TEST_LIFECYCLE_wait_for_finalize'

       35 | TEST_LIFECYCLE_wait_for_finalize(std::size_t timeoutMs);
          | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          | testLifecycleWaitForFinalize
  • test/qdmi/device/test_device_api.cpp:37:1: warning: [readability-identifier-naming]

    invalid case style for function 'TEST_LIFECYCLE_wait_for_initializations'

       37 | TEST_LIFECYCLE_wait_for_initializations(std::size_t expected,
          | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          | testLifecycleWaitForInitializations
  • test/qdmi/device/test_device_api.cpp:39:33: warning: [readability-identifier-naming]

    invalid case style for function 'TEST_LIFECYCLE_release_finalize'

       39 | TEST_LIFECYCLE_QDMI_EXPORT void TEST_LIFECYCLE_release_finalize();
          |                                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          |                                 testLifecycleReleaseFinalize
  • test/qdmi/device/test_device_api.cpp:40:40: warning: [readability-identifier-naming]

    invalid case style for function 'TEST_LIFECYCLE_initialize_count'

       40 | TEST_LIFECYCLE_QDMI_EXPORT std::size_t TEST_LIFECYCLE_initialize_count();
          |                                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          |                                        testLifecycleInitializeCount
  • test/qdmi/device/test_device_api.cpp:41:40: warning: [readability-identifier-naming]

    invalid case style for function 'TEST_LIFECYCLE_finalize_count'

       41 | TEST_LIFECYCLE_QDMI_EXPORT std::size_t TEST_LIFECYCLE_finalize_count();
          |                                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          |                                        testLifecycleFinalizeCount
  • test/qdmi/device/test_device_api.cpp:43:1: warning: [readability-identifier-naming]

    invalid case style for function 'TEST_LIFECYCLE_overlapping_initialize_count'

       43 | TEST_LIFECYCLE_overlapping_initialize_count();
          | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          | testLifecycleOverlappingInitializeCount
  • test/qdmi/device/test_device_api.cpp:367:36: warning: [misc-include-cleaner]

    no header providing "std::move" is directly included

       30 |   std::thread finalize([api = std::move(first)]() mutable { api.reset(); });
          |                                    ^
  • test/qdmi/manager/test_device_manager.cpp:102:8: warning: [misc-include-cleaner]

    no header providing "size_t" is directly included

       17 |   for (size_t i = 0; i < 4; ++i) {
          |        ^
  • test/qdmi/registry/test_device_registry.cpp:43:3: warning: [modernize-use-nodiscard]

    function 'write' should be marked [[nodiscard]]

       43 |   std::filesystem::path write(const std::filesystem::path& relative,
          |   ^
          |   [[nodiscard]] 
  • test/qdmi/registry/test_device_registry.cpp:151:3: warning: [misc-const-correctness]

    variable 'registry' of type 'qdmi::DeviceRegistry' can be declared 'const'

      151 |   qdmi::DeviceRegistry registry;
          |   ^
          |                        const 

Have any feedback or feature suggestions? Share it here.

@burgholzer
burgholzer changed the base branch from main to codex/qdmi-configuration-foundation July 27, 2026 18:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

major Major version update QDMI Anything related to QDMI refactor Anything related to code refactoring

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants