#75 SharedPtr's supplement - SharedArray

open
nobody
None
5
7 days ago
2008-06-16
Jiang
No

The attached class SharedArray is a supplement class for Poco::SharedPtr. It can be used for dynamically allocated array.

Several points:

  1. delete [] expression used, of course.

  2. add operator overloading for "[]", and remove the overloading for "->", since it is seldom useful.
    -> itself is overloaded by const/non-const version, together with "deref" handlers.

  3. remove the safe/unsafe cast, for the same reason.

  4. add operator "()", which simply return isNull, since we have operator "!()".

  5. include "SharedPtr.h" to shared the definition of reference counter class.

  6. keep the <, <=, >, >= family operator overloadings, since I am not sure they are necessary or not.
    -> well, IMMO they should be removed from the public interface.

What do you think about this issue?

This class is a quick&dirty hack of SharedPtr, it passed the following test.


//
// SharedArray.h
//
// $Id: //poco/svn/Foundation/include/Poco/SharedArray.h#2 $
//
// Library: Foundation
// Package: Core
// Module: SharedArray
//
// Definition of the SharedArray template class.
//
// Copyright (c) 2005-2008, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
//
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//

ifndef Foundation_SharedArray_INCLUDED

define Foundation_SharedArray_INCLUDED

include "Poco/SharedPtr.h"

namespace Poco {

template <class C,="" class="" RC="ReferenceCounter">
class SharedArray
/// SharedArray is a "smart" pointer for classes implementing
/// reference counting based garbage collection.
/// SharedArray is thus similar to SharedPtr. Unlike the
/// SharedPtr template, which can only be used with
/// single object pointer management, SharedArray
/// can be used with dynamically allocated array (Dynamically allocated
/// array are allocated with the C++ new[] expression).
///
/// SharedArray works as SharedPtr but using delete[] expression to
/// destroy the array object.
{
public:
SharedArray(): _pCounter(new RC), _ptr(0)
{
}

SharedArray(C* ptr): _pCounter(new RC), _ptr(ptr)
{
}

template <class Other> 
SharedArray(const SharedArray<Other, RC>& ptr): _pCounter(ptr._pCounter), _ptr(const_cast<Other*>(ptr.get()))
{
    _pCounter->duplicate();
}

SharedArray(const SharedArray& ptr): _pCounter(ptr._pCounter), _ptr(ptr._ptr)
{
    _pCounter->duplicate();
}

~SharedArray()
{
    release();
}

SharedArray& assign(C* ptr)
{
    if (get() != ptr)
    {
        RC* pTmp = new RC;
        release();
        _pCounter = pTmp;
        _ptr = ptr;
    }
    return *this;
}

SharedArray& assign(const SharedArray& ptr)
{
    if (&ptr != this)
    {
        SharedArray tmp(ptr);
        swap(tmp);
    }
    return *this;
}

template <class Other>
SharedArray& assign(const SharedArray<Other, RC>& ptr)
{
    if (ptr.get() != _ptr)
    {
        SharedArray tmp(ptr);
        swap(tmp);
    }
    return *this;
}

SharedArray& operator = (C* ptr)
{
    return assign(ptr);
}

SharedArray& operator = (const SharedArray& ptr)
{
    return assign(ptr);
}

template <class Other>
SharedArray& operator = (const SharedArray<Other, RC>& ptr)
{
    return assign<Other>(ptr);
}

void swap(SharedArray& ptr)
{
    std::swap(_ptr, ptr._ptr);
    std::swap(_pCounter, ptr._pCounter);
}

C& operator [] (std::ptrdiff_t index) 
{
    return deref(index);
}

const C& operator [] (std::ptrdiff_t index) const
{
    return deref(index);
}

C* get()
{
    return _ptr;
}

const C* get() const
{
    return _ptr;
}

operator C* ()
{
    return _ptr;
}

operator const C* () const
{
    return _ptr;
}

operator bool () const
{
    return isNull();
}

bool operator ! () const
{
    return _ptr == 0;
}

bool isNull() const
{
    return _ptr == 0;
}

bool operator == (const SharedArray& ptr) const
{
    return get() == ptr.get();
}

bool operator == (const C* ptr) const
{
    return get() == ptr;
}

bool operator == (C* ptr) const
{
    return get() == ptr;
}

bool operator != (const SharedArray& ptr) const
{
    return get() != ptr.get();
}

bool operator != (const C* ptr) const
{
    return get() != ptr;
}

bool operator != (C* ptr) const
{
    return get() != ptr;
}

bool operator < (const SharedArray& ptr) const
{
    return get() < ptr.get();
}

bool operator < (const C* ptr) const
{
    return get() < ptr;
}

bool operator < (C* ptr) const
{
    return get() < ptr;
}

bool operator <= (const SharedArray& ptr) const
{
    return get() <= ptr.get();
}

bool operator <= (const C* ptr) const
{
    return get() <= ptr;
}

bool operator <= (C* ptr) const
{
    return get() <= ptr;
}

bool operator > (const SharedArray& ptr) const
{
    return get() > ptr.get();
}

bool operator > (const C* ptr) const
{
    return get() > ptr;
}

bool operator > (C* ptr) const
{
    return get() > ptr;
}

bool operator >= (const SharedArray& ptr) const
{
    return get() >= ptr.get();
}

bool operator >= (const C* ptr) const
{
    return get() >= ptr;
}

bool operator >= (C* ptr) const
{
    return get() >= ptr;
}

private:
C& deref(std::ptrdiff_t index)
{
if (!_ptr)
throw NullPointerException();

    return _ptr[index];
}

const C& deref(std::ptrdiff_t index) const
{
    if (!_ptr)
        throw NullPointerException();

    return _ptr[index];
}

void release()
{
    poco_assert_dbg (_pCounter);
    int i = _pCounter->release();
    if (i == 0)
    {
        if (_ptr)
            delete [] _ptr;
        _ptr = 0;

        delete _pCounter;
        _pCounter = 0;
    }
}

SharedArray(RC* pCounter, C* ptr): _pCounter(pCounter), _ptr(ptr)
    /// for cast operation
{
    poco_assert_dbg (_pCounter);
    _pCounter->duplicate();
}

private:
RC _pCounter;
C
_ptr;

template <class OtherC, class OtherRC> friend class SharedArray;

};

template <class C,="" class="" RC="">
inline void swap(SharedArray<C, RC="">& p1, SharedArray<C, RC="">& p2)
{
p1.swap(p2);
}

} // namespace Poco

