How can I use RAII to manage resources programmed using a C-style API?
There is an easy way to use RAII to manage resources from a C-style interface: the standard library's smart pointers, which come in two flavors:
If you want simple, scope-based resource management,
You can do much the same with
Now, both of these are all well and good, but the standard library has
GotW#56 mentions that the evaluation of arguments to a function are unordered, which means if you have a function that takes your shiny new
means that the instructions might be ordered like this:
Now our precious C interface resource won't be cleaned up properly if step 2 throws because the
Again as mentioned in GotW #56, there's actually a relatively simple way to deal with the exception safety problem. Unlike expression evaluations in function arguments, function evaluations can't be interleaved. So if we acquire a resource and give it to a
You can use it like this:
and call func worry-free, like this:
The compiler can't take the construction of
There is an easy way to use RAII to manage resources from a C-style interface: the standard library's smart pointers, which come in two flavors:
std::unique_ptr for resources with a single owner and the team of std::shared_ptr and std::weak_ptr for shared resources. If you're having trouble deciding which your resource is, this FAQ should help you decide. Accessing the raw pointer managed by a smart pointer is as easy as calling its get member function.If you want simple, scope-based resource management,
std::unique_ptr is an excellent tool for the job. It's designed for minimal overhead, and is easy to set up to use custom destruction logic. So easy, in fact, that you can do it when you declare the resource variable:
#include <memory> // allow use of smart pointers
struct CStyleResource; // c-style resource
// resource lifetime management functions
CStyleResource* acquireResource(const char *, char*, int);
void releaseResource(CStyleResource* resource);
// my code:
std::unique_ptr<CStyleResource, decltype(&releaseResource)>
resource{acquireResource("name", nullptr, 0), releaseResource};
acquireResource executes where you call it, at the start of the variable's lifetime. releaseResource will execute at the end of the variable's lifetime, usually when it goes out of scope. You can do much the same with
std::shared_ptr, if you require that brand of resource lifetime instead:
// my code:
std::shared_ptr<CStyleResource>
resource{acquireResource("name", nullptr, 0), releaseResource};
Now, both of these are all well and good, but the standard library has
std::make_unique and std::make_shared and one of the reasons is further exception-safety.GotW#56 mentions that the evaluation of arguments to a function are unordered, which means if you have a function that takes your shiny new
std::unique_ptr type and some resource that might throw on construction, supplying that resource to a function call like this:
func(
std::unique_ptr<CStyleResource, decltype(&releaseResource)>{
acquireResource("name", nullptr, 0),
releaseResource},
ThrowsOnConstruction{});
means that the instructions might be ordered like this:
1. call acquireResource
2. construct ThrowsOnConstruction
3. construct std::unique_ptr from resource pointer
Now our precious C interface resource won't be cleaned up properly if step 2 throws because the
unique_ptr hasn't been constructed yet..Again as mentioned in GotW #56, there's actually a relatively simple way to deal with the exception safety problem. Unlike expression evaluations in function arguments, function evaluations can't be interleaved. So if we acquire a resource and give it to a
unique_ptr inside a function, we'll be guaranteed no tricky business will happen to leak our resource when ThrowsOnConstruction throws on construction. We can't use std::make_unique, because it returns a std::unique_ptr with a default deleter, and we want our own custom flavor of deleter. We also want to specify our resource acquisition function, since it can't be deduced from the type without additional code. Implementing such a thing is simple enough with the power of templates.
#include <memory> // smart pointers
#include <utility> // std::forward
template <
typename T,
typename Deletion,
typename Acquisition,
typename...Args>
std::unique_ptr<T, Deletion> make_c_handler(
Acquisition acquisition,
Deletion deletion,
Args&&...args){
return {acquisition(std::forward<Args>(args)...), deletion};
}
You can use it like this:
auto resource = make_c_handler<CStyleResource>(
acquireResource, releaseResource, "name", nullptr, 0);
and call func worry-free, like this:
func(
make_c_handler<CStyleResource>(
acquireResource, releaseResource, "name", nullptr, 0),
ThrowsOnConstruction{});
The compiler can't take the construction of
ThrowsOnConstruction and stick it between the call to acquireResource and the construction of the unique_ptr, so you're good.π5
The
Use is once again similar to the
and
There's a further improvement you can make to the use of
Then you can modify make_c_handler like so:
The usage syntax then changes slightly, to
shared_ptr equivalent is similarly simple: just swap out the std::unique_ptr<T, Deletion> return value with std::shared_ptr<T>, and change the name to indicate a shared resource:
template <
typename T,
typename Deletion,
typename Acquisition,
typename...Args>
std::shared_ptr<T> make_c_shared_handler(
Acquisition acquisition,
Deletion deletion,
Args&&...args){
return {acquisition(std::forward<Args>(args)...), deletion};
}
Use is once again similar to the
unique_ptr version:
auto resource = make_c_shared_handler<CStyleResource>(
acquireResource, releaseResource, "name", nullptr, 0);
and
func(
make_c_shared_handler<CStyleResource>(
acquireResource, releaseResource, "name", nullptr, 0),
ThrowsOnConstruction{});
There's a further improvement you can make to the use of
std::unique_ptr: specifying the deletion mechanism at compile time so the unique_ptr doesn't need to carry a function pointer to the deleter when it's moved around the program. Making a stateless deleter templated on the function pointer you're using requires four lines of code, placed before make_c_handler:
template <typename T, void (*Func)(T*)>
struct CDeleter{
void operator()(T* t){Func(t);}
};
Then you can modify make_c_handler like so:
template <
typename T,
void (*Deleter)(T*),
typename Acquisition,
typename...Args>
std::unique_ptr<T, CDeleter<T, Deleter>> make_c_handler(
Acquisition acquisition,
Args&&...args){
return {acquisition(std::forward<Args>(args)...), {}};
}
The usage syntax then changes slightly, to
auto resource = make_c_handler<CStyleResource, releaseResource>(
acquireResource, "name", nullptr, 0);
make_c_shared_handler would not benefit from changing to a templated deleter, as shared_ptr does not carry deleter information available at compile time.π7
ImplementingCVs.pdf
151.9 KB
--
Implementing Condition Variables with Semaphores
The paper discusses the implementation in C# but it is a straightforward translation to C/C++.
The reason I shared this paper is to show the thought process and the things people have to keep in mind when designing a solution for concurrency/parallel programming primitives.
All the kernels provide a semaphore as a primitive that can be used to build all the necessary primitives that a concurrency library provides.
Implementing Condition Variables with Semaphores
The paper discusses the implementation in C# but it is a straightforward translation to C/C++.
The reason I shared this paper is to show the thought process and the things people have to keep in mind when designing a solution for concurrency/parallel programming primitives.
All the kernels provide a semaphore as a primitive that can be used to build all the necessary primitives that a concurrency library provides.
π6
Undefined Behavior Sanitizer for C/C++ explained in depth:
https://maskray.me/blog/2023-01-29-all-about-undefined-behavior-sanitizer
If you want to learn what Undefined Behavior is, you can refer to a post earlier in this channel here.
https://maskray.me/blog/2023-01-29-all-about-undefined-behavior-sanitizer
If you want to learn what Undefined Behavior is, you can refer to a post earlier in this channel here.
MaskRay
All about UndefinedBehaviorSanitizer
Updated in 2025-01. UndefinedBehaviorSanitizer (UBSan) is an undefined behavior detector for C/C++. It consists of code instrumentation and a runtime. Both components have multiple independent impleme
π9
C/C++ Resources and FAQ pinned Β«List of books recommended for learning C (Beginners to Advanced) Beginner: C Programming - A Modern Approach Effective C Beej's Guide to C Programming - Free Online Book Intermediate Modern C 21st Century C - C tips from new school Advanced Linuxβ¦Β»
The Missing Semester of Your CS Education is a practical and hands-on computer science class that fills the gaps in traditional CS education. This course covers essential topics often overlooked in standard curriculum, empowering students and programmers to work efficiently and effectively within the computing ecosystem. Topics include mastering the command shell for task automation, harnessing version control for collaboration and problem-solving, optimizing text editing with advanced features, managing remote machines seamlessly, streamlining file searching, data wrangling from the command line, leveraging virtual machines for experimentation, and enhancing security practices online. With 12 engaging lectures and practical exercises, this class equips participants with indispensable skills to excel in the world of computer science and programming.
https://www.youtube.com/@MissingSemester/
https://www.youtube.com/@MissingSemester/
π30π1