C++ Logo

std-proposals

Advanced search

Secondary matching of overloaded operator->

From: Steve Thomas <steve.p.thomas_at_[hidden]>
Date: Tue, 15 Jun 2021 09:18:38 -0700
Hi all,

Had an idea for a language feature that I'd like to float.

At the moment, an overloaded operator->() eventually resolves to a pointer
of arbitrary type, before calling some method or accessing a data member
from the type of that pointer.

  class SmartPointer {
   public:
    Pointee* operator->();
  };
  SmartPointer s;
  s->f(); // Resolves to a Pointee* p, then calls Pointee::*f(p).

I would like to change this, so that instead of immediately
calling Pointee::*f(p), we look for a method in the current class scope and
if found, call that instead:
  class SmartPointer {
   public:
    Pointee* operator->();

    template<typename R, typename... Args>
    T operator->(Pointee* p, R (Pointee::*f)(Args...));
  };
  SmartPointer s;
  s->f(); // Resolves to s.operator-><Pointee>(s->operator(), &Pointee::f);

The motivation here is to allow something that looks very like a monad.
This secondary overload would provide the bind functionality of the monad,
composing the member function of the other class into the monadic context.

The real-life example that motivated this is the clunkiness of return types
in a serialization structure (Thrift structs, to be exact). At present, if
a Thrift field is marked optional, the accessor method returns an optional
reference wrapper for the type of that field. When there is a hierarchy of
optional fields, this tends to lead to code that looks like:
  if (c.field1_ref().has_value() &&
       c.field1_ref()->field2_ref().has_value() &&
       c.field1_ref()->field2_ref()->field3_ref().has_value() {
    ... do something
with c.field1_ref()->field2_ref()->field3_ref().value() ...
  }
Each level of the hierarchy has to be checked before moving onto the next,
or the expression will throw when a field is not present.

The above overload would allow the reference wrapper to recursively wrap
the return types of the child methods in an optional reference wrapper,
making a call to:
    c.field1_ref()->field2_ref()->field3_ref().has_value()
safe regardless of whether the fields are missing or not.

This would also allow other functionality that wants to intercept and
potentially redirect smart pointer function calls like:
* calling the method on a different thread
* calling the method on a different machine over RPC
* logging function call statistics
* experimental diversions on new code being tested

Received on 2021-06-15 11:18:52