endif // Foundation_SharedArray_INCLUDED


---------- tested with gcc 4.3 (cygwin)-----------
//
// SharedArrayTest.cpp
//
// $Id: //poco/1.3/Foundation/testsuite/src/SharedArrayTest.cpp#1 $
//
// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
//
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//

include "SharedArray.h"

include <assert.h>

using Poco::SharedArray;
using Poco::NullPointerException;

namespace
{
class TestObject
{
public:
TestObject(const int& data = 0) : _data(data)
{
++_count;
std::cout << "current counter : " << _count << "\n";
}

    virtual ~TestObject()
    {
        --_count;
        std::cout << "current counter : " << _count << "\n"; 
    }

    void set(const int& data)
    {
        _data = data;
    }

    int& data()
    {
        return _data;
    }

    static int count()
    {
        return _count;
    }

private:
    int _data;
    static int _count;
};

int TestObject::_count = 0;

}

struct SharedArrayTest
{

void testSharedArray()
{
    SharedArray<TestObject> ptr1;
    poco_assert(ptr1.get() == 0);
    TestObject* pTO1 = new TestObject[10];
    for(int i = 0; i < 10; i++){
        pTO1[i].set(i);
    }

    ptr1 = pTO1;
    assert (ptr1.get() == pTO1);
    assert (ptr1 == pTO1);
    for(int i = 0; i< 10; i++){
        assert(ptr1[i].data() == i);
    }

    TestObject* pTO2 = new TestObject[20];
    for(int i = 20; i > 0; i--){
        pTO2[i].set(i);
    }
    SharedArray<TestObject> ptr2 = pTO2;
    SharedArray<TestObject> ptr3 = ptr1;
    SharedArray<TestObject> ptr4;
    assert (ptr1.get() == pTO1);
    assert (ptr1 == pTO1);
    assert (ptr2.get() == pTO2);
    assert (ptr2 == pTO2);
    assert (ptr3.get() == pTO1);
    assert (ptr3 == pTO1);

    assert (ptr1 == pTO1);
    assert (ptr1 != pTO2);
    assert (ptr1 < pTO2);
    assert (ptr1 <= pTO2);
    assert (ptr2 > pTO1);
    assert (ptr2 >= pTO1);

    assert (ptr1 == ptr3);
    assert (ptr1 != ptr2);
    assert (ptr1 < ptr2);
    assert (ptr1 <= ptr2);
    assert (ptr2 > ptr1);
    assert (ptr2 >= ptr1);

    ptr1.swap(ptr2);
    assert (ptr2 < ptr1);
    ptr2.swap(ptr1);

    try
    {
        assert (ptr4[0].data() == 1);
        assert (!"must throw NullPointerException");
    }
    catch (NullPointerException&)
    {
    }

    assert (!(ptr4 == ptr1));
    assert (!(ptr4 == ptr2));
    assert (ptr4 != ptr1);
    assert (ptr4 != ptr2);

    ptr4 = ptr2;
    assert (ptr4 == ptr2);
    assert (!(ptr4 != ptr2));

    assert (TestObject::count() == 30);
    ptr1 = 0;
    ptr2 = 0;
    ptr3 = 0;
    ptr4 = 0;
    assert (TestObject::count() == 0);

    {
        SharedArray<TestObject> ptr = new TestObject[2];
        assert (TestObject::count() == 2);
    }
    assert (TestObject::count() == 0);

}

};

