0

I have an object pool that holds objects in std::unique_ptr<T>.

The pool, like most pools, holds objects that are expensive to create, but once an object is borrowed there is no way of preventing an object with the same settings from being added to the pool so I'd like to extend it to prevent that from happening.

I see 3 options for this:

  1. Replace std::unique_ptr<T> with std::shared_ptr<T> but there's no plans to have multithreaded code and this breaks the convention of ownership - the pool should not own an object just to read one of its properties.

  2. Keep a copy of a property of T with the pool in a std::vector, adding and removing when std::unique_ptr<T> is borrowed from and returned to the pool. Easy to implement but feels wrong as I'm duplicating data.

  3. Use a std::vector<std::reference_wrapper<std::unique_ptr<T>>>. By keeping a reference to borrowed std::unique_ptr<T> I have access to its properties and I can easily prevent objects with the same settings from being created.

I'm currently trying to implement 3 but am stuck on how to add a reference to std::unique_ptr<T> to std::vector<std::reference_wrapper<std::unique_ptr<T>>>. Is this even possible?

ruipacheco
  • 15,025
  • 19
  • 82
  • 138
  • If the object is always to return to the pool, it seem strange to move around a `std::unique_ptr` at all. Why not pass around a plain pointer and let the pool just hold all the objects? Using a reference wrapper to a unique_ptr and you gain what? – super Aug 27 '20 at 07:59
  • That's also a solution. – ruipacheco Aug 27 '20 at 08:02
  • About ownership semantics: the pool can hand out a `std::shared_ptr` and hold onto a `std::weak_ptr`. – Quentin Aug 27 '20 at 08:11
  • @Quentin Nice idea. But if the pool stores only `weak_ptr`, they will be stale directly after addition... – nop666 Aug 27 '20 at 08:26
  • Haven't you considered `weak_ptr`? And about your question, yes you can do it, see https://godbolt.org/z/PKqYe4. Why do think the third option is the best one? At the point where you return `unique_ptr` to the client code you already have dangling `reference_wrapper`, because `unique_ptr` are always moved and not copied. You program will compile, but be incorrect. If the objects upon deletion gets returned to the pool, using raw pointers instread of `reference_wrapper` to `unique_ptr` is also an option. – nicolai Aug 27 '20 at 08:29
  • 1
    "*the pool should not own an object*" - that statement makes no sense. The pool of course owns its objects and controls their lifetime. A borrowed object can be represented simply with a raw pointer, because it's guaranteed to exist until the object is returned to the pool. – rustyx Aug 27 '20 at 08:32
  • @nop666 why would they be? The caller holds a `std::shared_ptr` that keeps them alive. – Quentin Aug 27 '20 at 08:35
  • @ruipacheco, personally if I had to choose only from the three options you listed and if the size of "property of T" was comparable with the size of a pointer, I would go with the second option. – nicolai Aug 27 '20 at 08:35
  • If your question just how to implement number 3? Or whether to implement number 3? Or whether there are better alternatives you haven't thought of? – Useless Aug 27 '20 at 08:50
  • @Quentin Depends on what's the goal (clear separation between provider/consumer etc.). If a provider populate the pool with `add`, there could be no need for the provider to keep a reference. Same idea by the way for the consumers, that could not have to return ownership explicitly after the resource usage depending on the requirements... – nop666 Aug 27 '20 at 08:53
  • @nop666 We might not have the same idea of what an object pool should look like. I picture it working pretty much like the free store does: the caller requests an object, gets a handle to it, releases it when it's done. – Quentin Aug 27 '20 at 08:57
  • @Quentin So we both have the same idea! But I'm just trying to understand the translation of your idea with `std::weak_ptr` with the [given example](https://stackoverflow.com/questions/27827923/c-object-pool-that-provides-items-as-smart-pointers-that-are-returned-to-pool). – nop666 Aug 27 '20 at 09:11
  • 1
    @nop666 It doees not translate well, because I hadn't seen the link Now that I actually know what's up, I'd opt for having the pool keep ownership throughout via `std::shared_ptr`, since it will still be able to extend ownership to new callers. – Quentin Aug 27 '20 at 09:19

1 Answers1

1

The reference version is just bad - because as soon as the unique_ptr is moved the reference wrap gets invalid. You'd better just store raw pointers instead. But in this case it won't know whether it is destroyed or not. You can make unique_ptr with custom destructor that marks in the pool that the object is destroyed.

Besides, I believe you have a mess up in logic. If the pool holds unique_ptr and must not create objects with same settings then it should own all the objects and handle raw/observer pointers to users. You only need to write a routine that checks whether the object is in-use and inaccessible currently.

ALX23z
  • 4,456
  • 1
  • 11
  • 18