Library / SDK
eyalroz/cuda-api-wrappers avatar
eyalroz/cuda-api-wrappers

cuda-api-wrappers: C++ proxies over the CUDA Driver, Runtime and NVRTC APIs

Thin, unified, C++-flavored wrappers for the CUDA APIs

906 stars90 forksC++BSD-3-Clause

At a glance

What is it?
cuda-api-wrappers is a BSD-3-Clause header-only C++ library that wraps the CUDA Driver API, the Runtime API, NVRTC, the PTX compiler library, nvFatbin and NVTX in RAII proxy classes that throw on failure. It is aimed at C++ developers who want exception-based error handling and scoped resources without giving up the underlying CUDA semantics.
Who is it for?
cuda-api-wrappers fits a C++ codebase that already talks to the CUDA C APIs and wants scoped device, stream, event and module objects plus exceptions instead of status codes, while keeping the same CUDA semantics and the ability to drop back to raw handles at any point.
Can I use it commercially?
Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Six CUDA APIs behind one header-only C++ surface

NVIDIA exposes its GPU programming stack through several separate C-style interfaces, and this library wraps the core ones. The README lists the CUDA Driver API at the lowest level, the slightly higher-level CUDA Runtime API, NVIDIA's dynamic CUDA code compilation library NVRTC, the out-of-driver PTX compiler library available since CUDA 11.1, the fat binary creation library nvFatbin available since CUDA 12.4, and the in-program profiler API NVTX, the NVIDIA Toolkit Extensions library.

The stated purpose is to make working with those APIs more intuitive and consistent, using modern C++ language capabilities, idioms and practices. The audience is people who would otherwise call the APIs directly.

What is not here is worth as much as what is. The README is unusually direct about this: while the wrappers look like high-level, more abstract code, they are nothing more than a modern-C++ aesthetic arrangement of NVIDIA's own APIs. The library does not force any abstractions above CUDA's own, nor conventions about where to place data, how and when to synchronize, and so on. You keep the complete range of expression of the underlying APIs.

That constraint is the design. A wrapper that imposed its own memory placement or its own synchronization policy would be a different library with a different purpose, and it would be the wrong one for a project that already knows how it wants to talk to the GPU. Here, the change is in how the calls look, not in what they do.

The library is header-only, so there is nothing to compile before using it, and it is distributed under a 3-BSD licence with a Conan Center package and a Vcpkg version badge in the README.

Exceptions instead of status codes, and resources that clean themselves up

The two changes that a CUDA C programmer notices first are error handling and resource lifetime.

On errors, the README states that all functions and methods throw exceptions on failure which carry status information, and that there is no need to check return values. That is a complete inversion of the CUDA convention, where every call returns a status and the interesting result arrives through an out-parameter. A single failing CUDA call in a deeply nested code path becomes an exception that unwinds, rather than a status value that has to be threaded back up by hand.

The return-value change follows from it. Since a function does not need to return a status code, it returns what it produces. The README calls out the consequence: no more having to pre-allocate result variables and pass pointers to them as out-parameters, which it names as better compositionality.

Resources are the other half. The README lists proxy or wrapper objects for devices, streams, events, kernels, contexts, modules, link processes and timed intervals, all using the RAII convention, and states that you do not have to remember to free or release your resources yourself. In a CUDA program the manual release calls are where leaks come from, and RAII moves that responsibility to the destructor, where the compiler enforces the ordering.

There is a second consequence of the proxy design: you can forget about numeric IDs and handles, because the proxy classes fit everywhere. The README adds the escape hatch that matters for real projects: you can still get the numeric values back for cooperation with other CUDA-related software. So a wrapper object can be dropped into an existing call that expects a cudaStream_t or a CUdeviceptr without unwrapping the whole design.

The README also notes the aim of conformance with the C++ core guidelines, and describes the naming as aiming for clarity so that you do not need the official documentation to understand what each class and function does.

The context handle tax, and the promise of no hidden work

Two design claims in the README deserve to be taken apart, because one is a promise and the other is a caveat.

