There is a RAII-style C++ pattern that implements scope-based ownership by creating a class that has no member and relying on the class's constructor and destructor (and the fact that the destructor is automatically called when the function returns). For example, the standard std::lock_guard
implements this pattern.
I'm programming an EFM32 ARM Cortex-M microcontroller and came up with this class which uses a similar style:
#include <em_int.h>
class InterruptGuard final {
public:
explicit inline InterruptGuard() {
INT_Disable();
}
InterruptGuard(const InterruptGuard &other) = delete;
InterruptGuard(const InterruptGuard &&other) = delete;
inline ~InterruptGuard() {
INT_Enable();
}
InterruptGuard &operator=(const InterruptGuard &other) = delete;
InterruptGuard &operator=(const InterruptGuard &&other) = delete;
};
So if I want to disable interrupts inside a function with multiple return statements, I can make sure that they are going to be re-enabled without me worrying about explicitly re-enabling them at every return statement.
NOTE: the INT_Enable
and INT_Disable
functions implement a counter so INT_Enable
will do the right thing and only enable interrupts when they really need to be enabled. So this class should be properly nestable.
void func() {
InterruptGuard guard;
// ...
}
My question is:
When I use this pattern, is the compiler going to do "the right thing" here and optimize out the object (so that no memory is actually consumed by this class) and just inline the INT_Enable
and INT_Disable
calls to the function that uses the InterruptGuard
class?
Compiling with g++ -std=c++1y -O3 -Werror -Wextra
(gcc version 5.3.0) this code:
#include <cstdio>
class InterruptGuard final {
public:
explicit inline InterruptGuard() {
printf("enable\n");
}
InterruptGuard(const InterruptGuard &other) = delete;
InterruptGuard(const InterruptGuard &&other) = delete;
inline ~InterruptGuard() {
printf("disable\n");
}
InterruptGuard &operator=(const InterruptGuard &other) = delete;
InterruptGuard &operator=(const InterruptGuard &&other) = delete;
};
int main()
{
InterruptGuard i;
}
and this code:
#include <cstdio>
int main()
{
printf("enable\n");
printf("disable\n");
}
gives the same assembly in both cases:
.LC0:
.string "enable"
.LC1:
.string "disable"
main:
subq $8, %rsp
movl $.LC0, %edi
call puts
movl $.LC1, %edi
call puts
xorl %eax, %eax
addq $8, %rsp
ret