patterncppMinor
UNUSED() with teeth in C++11 debug builds
Viewed 0 times
buildswithdebugunusedteeth
Problem
When you turn up the compiler warnings with
But that's not very binding, since it's generally just a void cast (or near-equivalent). Hence if you get the warning, add an UNUSED(), and some time later actually do use the variable...it's misleading to to the reader of the code.
I got the odd idea of making C++11 give this some level of "enforcement", by seeing if it got in a POD lvalue it could trash it. Obviously won't work for everything (unused constants, etc.) But I kind of fiddled with it until it had the effect I wanted--while I can't say exactly why every variation I tried didn't work.
You could break it out in various ways to do something special for
Anything cleaner/simpler to achieve the same effect, or ideas to make it better?
-Wunused-variable, one tends to then employ an UNUSED() macro, to say something is intentionally not used at this time.But that's not very binding, since it's generally just a void cast (or near-equivalent). Hence if you get the warning, add an UNUSED(), and some time later actually do use the variable...it's misleading to to the reader of the code.
I got the odd idea of making C++11 give this some level of "enforcement", by seeing if it got in a POD lvalue it could trash it. Obviously won't work for everything (unused constants, etc.) But I kind of fiddled with it until it had the effect I wanted--while I can't say exactly why every variation I tried didn't work.
You could break it out in various ways to do something special for
std::is_pointer<> or other types, or even have some kind of "trashme()" method detection for non-POD values. But here's the core of the idea:#if !defined(NDEBUG)
template::type,
typename std::enable_if::value
|| std::is_const::value
|| !std::is_pod::value
>::type* = nullptr
>
void UNUSED(T && v) {
static_cast(v);
}
template::type,
typename std::enable_if::value
&& !std::is_const::value
&& std::is_pod::value
>::type* = nullptr
>
void UNUSED(T && v) {
memset(&v, 0xBD, sizeof(TRR)); // or 0xDECAFBAD pattern, etc.
}
#endifAnything cleaner/simpler to achieve the same effect, or ideas to make it better?
Solution
Missing headers
This code needs
Use
While we can write
Reduce repetition
There are two expressions that need to be maintained in parallel in the
Then the functions reduce to
In C++14, we could use the
If we were using C++17, we could further reduce to a single function, by moving the test within the function body:
Now we're no longer using
Enhancement: accept multiple arguments
Here's a varargs template that makes it easier to use:
Again, C++17 makes this simpler, with fold expressions:
A definition is required when
Probably just an oversight, but the code is missing an
Revised code
Four compilations are required to test this fully for all combinations of
This code needs
#include
#include Use
sizeof operator on a valueWhile we can write
sizeof (type), in this case we have a value, and we can use it here - sizeof will read through the reference, saving us some work:std::memset(&v, 0xBD, sizeof v);Reduce repetition
There are two expressions that need to be maintained in parallel in the
std::enable_if template. We can refactor these into a single trait template:template
struct is_mutable_pod_lvalue
: std::integral_constant::value
&& !std::is_const::type>::value
&& std::is_pod::type>::value>
{};Then the functions reduce to
template
typename std::enable_if::value>::type
UNUSED(T && v) {
static_cast(v);
}
template
typename std::enable_if::value>::type
UNUSED(T && v) {
std::memset(&v, 0xBD, sizeof v);
}In C++14, we could use the
_t and _v forms to avoid typing ::type and ::value respectively, and in C++17, we could use std::bool_constant instead of std::integral_constant. If we were using C++17, we could further reduce to a single function, by moving the test within the function body:
template
UNUSED(T&& v) {
if constexpr (is_mutable_pod_lvalue::value)
std::memset(&v, 0xBD, sizeof v);
}Now we're no longer using
is_mutable_pod_value more than once, we can just inline it back again:template
void UNUSED(T&& v) {
static const bool is_mutable_pod_lvalue =
std::is_lvalue_reference::value
&& !std::is_const::type>::value
&& std::is_pod::type>::value;
if constexpr ((is_mutable_pod_lvalue)
std::memset(&v, 0xBD, sizeof v);
}Enhancement: accept multiple arguments
Here's a varargs template that makes it easier to use:
template
void UNUSED(T&& v, U&&... u)
{
UNUSED(v);
UNUSED(u...);
}Again, C++17 makes this simpler, with fold expressions:
template
void UNUSED(T&&... v)
{
(UNUSED(v) , ...);
}A definition is required when
NDEBUG is setProbably just an oversight, but the code is missing an
#else block. It's probably worth reordering so that the positive branch comes first (confusing because NDEBUG is itself a negative...). Further, since the only difference between the debug and non-debug cases it the use of std::memset() in one of the implementations, we can reduce the scope of #ifdef to just that (at a small cost to non-debug compilation times):template
typename std::enable_if::value>::type
UNUSED(T&& v)
{
static_cast(v);
#ifndef NDEBUG
std::memset(&v, 0xBD, sizeof v);
#endif
}Revised code
#include
#include
template
struct is_mutable_pod_lvalue
: std::integral_constant::value
&& !std::is_const::type>::value
&& std::is_pod::type>::value>
{};
template
typename std::enable_if::value>::type
UNUSED(T&& v)
{
static_cast(v);
}
template
typename std::enable_if::value>::type
UNUSED(T&& v)
{
static_cast(v);
#ifndef NDEBUG
std::memset(&v, 0xBD, sizeof v);
#endif
}
template
void UNUSED(T&& v, U&&... u)
{
UNUSED(v);
UNUSED(u...);
}#include
void f(int i, const int ci, int& ri, const int& cri, int *pi,
std::unique_ptr ui, std::unique_ptr&& rrui)
{
#ifdef TEST_UNUSED
UNUSED(i, ci, ri, cri, pi, ui, rrui);
#endif
}Four compilations are required to test this fully for all combinations of
TEST_UNUSED and NDEBUG.Code Snippets
#include <type_traits>
#include <cstring>std::memset(&v, 0xBD, sizeof v);template<typename T>
struct is_mutable_pod_lvalue
: std::integral_constant<bool,
std::is_lvalue_reference<T&&>::value
&& !std::is_const<typename std::remove_reference<T>::type>::value
&& std::is_pod<typename std::remove_reference<T>::type>::value>
{};template<typename T>
typename std::enable_if<!is_mutable_pod_lvalue<T>::value>::type
UNUSED(T && v) {
static_cast<void>(v);
}
template<typename T>
typename std::enable_if<is_mutable_pod_lvalue<T>::value>::type
UNUSED(T && v) {
std::memset(&v, 0xBD, sizeof v);
}template<typename T>
UNUSED(T&& v) {
if constexpr (is_mutable_pod_lvalue<T>::value)
std::memset(&v, 0xBD, sizeof v);
}Context
StackExchange Code Review Q#159439, answer score: 4
Revisions (0)
No revisions yet.