The promise is that nothing happens behind your back. The README says there is no work done behind your back, no caches or indices or any such thing, with almost no exceptions, and it names those exceptions: ensuring Runtime-API and Driver-API compatibility sometimes requires it, and there is the matter of creating a device's primary context. The library also avoids costly inheritance structures, vtables and virtual methods for almost all wrappers, so they vanish almost entirely on compilation. And all Runtime-API level actions are implemented so as not to disrupt Driver-API level work, which matters if your project uses both.

The caveat is the one the README calls noteworthy. The wrapper API calls cannot make assumptions about previous or later code of yours, which means some of them require more calls to obtain the current context handle or to push an existing context and then pop it. The README is honest that while these calls are cheap, they are still non-trivial and cannot be optimized away.

That is a real cost to plan for, and it is the kind of thing that is invisible until you look. A wrapper that had to look up the current context on every call in a hot loop would show up in a profile. Here the claim is narrower: the calls exist, and the compiler cannot remove them. For most code, where a CUDA call is already expensive relative to a context push, the overhead is in the noise. For code that issues thousands of very small calls, it is worth measuring rather than assuming.

A second caveat about the API shape: the proxies are scoped, which means the lifetime rules of C++ objects now govern resources whose lifetimes in the C API were explicit and manual. A stream that was created in one function and used in another now has to be passed or shared explicitly. That is usually an improvement, but it does change the shape of existing code.

Adding a header-only library to a CMake project

Because the library is header-only, the integration story is short, and the repository layout shows what is involved. The top level holds a CMakeLists.txt, a cmake/ directory for CMake modules, a src/ directory for the headers, an examples/ directory, a docs/ directory, a doxygen.cfg file and a .codedocs directory.

The examples directory is the interesting part for a first user, because it is organised rather than a single sample. The tree contains examples/CMakeLists.txt, subdirectories named by_api_module, cmake, modified_cuda_samples and other, and shared headers including common.hpp, enumerate.hpp, string_view.hpp, type_name.hpp and zip.hpp. The by_api_module directory corresponds to the README's coverage programs, one per API module, which are the programs that exercise each wrapped API. The modified_cuda_samples directory corresponds to NVIDIA's own samples, rewritten to use the wrappers.

That second group is the fastest way to understand the library, and it is a deliberate part of the project rather than an afterthought: the same call expressed in C++ proxy style next to the original form is a direct statement of what the wrappers change and what they leave alone.

For consumption, the README carries a Conan Center badge and a Vcpkg version badge, and the repository is published under a 3-BSD licence with a LICENSE file at the root. The project is set up for both a system package manager and a plain CMake-based build, and the CI badges in the README show separate Linux and Windows build workflows, so the same source is exercised on both platforms.

The README does not publish a one-line install command for a plain CMake project in the part reproduced here, so rather than guess at target names, the concrete entry points are the examples/CMakeLists.txt in the repository, the Conan Center package and the Vcpkg package.

A beta tag, a namespace change and the cost of upgrading

On maintenance, the repository is not archived and the last push was on 2026-10-08, with the default branch named master and a separate development branch used for CI.

The release history is the part to plan around. The most recent tag is v0.9.0-b1, published on 2026-09-29, and its release note names the changes: a namespace change, unique_span improvements and bug fixes. Before that came v0.8.2 on 2025-11-10, described as CUDA 13.1 compatibility with minor additions and bug fixes, and v0.8.1 on 2025-02-19, described as CUDA 12.x compatibility improvements and minor fixes.

Two things follow from that list. The first is that compatibility with a given CUDA toolkit version is something the project tracks explicitly and mentions in its release notes, so the version of CUDA you build against is a real compatibility question rather than an assumption.

The second is that v0.9.0-b1 is a beta, and it contains a namespace change. For a library whose entire value proposition is that the types fit together, a namespace change is a source-level breaking change for every user. A project that adopted v0.8.x and wants to move forward needs to budget for a recompile and a review of using declarations, not a routine version bump.

The pre-1.0 version number is consistent with the README's own tone, which is enthusiastic about the ergonomics but explicit about the boundaries of the abstraction. Nothing here suggests the API is frozen.

