One of the hairiest problems in C++ programming is ownership of objects. Get it wrong, and you end up with memory leaks or worse. The desktop world has more or less solved this problem with smart pointers, but for small embedded systems that requires predictable timing and don’t have the RAM to spare to avoid fragmentation problems, allocating data on the heap is not an option.

For my hwcpp library I want the user to be able to write code like shown below. The led is connected to a certain gpio pin, but its other side is connected to the power, not to the ground, so it is active low. Hence setting the alarm to false must set the pin high, and when later the alarm is set to true the pin must be made low. This is done by decorating the pin with an inverter.

auto alarm = invert( target.gpio_0_1 ); alarm.set( false ); // alarm off -> pin is high . . . alarm.set( true ); // alarm on -> pin is low

In this case, the invert decorator should not make a copy of its argument, that would be wasteful and conceptually wrong (a copy of a GPIO pin, what could that mean?). It should store (just) a reference. But one day a user might (directly or indirectly) write code like the next. The inner invert call should still store a reference, but the outer call gets as argument the temporary object returned by the inner call. If it stores a reference this will soon become a stale reference.

auto alarm = invert( invert( target.gpio_0_1 ));

Using rvalue-references the invert() function can distinguish between the two cases: the inner invert() call gets an plain object as argument, hence an lvalue. The outer call gets a temporary as argument, hence an rvalue. To distinguish between these two cases, the invert() function must catch its argument as template parameter and by &&.

template< typename P > auto invert( P && pin ){ . . . }

This causes the pin parameter to be passed as P& (lvalue-reference) for a plain object, and as P&& (rvalue-reference) for a temporary. That provides a distinction between objects and temporaries, but not the distinction we need. But the *template* parameter will be a pin& when a plain object is passed, and a plain pin (not an rvalue-reference!) when a temporary is passed. Hence the object that is returned by invert should contain an attribute of this type P. This can be realised by returning an object of a type that is itself templatized on P.

template< typename P > struct invert_implementation : public pin_out { P pin; invert_implementation( P pin ): pin{ pin }{} void set( bool x ) override final { pin.set( ! x ); } . . . }; template< typename P > auto invert( P && pin ){ return invert_implementation< P >( pin ); }

The invert_implementation class has a P pin attribute, which resolves to either a P& (when invert() was called with a plain object), or to a plain P (when invert() was called with a temporary). In both cases the constructor initializes the attribute with its argument, which causes either a reference to or a copy of the argument to be stored inside the invert_implementation.

The mechanism is usefull in the situation where a temporary object is passed, but it can’t prevent all problems. When you call invert() on a local object that subsequently goes out of scope, the result is still a dangling reference. There is no cure for stupidity.

The real implementation has some more details like initialization, inverting other type of pins than pin_out, and producing a decent error message when asked to invert something that it can’t handle.