-1

I'm wrestling with a design choice question here.

I've got a class that needs a couple of semaphores. Semaphores are non-movable objects. Objects of this class however need to go into a vector (there's one per thread for this process). I'm pretty sure the Right Thing™ to do here is put the semaphores in some kind of managed pointer object, and put that in the class. So far so good.

Ultimately there will be only one of each of these semaphores, although the container might need to do some moving. So theoretically, std::unique_ptr seems like the Right Thing™ here. However, if I do that I'm going to need to manually write all the move constructors for this class they go in. That in my experience can be tricky code to get right, and is brittle to class changes. We'd now "own" the code for all the move constructors, and have to keep them up to date and correctly coded now and forever.

On the other hand, if I use std::shared_ptr, it will all just work. No extra code required. However, that smart pointer class is meant for use in situations where more than one user might be holding the pointer at once. That's not going to be the case here, so its arguably misleading, and there could well be some unnecessary internal overhead.

I was told it would be clearer with code (not so sure about that), but here's roughly what it looks like now, and works fine with:

class task_interface {
public:
    explicit task_interface(std::string logical_name) :
        go_semaphore(open_or_create (logical_name + "_go_semaphore")),
        done_semaphore(open_or_create(logical_name + "_done_semaphore")) {}
   
private:
    std::shared_ptr <some_custom_movable_semaphore_type> go_semaphore;
    std::shared_ptr <some_custom_movable_semaphore_type> done_semaphore;
...    
}

If I try to use std::unique_ptr, since task_interface objects are getting put in a container class, this won't compile unless I also provide the required move constructors. But with std::shared_ptr it works fine with no extra code required.

So basically, I've got the design goal of keeping our code simpler butting up against the goal of avoiding using a class for something its not designed for.

Given how difficult it could be to track down issues in this code (since its being used in a multi-thereaded environment), I'm inclined to use the shared_ptr. Am I off-base here? This will probably get reviewed, and I'd like to make sure I at least have a decent argument.

5
  • 2
    There are some assumptions in this post that I don't quite follow. Perhaps illustrating with code snippets would help? (1) Do you need one set of semaphores per class? If so, would static variables be an alternatives? Or do you want to avoid that due to initialization order issues? (2) Why would unique_ptr require you to write manual move constructors? You must follow the rule of zero/three/five, but typically you'd either follow the rule of zero and let the compiler implicitly define all necessary ctors, or = default the ctor.
    – amon
    Mar 29 at 20:59
  • @amon - Nope, each object of the class needs its own pair of semaphores, separate from other objects of that class.
    – T.E.D.
    Mar 29 at 22:06
  • What will happen if one thread locks the semaphore and does something with the object, while another thread adds some items to the vector, causing it to be reallocated, causing the object to be moved?
    – user253751
    Mar 30 at 9:27
  • @user253751 - Not a concern in this case, since the code that's building the vector is single-threaded, and no client accesses happen until its completely built. However, once its built, it will get accessed multi-threaded (and yes, that means the semaphores get used by different threads than the one that created them).
    – T.E.D.
    Mar 30 at 19:28
  • @T.E.D. Then maybe you can create a "fake movable semaphore" type, and put it directly in the objects, so the CPU doesn't waste time chasing pointers. It doesn't have to actually move the semaphore, since you aren't using the semaphore when you move it, and you don't move it when you use it - it can just destroy and create a new one (while "pretending" to move)
    – user253751
    Mar 30 at 20:44

1 Answer 1

1

You have a misconception about how special member functions are generated when they aren't declared.

Having a data member of type std::unique_ptr<Foo> means that you have implicitly deleted copy constructor / assignment, and the implicitly default move constructor / assignment does member-wise moves.

std::vector will use moves during reallocations if its element's move constructor is noexcept, which it will be unless you have some other data member with a throwing move constructor.

13
  • Hmm. I'll give it a shot. I think part of my problem is that what got me here was an earlier version that didn't use pointers at all but overrode the move constructor was failing in a manner that strongly implied the move constructors weren't working properly (only the last-constructed semaphore was firing). So I'm a bit gun-shy about relying on them.
    – T.E.D.
    Mar 30 at 13:35
  • 1
    @T.E.D. there's a world of difference between a moveable but uncopiable type and an unmoveable type
    – Caleth
    Mar 30 at 13:37
  • We have an older compiler that allows semaphores to be moved (with a compiler warning). I inherited this code of course. I feel better getting rid of the warnings anyway (Right Thing and all), but what it was doing before should have been working.
    – T.E.D.
    Mar 30 at 13:43
  • Well, I went and tried changing the shared_ptr to unique_ptr, and its definitely making some of the enclosing class' constructors used somewhere in the code private (and then throwing compile errors, of course). So misconceptions or no, the issue remains that I'd have to manually write constructors if I used std::unique_ptr.
    – T.E.D.
    Mar 30 at 19:03
  • @T.E.D. you will have to move things, not copy them, when you are moving. Don't write a "copy" constructor that moves
    – Caleth
    Mar 30 at 19:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.