Jeff
Jeff

Reputation: 3525

C++ hinting/warning for returned reference lifetimes

I have a not-so-ideal situation where a class returns handle references to objects that shouldn't be accessed after the parent objects' lifetimes. What is the best way to alter the pattern below to aid defensive coding?

// 'A<T>' is a thin factory-style utility class with asynchronous consumers.
template <typename T>
struct A {
  A() : h_(*(new T())) { /* ... */ }
  ~A() { /* h_ deleted or ownership passed elsewhere */ }

  // What's the best way to indicate that these handles shouldn't be used after
  // the destructions of the A instances?
  T &handle() { return h_; }

private
  T &h_;
};

struct B { /* ... */ };

int main() {
   B *b1{nullptr};
   {
     A<B> a;

     // Is there a good way to trigger detection that the reference is bound to
     // a variable which will outlive its 'valid' local lifetime?
     b1 = &a.handle();

     B &b2(a.handle()); // this is reasonable though

     b1->ok_action();
     b2.also_alright();
   }
   b1->uh_oh();
}

I know you can't truly prevent a user of C++ from doing most unsafe things, but if I could at least generate warnings on trivial accidental uses like this is would be the bulk of what I'd like to achieve.

Upvotes: 0

Views: 89

Answers (2)

glank
glank

Reputation: 391

I'm taking the liberty of making a few assumptions about your situation:

  • The handles point to dynamically allocated objects generated by A at the users discretion.
  • The handles will be passed around where A is out of scope, thus A cannot be used as a mandatory gateway.
  • The data to which the handles point must be destroyed when A is destroyed, thus automatic garbage collection cannot be applied to the handles.
  • So far, compile-time safety checking does not seem to be possible. You want coding mistakes to manifest themselves at runtime through some kind of exception mechanism rather than spontaneous crashes.

With that in mind here's a possible solution:

Within A's constructor, allocate some kind of signal object S which is set when A is destroyed. Handle S using a shared_ptr. Have A::handle return a custom handle class H which contains a B handle, and a shared_ptr to S. Create a dereference operator within H which verifies that A is still valid (S is not set), or throws an exception. When all handles expire, S will be destroyed automatically.

Upvotes: 1

brewbuck
brewbuck

Reputation: 897

You want object A to produce another object of class B, let somebody use it, then ensure that B is destroyed before A?

Rather than return an instance of B, would it be possible to define a method on A which obtains the B and then passes it to some kind of delegate (virtual method, functor, lambda function)? This way the user function is nested inside a call to a method on the A object, so it's logically impossible for A to be destroyed before the user code has finished whatever it is doing.

For instance:

class A { public: template <typename Functor> void DoSomethingWithAnInstanceOfB(const char* whichB, Functor f) { B& bref = InternalLookupB(whichB); f(bref); } };

This looks up the correct B instance and then passes it to an arbitrary functor. The functor can do whatever it wants, but it necessarily must return before DoSomethingWithAnInstanceOfB() will return, therefore guaranteeing that A's lifetime is at least as long as B.

Upvotes: 0

Related Questions