Skip to content

[SUGGESTION] Null(ptr/opt) coalescing for safer pointer dereferencing and more pleasant optional usage #320

Closed
@torshepherd

Description

@torshepherd

The canonical case

https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#i12-declare-a-pointer-that-must-not-be-null-as-not_null

Smart pointers can be nullptr. Dereferencing them is therefore always dangerous, and the following is the main way of guaranteeing safety:

struct P { int innerVal; };
std::shared_ptr<P> x;
std::optional<int> inner = x ? std::optional(x->innerVal) : std::optional<int>();

This is a pain that stems from two issues:

  1. std::shared_ptr does not have first-class optional semantics
  2. C++ does not have sugared optional operators for null coalescing

Optional sugar

Modern languages like Typescript and Rust provide ways to really nicely work with optional types using different forms of null coalescing to values.

Typescript:

let x: string | undefined;
y = x! // throw if undefined. y must have type string
y = x ?? 'hello' // shorthand for x ? x : 'hello'. y must have type string
y = x?.endsWith('ello') // If x is undefined, return undefined. Otherwise do the thing. y must have type bool | undefined

The last one, in particular, allows for method chaining with early-exit if the return is undefined at any point along the chain.

This sugared way of working with optional types means that there is basically no reason not to do so whenever it makes sense because the barrier to usage is so low. The TSC tooling in particular makes it a breeze to use these things in a modern text editor, doing stuff like converting . to ?. if the left hand side of the dot access is an optional type. It's awesome.

The proposal

Two ideas (can be decoupled).

  1. Sugary operators for null coalescing. Could just use the typescript ones directly to apply to std optional IMO.
x: std::optional<std::string>;
y = x!; // x.value()
// Alternatively could be:
y = x as std::string;
y = x ?? "hello"; // x.value_or("hello")
y = x?->ends_with("ello"); // x ? std::optional<bool>(x->ends_with("ello")) : std::optional<bool>();
  1. First-class optional support for smart pointers. Consider the following code:
struct P { int innerVal; };
x: std::shared_ptr<P>;
inner = x?->innerVal;

IMO this would make working with both boxed values and optional types a real pleasure compared to where we are now in C++.

Other options

  • Some way to encourage people to use not_null smart pointers instead of regular nullable smart pointers. Maybe offer not_null_shared_ptr and optional_shared_ptr = std::optional<std::shared_ptr>

Tooling support

One could easily imagine a clang-tidy-like rule that checks for unchecked access to smart pointers and suggests changing -> to ?->. This would make finding and preventing potential segfaults an automated process, with the only manual effort going into handling the nullopt cases of the optional dereferencing operator.

Let me know if this sounds intriguing and I can work on a PR!

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions