// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Authors:
 *   MenTaLguY <mental@rydia.net>
 * * Copyright (C) 2004 MenTaLguY
 *
 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
 */

#ifndef SEEN_INKSCAPE_GC_ANCHORED_H
#define SEEN_INKSCAPE_GC_ANCHORED_H

#include "inkgc/gc-managed.h"

namespace Inkscape::GC {

/**
 * A base class for anchored objects.  
 *
 * Objects are managed by our mark-and-sweep collector, but are anchored 
 * against garbage collection so long as their reference count is nonzero.
 *
 * Object and member destructors will not be called on destruction
 * unless a subclass also inherits from Inkscape::GC::Finalized.
 *
 * New instances of anchored objects should be created using the C++ new
 * operator.  Under normal circumstances they should not be created on
 * the stack.
 *
 * A newly created anchored object begins with a refcount of one, and
 * will not be collected unless the refcount is zero.
 *
 * NOTE: If you create an object yourself, it is already anchored for
 *       you.  You do not need to anchor it a second time.
 *
 * Note that a cycle involving an anchored object (with nonzero refcount)
 * cannot be collected.  To avoid this, don't increment refcounts for
 * pointers between two GC-managed objects.
 *
 * @see Inkscape::GC::Managed
 * @see Inkscape::GC::Finalized
 * @see Inkscape::GC::anchor
 * @see Inkscape::GC::release
 */

class Anchored {
public:
    void anchor() const;
    void release() const;

    // for debugging
    unsigned _anchored_refcount() const {
        return ( _anchor ? _anchor->refcount : 0 );
    }

    Anchored(Anchored const &) = delete; // no copy
    void operator=(Anchored const &) = delete; // no assign

protected:
    Anchored() : _anchor(nullptr) { anchor(); } // initial refcount of one
    virtual ~Anchored() = default;

private:
    struct Anchor : public Managed<SCANNED, MANUAL> {
        Anchor() : refcount(0),base(nullptr) {}
        Anchor(Anchored const *obj) : refcount(0) {
            base = Core::base(const_cast<Anchored *>(obj));
        }
        int refcount;
        void *base;
    };

    mutable Anchor *_anchor;

    Anchor *_new_anchor() const;
    void _free_anchor(Anchor *anchor) const;
};

/**
 * @brief Increments the reference count of a anchored object.
 *
 * This function template generates functions which take
 * a reference to a anchored object of a given type, increment
 * that object's reference count, and return a reference to the
 * object of the same type as the function's parameter.
 *
 * @param m a reference to a anchored object
 *
 * @return the reference to the object
 */
template <typename R>
static R &anchor(R &r) {
    static_cast<Anchored const &>(const_cast<R const &>(r)).anchor();
    return r;
}

/**
 * @brief Increments the reference count of a anchored object.
 *
 * This function template generates functions which take
 * a pointer to a anchored object of a given type, increment
 * that object's reference count, and return a pointer to the
 * object of the same type as the function's parameter.
 *
 * @param m a pointer to anchored object
 *
 * @return the pointer to the object
 */
template <typename R>
static R *anchor(R *r) {
    static_cast<Anchored const *>(const_cast<R const *>(r))->anchor();
    return r;
}

/**
 * @brief Decrements the reference count of a anchored object.
 *
 * This function template generates functions which take
 * a reference to a anchored object of a given type, decrement
 * that object's reference count, and return a reference to the
 * object of the same type as the function's parameter.
 *
 * The return value is safe to use since the object, even if
 * its refcount has reached zero, will not actually be collected
 * until there are no references to it in local variables or
 * parameters.
 *
 * @param m a reference to a anchored object
 *
 * @return the reference to the object
 */
template <typename R>
static R &release(R &r) {
    static_cast<Anchored const &>(const_cast<R const &>(r)).release();
    return r;
}

/**
 * @brief Decrements the reference count of a anchored object.
 *
 * This function template generates functions which take
 * a pointer to a anchored object of a given type, decrement
 * that object's reference count, and return a pointer to the
 * object of the same type as the function's parameter.
 *
 * The return value is safe to use since the object, even if
 * its refcount has reached zero, will not actually be collected
 * until there are no references to it in local variables or
 * parameters.
 *
 * @param m a pointer to a anchored object
 *
 * @return the pointer to the object
 */
template <typename R>
static R *release(R *r) {
    static_cast<Anchored const *>(const_cast<R const *>(r))->release();
    return r;
}

} // namespace Inkscape::GC

#endif // SEEN_INKSCAPE_GC_ANCHORED_H

/*
  Local Variables:
  mode:c++
  c-file-style:"stroustrup"
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
  indent-tabs-mode:nil
  fill-column:99
  End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