The licence is 3-BSD, described in the README as a permissive free software licence, and the file is at the root of the tree. That permits use in proprietary and commercial software without a copyleft obligation, and the licensing question for most commercial users therefore ends quickly. The cost of this library is not legal, it is the upgrade work described above.

When to use the wrappers and when to keep the raw C API

The genuine alternative to this library is not another wrapper. It is calling NVIDIA's C-style APIs directly, and for some projects that is the better choice.

The README lays out the case for wrapping. Both APIs are C-style interfaces targeting the lowest common denominator of language facilities for abstraction, safety and ease of use. The Runtime API has a few exceptions to that, so you actually need to write C++ to use it, but the README argues that the Runtime API is supposedly the higher-level, more convenient version of the Driver API and in fact is missing a lot of important functionality and cannot be used in conjunction with other CUDA facilities. The point being made is that the two NVIDIA APIs do not fit together cleanly, which is exactly the kind of thing a unifying layer can address.

The alternative approach is a mixed one, and it is common in existing codebases: keep the C API for the parts of the program that are performance sensitive and already written against raw handles, and use the proxies only in newer code where resource lifetimes and error paths are being designed rather than patched. Because the README insists the wrappers add no abstraction above CUDA's own and that you can still retrieve the numeric IDs and handles, a mixed codebase is a supported shape rather than a hack.

What the wrappers cost you is honest to state. Exception-based error handling changes the shape of the code, and a codebase that is built around checking status codes will need rethinking, not just recompiling. The context handle calls add a small, non-removable overhead. The proxy lifetimes change how resources are shared between functions. And in exchange you get scoped cleanup, exceptions that carry status information, and return values instead of out-parameters.

The other case where the raw API is the right answer is a project whose CUDA usage is a small, well-understood corner of a much larger codebase, written once and rarely touched. Introducing a wrapper layer there buys very little and costs a dependency.

Editorial conclusion

cuda-api-wrappers fits a C++ codebase that already talks to the CUDA C APIs and wants scoped device, stream, event and module objects plus exceptions instead of status codes, while keeping the same CUDA semantics and the ability to drop back to raw handles at any point. It is the wrong choice if you want an abstraction above CUDA, because the README is explicit that the library adds none, and it is a poor fit for a stable codebase right now because the current tag is v0.9.0-b1, a beta whose release note mentions a namespace change. Before you upgrade across that beta, plan for the source-level breaking change its release note names, and check which CUDA versions your own toolkit needs the project to support.

Frequently asked questions

What CUDA APIs does cuda-api-wrappers cover?

The README lists the CUDA Driver API, the CUDA Runtime API, NVRTC for dynamic compilation, the PTX compiler library available since CUDA 11.1, the nvFatbin library available since CUDA 12.4, and the NVTX profiler extensions.

How do cuda-api-wrappers report errors?

All functions and methods throw exceptions on failure, and those exceptions carry status information, so there is no return value to check. Because no status code has to be returned, the functions return the value they produce instead of filling an out-parameter.

Does cuda-api-wrappers need to be compiled before use?

No. The README describes it as header-only, so there is no need to compile anything special to use the library. The repository root has a CMakeLists.txt, and the project is also published to Conan Center and Vcpkg.

Do the wrapper objects still expose the raw CUDA handles?

Yes. The README says you can forget about numeric IDs and handles because the proxy classes fit everywhere, and that you can still get those numeric values for cooperation with other CUDA-related software.

What are the known limitations of the wrapper API?

The README names one noteworthy caveat: the wrapper API calls cannot make assumptions about previous or later code of yours, so some calls require extra work to obtain the current context handle or to push and pop a context, and while those calls are cheap they are non-trivial and cannot be optimized away.

Official sources

  1. eyalroz/cuda-api-wrappers on GitHub
  2. Issues
  3. License: BSD-3-Clause
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/eyalroz-cuda-api-wrappers.svg)](https://hysenlabs.com/projects/eyalroz-cuda-api-wrappers)

Was this useful?