not_null is a 0-overhead modern utility for ensuring non-nullability in a simple and coherent way.
Unlike gsl::not_null
, this type can work with move-only pointers like
std::unique_ptr
, and does not require runtime checks unless explicitly
specified.
Stop worrying about nulls, and use not_null
today!
auto register_widget(cpp::not_null<std::unique_ptr<Widget>> p) -> void
{
legacy_service.adopt_widget(std::move(p).as_nullable());
}
...
// use 'cpp::check_not_null' to check and validate that 'p' is not null
register_widget(cpp::check_not_null(std::move(p)));
- Works with
unique_ptr
, and still allows nullability - Zero-Overhead;
not_null
has no hidden additional runtime or storage overhead. - Written to be compatible with C++11
- Single-header, header-only solution -- easily drops into any project
- No dependencies
- Background
A background on the problem not_null solves - Tutorial
A quick pocket-guide to using not_null - Installation
For a quick guide on how to install/use this in other projects - API Reference
For doxygen-generated API information - Attribution
Information about how to attribute this project
When working in a codebase, it's often difficult to know when any given pointer may legally contain a null pointer value. This requires the user to do one of two possible things:
- Make an assumption that the pointer can never be
nullptr
, which risks the posibility of a null-pointer dereference leading to undefined behavior, or - Always check for
nullptr
before using -- resulting in more complicated code paths, more branches, and potential pessimizations
This is the driving force behind a not_null
utility. The Guidelines Support
Library offers gsl::not_null
to attempt to satisfy this, though this has
two notable limitations:
-
Construction and assignment of
not_null
is implicit, and requires runtime checks unconditionally (which may result in a contract violation); for example:// Performs check that the pointer is not null, when the expression can never // take on the form of a non-null pointer gsl::not_null<int*> p = new int{42};
-
it does not work with move-only pointer types like
unique_ptr
, for example:gsl::not_null<std::unique_ptr<int>> p = std::make_unique<int>(42); // This errors due to 'p' not being movable! gsl::not_null<std::unique_ptr<int>> q = std::move(p);
This is where this library comes in.
Rather than allow implicit construction and assignment from the underlying
pointer, this library takes the hard stance of correct on construction,
which uses factory functions to produce the not_null
objects. Two factories
are presented:
check_not_null
, which checks a pointer for nullability before raising a contract violation, which by default is thrown as an exception, orassume_not_null
, which does no checking to produce thenot_null
and has zero overhead, which is to be used for known non-null values
With these, consumers are required to consent to a check, or explicitly
state that their pointers are non-null, to reduce errors. This leaves any
remaining uses of not_null
as nothing more than a 0-overhead wrapper; making
any runtime checks a manully consented operation. For example:
// No check performed, since the expression can never be null!
cpp::not_null<int*> p = cpp::assume_not_null(new int{42});
// Get pointer from API that we *expect* to be null, but cannot guarantee
auto q = cpp::check_not_null(get_pointer());
Additionally, not_null
allows for move-constructors and move-assignment, since
reusing an object after a move is generally a logic-error, and there are many
pieces of tooling that will check for such a case. This allows better interop
with types like unique_ptr
, and also allows for this not_null
to communicate
with more legacy APIs by propagating such pointers out efficiently:
// Should never be null, but not yet refactored to be 'not_null'
auto old_api(std::unique_ptr<Widget> p) -> void;
auto new_api(cpp::not_null<std::unique_ptr<Widget>> p) -> void
{
// Extract the move-only unique_ptr, and push along to 'old_api'
old_api(std::move(p).as_nullable());
}
The defaults for this library should satisfy most consumers needs; however not_null supports two optional features that may be controlled through preprocessor symbols:
- Using a custom namespace, and
- Disabling all exceptions
The namespace
that not_null
is defined in is configurable. By default,
it is defined in namespace cpp
; however this can be toggled by defining
the preprocessor token NOT_NULL_NAMESPACE
to be the name of the desired
namespace.
This could be done either through a #define
preprocessor directive prior to
inclusion:
#define NOT_NULL_NAMESPACE example
#include <not_null.hpp>
auto test(example::not_null<int*> p) -> void;
Or it could also be defined using a compiler flag with -D
or /D
, such
as:
g++ -std=c++11 -DNOT_NULL_NAMESPACE=example test.cpp
#include <not_null.hpp>
auto test(example::not_null<int*> p) -> void;
By default, not_null
will throw a cpp::not_null_contract_violation
when
the contract has been violated (during factory construction).
However, it is realized that not all developers appreciate or use exceptions,
and that environments that disable exception handling specifically may
error due to the existence of a throw
expression. To account for this
possibility, exceptions may be disabled through the introduction of a simple
macro preprocessor symbol: NOT_NULL_DISABLE_EXCEPTIONS
.
In doing so, contract-violations will now behave differently:
- Contract violations will call
std::abort
, causing immediate termination (and often, core-dumps for diagnostic purposes) - Contract violations will print directly to
stderr
to allow context for the termination - Since exceptions are disabled, there is no way to perform a proper stack unwinding -- so destructors will not be run. There is simply no way to allow for proper RAII cleanup without exceptions in this case.
not_null is compatible with any compiler capable of compiling valid C++11. Specifically, this has been tested and is known to work with:
- GCC 5, 6, 7, 8, 9, 10
- Clang 3.5, 3.6, 3.7, 3.8, 3.9, 4, 5, 6, 7, 8, 9, 10, 11
- Apple Clang (Xcode) 10.3, 11.2, 11.3, 12.3
- Visual Studio 2015, 2017, 2019
Latest patch level releases are assumed in the versions listed above.
not_null is licensed under the MIT License:
Copyright © 2019 Matthew Rodusek
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
alloy::core::not_null
: the original implementation that this utility is based on.gsl::not_null
: a competing implementation offered as part of the core-guidelines support librarydoxygen::oxygen::nn
: an alternative to nullability offered by Dropbox (archived)