Opened 15 years ago

Closed 9 years ago

#1109 closed Feature Requests (fixed)

intrusive_ptr needs a helper base class

Reported by: Peter Dimov Owned by: Peter Dimov
Milestone: To Be Determined Component: smart_ptr
Version: Severity: Problem
Keywords: Cc:

Description

http://lists.boost.org/Archives/boost/2006/04/103182.php http://lists.boost.org/Archives/boost/2006/04/103203.php http://lists.boost.org/Archives/boost/2006/04/103267.php http://lists.boost.org/Archives/boost/2006/04/103273.php

class counted_base
{
private:

    mutable detail::atomic_count count_;

protected:

    counted_base(): count_( 0 ) {}
    virtual ~counted_base() {}
    counted_base( counted_base const & ): count_( 0 ) {}
    counted_base& operator=( counted_base const & ) { return *this; }

public:

    inline friend void intrusive_ptr_add_ref( counted_base const * p )
    {
        ++p->count_;
    }

    inline friend void intrusive_ptr_release( counted_base const * p )
    {
        if( --p->count_ == 0 ) delete p;
    }

    long use_count() const { return count_; }
};

http://lists.boost.org/Archives/boost/2006/04/103306.php

Change History (10)

comment:1 by Peter Dimov, 15 years ago

Owner: set to Peter Dimov
Status: newassigned

comment:2 by Peter Dimov, 14 years ago

Milestone: To Be DeterminedBoost 1.37.0

comment:3 by Peter Dimov, 14 years ago

Milestone: Boost 1.37.0To Be Determined

comment:4 by Peter Dimov, 12 years ago

Status: assignednew

comment:5 by NN, 11 years ago

Virtual destructor is not necessary here:

template<typename T>
class counted_base
{
private:

    mutable detail::atomic_count count_;

protected:

    counted_base(): count_( 0 ) {}
    ~counted_base() {}
    counted_base( counted_base const & ): count_( 0 ) {}
    counted_base& operator=( counted_base const & ) { return *this; }

public:

    inline friend void intrusive_ptr_add_ref( T const * p )
    {
        ++p->counted_base<T>::count_;
    }

    inline friend void intrusive_ptr_release( T const * p )
    {
        if( --p->counted_base<T>::count_ == 0 )
            delete p;
    }

    long use_count() const { return count_; }
};

comment:6 by Olaf van der Spek <olafvdspek@…>, 11 years ago

What's the advantage of intrusive over shared when make_shared is used?

in reply to:  6 comment:7 by ArtemGr, 9 years ago

Replying to Olaf van der Spek <olafvdspek@…>:

What's the advantage of intrusive over shared when make_shared is used?

Olaf, one important advantage is that the class does not need to be fully defined,
another advantage is that you can create intrusive_ptr anywhere, you don't have to pass it around in order to have it.

comment:8 by Tomas Kislan <pinkerik88@…>, 9 years ago

I would also template the type of count_ variable, sometimes there's no need for thread-safe counter, and using long would give some performance boost

comment:9 by Jiří Havel <JSH@…>, 9 years ago

I use this base class. It is inspired by boost.intrusive and uses the example reference counter from boost.atomic.

If you want, I can improve this a bit. I think, it should be possible to select atomic/nonatomic internal counter and some checking policy (again inspired by boost.atomic).

#ifndef INTRUSIVE_PTR_BASE_HOOK_H_INCLUDED
#define INTRUSIVE_PTR_BASE_HOOK_H_INCLUDED

#include <memory>
#include <utility>

#include <boost/assert.hpp>
#include <boost/atomic.hpp>
#include <boost/intrusive_ptr.hpp>
#include <boost/parameter/binding.hpp>
#include <boost/parameter/name.hpp>
#include <boost/parameter/parameters.hpp>

namespace utils {

BOOST_PARAMETER_TEMPLATE_KEYWORD(deleter)
BOOST_PARAMETER_TEMPLATE_KEYWORD(count_type)

typedef boost::parameter::parameters<
    boost::parameter::optional<tag::deleter>,
    boost::parameter::optional<tag::count_type>
> intrusive_ptr_base_hook_signature;

template<typename Derived, typename ... Params>
class intrusive_ptr_base_hook
{
    typedef typename intrusive_ptr_base_hook_signature::bind<Params...>::type args;

protected :
    typedef Derived derived_type;

    typedef typename boost::parameter::binding<
            args, tag::deleter, std::default_delete<derived_type>
        >::type deleter_type;

    typedef typename boost::parameter::binding<
            args, tag::count_type, unsigned
        >::type count_type;

private:
    struct Data
        : public deleter_type
    {
        boost::atomic<count_type> count;

        constexpr Data(count_type c, deleter_type const & d)
            : deleter_type(d)
            , count(c)
        {}
    } m_data;

protected :
    constexpr intrusive_ptr_base_hook()
        : m_data(0, deleter_type())
    {}

    intrusive_ptr_base_hook(intrusive_ptr_base_hook const &) = delete;
    intrusive_ptr_base_hook & operator=(intrusive_ptr_base_hook const &) = delete;

    ~intrusive_ptr_base_hook() noexcept
    {
        BOOST_ASSERT(m_count.load(boost::memory_order::memory_order_relaxed) == 0);
    }

public :
    friend void intrusive_ptr_add_ref(
            intrusive_ptr_base_hook const * p)
    {
        const_cast<intrusive_ptr_base_hook*>(p)
            ->m_data.count.fetch_add(1, boost::memory_order_relaxed);
    }

    friend void intrusive_ptr_release(
            intrusive_ptr_base_hook const * pc)
    {
        intrusive_ptr_base_hook * p = const_cast<intrusive_ptr_base_hook*>(pc);
        if(p->m_data.count.fetch_sub(1, boost::memory_order_release) == 1)
        {
            boost::atomic_thread_fence(boost::memory_order_acquire);
            // call deleter hidden in m_data
            p->m_data(static_cast<derived_type*>(p));
        }
    }
};

template<typename T, typename ... Args>
inline boost::intrusive_ptr<T> make_intrusive(Args && ... args)
{
    return boost::intrusive_ptr<T>(new T(std::forward<Args>(args)...));
}

}//namespace utils

#endif//INTRUSIVE_PTR_BASE_HOOK_H_INCLUDED

comment:10 by Peter Dimov, 9 years ago

Resolution: fixed
Status: newclosed

There's boost/smart_ptr/intrusive_ref_counter.hpp now.

Note: See TracTickets for help on using tickets.