Boost logo

Boost :

Subject: Re: [boost] [outcome] How to drop the formal empty state
From: Niall Douglas (s_sourceforge_at_[hidden])
Date: 2017-05-26 16:34:07


>> That's surprisingly hard to answer in a meaningful way. I guess the
>> former form you are writing code to generate a Foo, and then wrapping
>> that Foo up into result<Foo> for the purposes of indicating success. The
>> second form you are being more specific, you are not generating a Foo,
>> you are generating a result<Foo>. Somehow the code feels right with the
>> second form. It's somehow more idiomatic.
>
> The problem with this idiom is that you have a reference to something
> that could become an error :(
> We are introducing with this idiom a possibly reference leak. But this
> is C++.

Actually no. We are in fact employing a two stage object construction
design pattern. Let me flesh out the example code a bit so it's more
realistic:

```
class Foo
{
  A *a {nullptr};
  B *b {nullptr};
public:
  constexpr Foo() noexcept {} // never throws, constexpr
  ~Foo()
  {
    // destructor is capable of destroying partially
    // constructed instances!
    delete b; // really if(b) delete b;
    delete a; // really if(a) delete a;
  }
};

result<Foo> make_Foo() noexcept
{
  // constexpr construct a valid but uninitialised Foo instance
  result<Foo> ret(Foo());

  // get a reference to the Foo instance to be returned
  Foo &foo = ret.value();

  // initialise member variable "a" in Foo
  foo.a = new(std::nothrow) A();
  if(foo.a == nullptr)
  {
    return make_errored_result(std::errc::not_enough_memory);
  }
  // initialise member variable "b" in Foo
  foo.b = new(std::nothrow) B();
  if(foo.b == nullptr)
  {
    // Foo's destructor will be called by result<Foo> being unwound,
    // thus destroying the A object created earlier
    return make_errored_result(std::errc::not_enough_memory);
  }
  // RVOed on C++ 14 as well as C++ 17
  return ret;
}
```

Every object in AFIO v2 is constructed like this. A static init function
for every class returns a result<TYPE> and is implemented using a
two-stage construction design pattern like the above. The constexpr
noexcept constructor never initialises anything except trivial types,
and once the object has exited the constexpr constructor it is now valid
and its destructor will definitely be called.

So returning to my previous post to Peter, in the above there is no good
reason to ever return an empty result. It would make no sense, so you
never write code which could from the beginning. And type Foo knows how
to destroy a partially constructed edition of itself, so your init
function can bail out with an error at any time and all resources are
tidied up.

Niall

-- 
ned Productions Limited Consulting
http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk