Giter Site home page Giter Site logo

Comments (6)

lhmouse avatar lhmouse commented on May 25, 2024

Hi,

a) the implementation basically requires atomic variables. However, there are usecases where one wants intrusive_ptr but not including atomic operations, for example because they pull in extra library dependencies on some embedded architectures or because they make the operation slower (memory barriers) although in the specific design the MT-safety might be not required.

Good question. I am aware of that sometimes people don't want reference counts with atomicity just because there could be no more than one thread - for example when writing single-threaded programs. Basically, in order to make reference count increment/decrement operations overrideable I suggest use of argument dependent lookup (you know it, no?). The default version is used only in absence of user-provided ones - just like std::swap.

b) I don't see the point of intrusive_weak_ptr. Maybe it's just missing documentation?

intrusive_weak_ptr is to intrusive_ptr what weak_ptr is to shared_ptr. It is as simple as that. Saying you have an object of type A holding an intrusive_ptr to an object B, which in turn holds an intrusive_ptr to A, you end up with a circular dependency. intrusive_weak_ptr is there to break such dependencies. intrusive_ptr asserts ownership, and intrusive_weak_ptr disclaims ownership. You .lock() an intrusive_weak_ptr to get an intrusive_ptr which is potentially null (a null intrusive_ptr is always empty. This is not the case for shared_ptr, whose null-ness and emptiness are orthogonal).

Trivia

It should be noted that, when constructing a weak_ptr from a shared_ptr, the control block containing reference counters (there are typically two reference counters: The strong reference count is what will be returned by use_count() and the weak reference count is used to constrain the lifetime of the control block itself) will have already been allocated by either make_shared() or the constructor of the shared_ptr, so the entire construction merely increments the weak reference count hence throws no exceptions. This is not the case for intrusive_weak_ptr, which allocates the control block in its constructor. The possibility of exceptions can however be eliminated by calling .reserve_weak() on an object before it is assigned to a intrusive_weak_ptr.

Despite this flaw, copying an intrusive_ptr increments the strong reference count only, in contrast to copying a shared_ptr which increments both. Another advantage of intrusive_ptr is that, if you don't use intrusive_weak_ptr, you don't pay for the overheads brought by the weak reference count, which is not possible in case of shared_ptr.

from intrusive_ptr.

lhmouse avatar lhmouse commented on May 25, 2024

There are some considerations allowing incrementing/decrementing reference counts using user-defined manipulators:

namespace my {

class base : public std::intrusive_base<base> {
protected:
    virtual ~base(){ }
};
class derived : private base { // Use private inheritance.
public:
    ~derived() override { }
public:
    std::intrusive_ptr<base> get_base_ptr(){
        return std::intrusive_ptr<base>(this); // Okay.
    }
};

// Returns `true` if the reference count was not zero and has been incremented.
// Returns `false` if the reference count was zero.
bool try_add_ref(base &b){
    return b.std::intrusive_base<base>::try_add_ref();
}
// The behavior is undefined if the reference count was zero.
void add_ref(base &b){
    b.std::intrusive_base<base>::add_ref();
}
// Decrements the reference count.
// Returns `true` if the reference count has been decremented to zero.
// Returns `false` otherwise.
// The behavior is undefined if the reference count was zero.
bool drop_ref(base &b){
    return p.std::intrusive_base<base>::drop_ref();
}

} // namespace my

void check(){
    std::intrusive_ptr<my::derived> pd = std::make_intrusive<my::derived>();
    std::intrusive_ptr<my::base> pb = pd->get_base_ptr(); // Okay.
    pb.reset(); // Maybe this can use ADL to decrement the reference count of
                // an object of type `my::base`...
    pd.reset(); // ... but how about this? `my::base` is a PRIVATE base hence inaccessible here.
}

from intrusive_ptr.

lhmouse avatar lhmouse commented on May 25, 2024

Another solution to a) would be using virtual add_ref(), try_add_ref() and drop_ref() functions. Virtual functions are nothing better than atomic counters essentially .

from intrusive_ptr.

Code7R avatar Code7R commented on May 25, 2024

Well, thanks for commenting. Indeed, I have not looked deep enough and I overlooked the part about weak_view. So this basically means that you recreated the conditions for weak_ptr by storing the control block optionally in a separate object allocated on stack.

Not sure I like this decision - for a generic solution it's probably ok but the user should be aware of the additional costs of this implementation. Because, I said, one usually looks for intrusive_ptr for performance reasons.
Regarding the addition of alternative behavior - yes, virtual calls might be a way to get this. But then again, it shifts costs to runtime which becomes a questionable thing. And in overall, looking at the code size and complexity, I am slightly reluctant to any of the proposals.

So, thanks for your work! But sorry, I will stay with my own little intrusive pointer implementation for now, which appears more pragmatic for the mentioned usecase.

from intrusive_ptr.

lhmouse avatar lhmouse commented on May 25, 2024

Well, thanks for commenting. Indeed, I have not looked deep enough and I overlooked the part about weak_view. So this basically means that you recreated the conditions for weak_ptr by storing the control block optionally in a separate object allocated on stack.

It is not 'allocated on stack'. It is dynamic.

Not sure I like this decision - for a generic solution it's probably ok but the user should be aware of the additional costs of this implementation. Because, I said, one usually looks for intrusive_ptr for performance reasons.

Indeed. Elimination of separated allocation of the object and its control block is what people benefit from make_shared. Well, intrusive_ptr attracts people who don't make use of weak semantics.

Regarding the addition of alternative behavior - yes, virtual calls might be a way to get this. But then again, it shifts costs to runtime which becomes a questionable thing. And in overall, looking at the code size and complexity, I am slightly reluctant to any of the proposals.

More than that, if a non-template class has a virtual function (_Impl_intrusive_ptr::_Ref_count_base), we must provide an out-of-line definition of its RTTI, otherwise dynamic_cast'ing across dynamic library boundaries might fail, which prevents this library from being header-only.

So, thanks for your work! But sorry, I will stay with my own little intrusive pointer implementation for now, which appears more pragmatic for the mentioned usecase.

You are welcome. 😆

from intrusive_ptr.

lhmouse avatar lhmouse commented on May 25, 2024

I am closing this issue for now. Please open an new issue for any further discussion.

from intrusive_ptr.

Related Issues (6)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.