int main()
{

SharedArrayTest test;
test.testSharedArray();

}

Discussion

  • Jiang
    Jiang
    2008-06-16

     
    Attachments
  • Alex Fabijanic
    Alex Fabijanic
    2008-06-23

    Logged In: YES
    user_id=1001095
    Originator: NO

    What do you think about this issue?

    It certainly is one way to address the concern. Another way is to provide a deleter function to SharedPtr like boost::shared_ptr does. I'd like to let this brew for a while and hear more opinions on this before we accept it in the trunk.

    In the meantime, if you'd like repository write access to be able to patch things yourself and play in the sandbox, email me.

    Alex

     
  • Jiang
    Jiang
    2008-06-24

    Logged In: YES
    user_id=2096293
    Originator: YES

    Hey Alex

    Yes, add a deleter to the Poco::SharedPtr is one solution for the memory management.
    The problem is, well, how to simulate the naive c-style array semantics as much as possible,
    while keeping the semantics of Poco::SharedPtr.

    To list some issues:

    • For array element access, we need "operator []", but add this operator
      to Poco::SharedPtr is meaningless.

    • Compared with Poco::SharedPtr , the interface size of ShareArray should be
      smaller.

    • Since array is not an lvalue, assignment is not allowed. Also it shall not be
      used as return value.
      -> We can remove this limitation iif this behavior won't confuse the users.
      -> boost::shared_array allows assignment, but the rhs array will be destroyed.
      Frankly speaking this is strange&danger in my mind.

    As I said in my last post, the previous proposal is a hack and need be polished. I will find time rewrite this class again and
    compared it with the deleter-enabled Poco::SharedPtr. After that, we can discuss whether put it to sandbox or not.
    Thank you for your time.

    Jiang

     
  • Peter Schojer
    Peter Schojer
    2008-06-24

    Logged In: YES
    user_id=2084842
    Originator: NO

    Why not use the Poco Buffer class? I think it provides the interface you need.

    br
    Peter

     
  • Peter Schojer
    Peter Schojer
    2008-06-24

    Logged In: YES
    user_id=2084842
    Originator: NO

    Sorry,
    now I see what you mean:
    you need arrays plus refcounting.
    So essentially some kind of buffer object that extends from RefCountedObject
    or the SharedPtr approach you presented.
    Well,
    also not sure about how we should handle shared arrays, so I agree with Alex,
    let it mature in the sandbox and wait for more feedback.

    br
    peter

     
  • Jiang
    Jiang
    2008-06-24

    Logged In: YES
    user_id=2096293
    Originator: YES

    Hey, Peter

    Thank for the pointer for Poco::Buffer.
    Actually I did not know it uses dyanmic allocation.
    I thought it was a std::tr1::array like static buffer class.

    Well, the Poco::Buffer is almost exact what I want(RAII), but
    except the refcounting issue, another issue is,
    Poco::Buffer is it not copyable and assignable, which makes
    the buffer safe. However it can not be used together with stl
    containers anymore.
    * your post does remind me that the assignment for the
    so called SharedArray is a must.

    As I said just now, I will dig further for this class and
    the following methods will be examined:

    1. Poco::SharedArray proposed (need further polish).
    2. Deleter-equipped Poco::SharedPtr
    3. Combination of std::vector and Poco::SharedPtr

    I will post the result here and hope we can find the best
    solution suitable for POCO.

    Thank you for your time.

    Jiang

     
  • Logged In: NO

    Jiang,

    If I may suggest, it would be more productive to have the code in a subversion sandbox project because it makes it much easier to download and experiment with it. Alternatively, you could attach a ready-to-use archive to your posts here. Reading long chunks of code from a web page makes it harder to properly evaluate things. Copy-pasting, on the other side, requires extra effort and can detract a potential evaluator of your code. Again, if you want SVN write access to create your own sandbox project there, let me know.

    Alex

     
  • Jiang
    Jiang
    2008-08-26

    Logged In: YES
    user_id=2096293
    Originator: YES

    Alex & Guenter

    As you know I am trying to add SharedArray and WeakPtr support to to POCO's smart pointer,
    however I have a question about the original Poco::SharedPtr's memory management, hope you or others can elaborate on it.

    The original Poco::SharedPtr's constructors use dynamical allocation for reference counting object's construction.

    SharedPtr(): _pCounter(new RC), _ptr(0)
    {
    }
        /// ... 
    RC* _pCounter;
    

    Here may I know the rationale why use the dynamical allocation instead of automatic storage?

    I ask because :

    1. According to the TR1::shared_ptr::shard_ptr(), no exception shall be thrown.

    2. I am worrying about the efficiency issues here. For some use cases (construct/destruct SharedPtr objects in tight loop, etc.. ), dynamical allocation/deallocation hurt.

    3. For WeakPtr' expired()/lock() interface, it is not easy to check the underlying SharedPtr's state if the reference counting object was deleted.

    Actually maybe TR1's specification is not really an big issue, and I managed to implement the dynamical allocation based deleter improvement for SharedPtr/SharedArray, but I am stuck in the WeakPtr since I could not find a good way to implement the lock interface.

    BTW, would you please have a look at the new SharedPtr/SharedArray in sandbox? Tell me if you feel something strange or need improvement.

    Also I planned to add the so-called AutoArray, but finally I found it is neither necessary (We have a similar class Poco::Buffer) nor possible (For the release() issue). I will remove AutoArray's source tree from sandbox later.

    Thank you for your time.

    Jiang

     
  • Alex Fabijanic
    Alex Fabijanic
    2008-08-26

    Logged In: YES
    user_id=1001095
    Originator: NO

    Jiang,

    Both Guenter and I are on vacation. We'll look into this when we're back. In the meantime, whoever has opinion is welcome to speak.

    Alex

     
  • Poco::SharedArray (with semantics comparable to boost::shared_array) would be great

     
  • We did plan to add this construct to Poco.Foundation, actually a test class called Poco.SharedArray was added in Poco's sandbox several months ago (though not quite usable). However, since then the Poco::SharedPtr's interface/policy changed a little so I need check it again (together with the reference counting issue). And, this issue can also be solved by adding array management policy to SharedPtr.

    In a word, we need some time to settle down about this issue.

    Regards,

     
  • Alex Fabijanic
    Alex Fabijanic
    2009-05-13

    SharedArrayPolicy is in the trunk. It did not make it into 1.3.5, though.

     
  • Jiang
    Jiang
    2009-05-13

    As Alex just explained, ReleaseArrayPolicy had already been added to SharedPtr in trunk. I did not notice that, sorry for the possible confusion I've made.

    Though it was not merged into 1.3.5, I think you can do it by yourself, well, if you really want to use it. And you can use it as :

    class foo
    {};

    typedef Poco::SharedPtr<foo, Poco::ReferenceCounter,="" Poco::ReleaseArrayPolicy<foo=""> > SharedFooArray;

    SharedFooArray pA = new foo[1024];
    // ...

    BTW, since Poco.SharedPtr has its own semantics and interfaces, which are different from tr1::shared_ptr or boost.shared_ptr. Please do check the document for details.

    Regards,