473,416 Members | 1,732 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,416 software developers and data experts.

A smart pointer and type forwarding

Hello.

I have written a simple reference-counting smart pointer class template
called RefCountPtr<T>. It works in conjunction with another class,
ReferenceCountable, which is responsible for the actual counting. Here
is the latter's definition:

// --- Begin ReferenceCountable.h ----------

class ReferenceCountable
{
public:
virtual ~ReferenceCountable() {}
void incrementReferenceCount() { count++; }
void decrementReferenceCount() { count--; }
unsigned int referenceCount() const { return count; }
protected:
ReferenceCountable() : count(0) {}
private:
unsigned int count;
};

// --- End ReferenceCountable.h ----------

Classes whose objects are to be reference-counted must derive from
ReferenceCountable. E.g.:

// --- Begin Foo.h ----------

#include "ReferenceCountable.h"

class Foo : public ReferenceCountable
{
public:
void f() {}
};

// --- End Foo.h ----------

From now on, one can create instances of the smart pointer class and
use them as regular pointers. E.g.:

RefCountPtr<Foopfoo = new Foo;
pfoo->f();

The idea of making the smart pointer behave like regular pointers is key
and is also the reason for this post. There is a situation where I have
found myself unable to make my smart pointers behave like regular ones.
It occurs when a given class holds RefCountPtr<Tas member variables.
For a regular pointer, one could do:

class Foo;

class Test
{
Foo* mFoo;
};

Therefore, I would like to be able to have:

#include "RefCountPtr.hpp"

class Foo;

class Test
{
RefCountPtr<FoomFoo;
};

But, unfortunately, the compiler does not find it sufficient to know
that Foo is a class. Here is a relevant snippet of the /RefCountPtr<T>/
class template, showing its constructors, destructor and only member
variable:

// --- Begin snippet of RefCountPtr.hpp ----------

#include "ReferenceCountable.h"

template <class T>
class RefCountPtr
{
public:
RefCountPtr()
: object(0)
{
}

RefCountPtr(T* pointer)
: object(pointer)
{
if (object)
object->incrementReferenceCount();
}

RefCountPtr(const RefCountPtr& original)
: object(original.object)
{
if (object)
object->incrementReferenceCount();
}

~RefCountPtr()
{
if (object)
{
object->decrementReferenceCount();
if (object->referenceCount() == 0)
delete object;
}
}
Nov 7 '06 #1
33 5003
Ney André de Mello Zunino wrote:
....
Error E2315 RefCountPtr.hpp 44: 'decrementReferenceCount' is not a
member of 'Foo', because the type is not yet defined in function
RefCountPtr<Foo>::~RefCountPtr()

As I understand it, upon finding the declaration of the /Test::mFoo/
member variable, the compiler goes on to instantiate the template for
the type /Foo/. Because the definition for /Foo/ has not been provided
(yet), it fails to compile the calls object->decrementReferenceCount()
and object->referenceCount().

The main point of my post is to try to figure out how I could arrange
the RefCountPtr<Tclass template so that type forwarding would suffice
when using it as a member of another class. The goal is to *reduce
dependencies* and to have the smart pointer behave like regular ones.

Note: if necessary, I can post the complete code of a simple test program.
It's important to have some example code that shows the error. BTW, the
Austria C++ smart pointer (at::Ptr<>)does all this and more so if you
want an example, go check it out.

Also, your ReferenceCountable class does dot handle copy construct and
assignment correctly and neither does your RefCountPtr.
>
Thank you,
Nov 7 '06 #2
Ney André de Mello Zunino wrote:
Hello.

I have written a simple reference-counting smart pointer class template
called RefCountPtr<T>. It works in conjunction with another class,
ReferenceCountable, which is responsible for the actual counting. Here
is the latter's definition:

// --- Begin ReferenceCountable.h ----------

class ReferenceCountable
{
public:
virtual ~ReferenceCountable() {}
void incrementReferenceCount() { count++; }
void decrementReferenceCount() { count--; }
unsigned int referenceCount() const { return count; }
protected:
ReferenceCountable() : count(0) {}
private:
unsigned int count;
};

// --- End ReferenceCountable.h ----------

Classes whose objects are to be reference-counted must derive from
ReferenceCountable. E.g.:

// --- Begin Foo.h ----------

#include "ReferenceCountable.h"

class Foo : public ReferenceCountable
{
public:
void f() {}
};

// --- End Foo.h ----------

From now on, one can create instances of the smart pointer class and
use them as regular pointers. E.g.:

RefCountPtr<Foopfoo = new Foo;
pfoo->f();

The idea of making the smart pointer behave like regular pointers is key
and is also the reason for this post. There is a situation where I have
found myself unable to make my smart pointers behave like regular ones.
It occurs when a given class holds RefCountPtr<Tas member variables.
For a regular pointer, one could do:

class Foo;

class Test
{
Foo* mFoo;
};

Therefore, I would like to be able to have:

#include "RefCountPtr.hpp"

class Foo;

class Test
{
RefCountPtr<FoomFoo;
};

But, unfortunately, the compiler does not find it sufficient to know
that Foo is a class. Here is a relevant snippet of the /RefCountPtr<T>/
class template, showing its constructors, destructor and only member
variable:

// --- Begin snippet of RefCountPtr.hpp ----------

#include "ReferenceCountable.h"

template <class T>
class RefCountPtr
{
public:
RefCountPtr()
: object(0)
{
}

RefCountPtr(T* pointer)
: object(pointer)
{
if (object)
object->incrementReferenceCount();
}

RefCountPtr(const RefCountPtr& original)
: object(original.object)
Bug: You need to decrement the reference count before reassigning.
{
if (object)
object->incrementReferenceCount();
}

~RefCountPtr()
{
if (object)
{
object->decrementReferenceCount();
if (object->referenceCount() == 0)
delete object;
}
}
.
.
.
private:
T* object;
};

// --- End snippet of RefCountPtr.hpp ----------

Here is one of the error messages output by the compiler:

Error E2315 RefCountPtr.hpp 44: 'decrementReferenceCount' is not a
member of 'Foo', because the type is not yet defined in function
RefCountPtr<Foo>::~RefCountPtr()

As I understand it, upon finding the declaration of the /Test::mFoo/
member variable, the compiler goes on to instantiate the template for
the type /Foo/. Because the definition for /Foo/ has not been provided
(yet), it fails to compile the calls object->decrementReferenceCount()
and object->referenceCount().

The main point of my post is to try to figure out how I could arrange
the RefCountPtr<Tclass template so that type forwarding would suffice
when using it as a member of another class. The goal is to *reduce
dependencies* and to have the smart pointer behave like regular ones.

Note: if necessary, I can post the complete code of a simple test program.
Several solutions come to mind:

1. *Declare* Foo fully before your use of it as a member of Test. Of
course, this doesn't aid in reducing dependencies.

2. Put the *definitions* of RefCoutPtr's member functions after all
instances of its use as a member. Absent the export keyword (cf.
http://www.parashift.com/c++-faq-lit...tml#faq-35.14), this
is ugly but not exceedingly dangerous since you'll get compile-time
errors if you mess it up.

3. Use non-intrusive reference counting such as that found in
std::tr1::shared_ptr (aka boost::shared_ptr), Loki::SmartPtr, or this
FAQ:
<http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.22>.

Cheers! --M

Nov 7 '06 #3

Ney André de Mello Zunino wrote:
The idea of making the smart pointer behave like regular pointers is key
and is also the reason for this post. There is a situation where I have
found myself unable to make my smart pointers behave like regular ones.
It occurs when a given class holds RefCountPtr<Tas member variables.
For a regular pointer, one could do:

class Foo;

class Test
{
Foo* mFoo;
};

Therefore, I would like to be able to have:

#include "RefCountPtr.hpp"

class Foo;

class Test
{
RefCountPtr<FoomFoo;
};

But, unfortunately, the compiler does not find it sufficient to know
that Foo is a class. Here is a relevant snippet of the /RefCountPtr<T>/
class template, showing its constructors, destructor and only member
variable:
You should declare a destructor in Test and put its implementation into
the compilation unit, even if it is empty (which is probably will be).
Then your compiler won't attempt to call the destructor of your
RefCountPtr class.

Note that the type does have to be complete at the point of its
deletion.

Another alternative, that I used in my final version of the intrusive
smart pointer before I did away with it, was to use a second template
parameter and use that in the destructor. RefCountPtr< T, BASE_T=T >
and now BASE_T was the base-class from which you derive and it calls
methods through that. Only BASE_T has to be complete so in Test you
would declare as

class Test
{
RefCountPtr< Foo, ReferenceCountable mFoo;
};

Even if you don't want to use it, I would suggest you look at the code
for boost::intrusive_ptr (I hope you can decipher it) just to see how
they did it.

Nov 7 '06 #4
Gianni Mariani wrote:
It's important to have some example code that shows the error. BTW, the
Austria C++ smart pointer (at::Ptr<>)does all this and more so if you
want an example, go check it out.
I will have a look at it. Thank you. And let me just clarify that the
reason I decided to create my own smart pointer class was simply as an
opportunity for improving my experience and not because existing
alternatives did not suffice.
Also, your ReferenceCountable class does dot handle copy construct and
assignment correctly and neither does your RefCountPtr.
You are right. I am infringing the "rule of three". I suppose that, in
the case of /ReferenceCountable/, I should make sure both the copy
constructor and assignment operator reset the reference count for the
new object, right? Is there anything else I should take into account?

As for /RefCountPtr<T>/, could you help me see what I am doing wrong in
the copy constructor? As for the assignment operator, I did implement
it, though I did not include the code in the original post. Here it is:

RefCountPtr& operator=(const RefCountPtr& original)
{
return operator=(original.object);
}

RefCountPtr& operator=(T* pointer)
{
// Check for self-assignment and assignment from
// equivalent smart pointer
if (this->object != pointer)
{
if (object)
{
object->decrementReferenceCount();
if (object->referenceCount() == 0)
delete object;
}
object = pointer;
if (pointer)
pointer->incrementReferenceCount();
}
return *this;
}

Is the implementation of the assignment operator for /RefCountPtr<T>/
also incorrect or incomplete?

Thank you,

--
Ney André de Mello Zunino
http://blog.zunino.eti.br/
Nov 8 '06 #5
mlimber wrote:
Ney André de Mello Zunino wrote:
> RefCountPtr(const RefCountPtr& original)
: object(original.object)

Bug: You need to decrement the reference count before reassigning.
> {
if (object)
object->incrementReferenceCount();
}
I am sorry, but I could not understand your remark. Why should I
decrement the reference count? For example, suppose I have an instance
/foo/ of type /Foo/ being managed by an instance of /RefCountPtr<Foo>/
named /pFoo1/. So far, the reference counter on the /foo/ object has
value 1. If a new /RefCountPtr<Foo>/, say /pFoo2/, is created by copy
construction from /pFoo1/, I expect both smart pointers to refer to the
same object and, therefore, its reference count should go up by 1,
becoming 2. That is why I increment the managed object's reference count
in the copy constructor. Am I wrong?
Several solutions come to mind:

1. *Declare* Foo fully before your use of it as a member of Test. Of
course, this doesn't aid in reducing dependencies.
Okay. That surely works and it's what I have been doing. But, as you
mentioned, the dependencie issue is still there and the smart pointer is
still not behaving like a regular pointer.
2. Put the *definitions* of RefCoutPtr's member functions after all
instances of its use as a member. Absent the export keyword (cf.
http://www.parashift.com/c++-faq-lit...tml#faq-35.14), this
is ugly but not exceedingly dangerous since you'll get compile-time
errors if you mess it up.
Could you show me what you mean?
3. Use non-intrusive reference counting such as that found in
std::tr1::shared_ptr (aka boost::shared_ptr), Loki::SmartPtr, or this
FAQ:
<http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.22>.
When I decided to write my own smart pointer class, I read a little
about the different approaches one can take. I remember having picked
the intrusive one because it looked easier to implement and because it
incurred little penalty on performance.

Thank you,

--
Ney André de Mello Zunino
Nov 8 '06 #6
Ney André de Mello Zunino wrote:
mlimber wrote:
Ney André de Mello Zunino wrote:
RefCountPtr(const RefCountPtr& original)
: object(original.object)
Bug: You need to decrement the reference count before reassigning.
{
if (object)
object->incrementReferenceCount();
}

I am sorry, but I could not understand your remark. Why should I
decrement the reference count? For example, suppose I have an instance
/foo/ of type /Foo/ being managed by an instance of /RefCountPtr<Foo>/
named /pFoo1/. So far, the reference counter on the /foo/ object has
value 1. If a new /RefCountPtr<Foo>/, say /pFoo2/, is created by copy
construction from /pFoo1/, I expect both smart pointers to refer to the
same object and, therefore, its reference count should go up by 1,
becoming 2. That is why I increment the managed object's reference count
in the copy constructor. Am I wrong?
Mea culpa. I was thinking of the assignment operator.
Several solutions come to mind:

1. *Declare* Foo fully before your use of it as a member of Test. Of
course, this doesn't aid in reducing dependencies.

Okay. That surely works and it's what I have been doing. But, as you
mentioned, the dependencie issue is still there and the smart pointer is
still not behaving like a regular pointer.
2. Put the *definitions* of RefCoutPtr's member functions after all
instances of its use as a member. Absent the export keyword (cf.
http://www.parashift.com/c++-faq-lit...tml#faq-35.14), this
is ugly but not exceedingly dangerous since you'll get compile-time
errors if you mess it up.

Could you show me what you mean?
Declare RefCountPtr in a header RefCountPtr.hpp (or whatever), but put
the implementation of its functions in RefCountPtr.cpp (or whatever).
Include the latter file explicitly in every .cpp file that uses
RefCountPtr but make sure it is *after* the full declarations of the
forward-declared objects. E.g.,

In Bar.hpp
#include "RefCountPtr.hpp"
class Foo;
class Bar
{
// ...
RefCountPtr<Foofoo_;
};

In Bar.cpp:
#include "Bar.hpp"
#include "Foo.hpp"
#include "RefCountPtr.cpp"
// ...
3. Use non-intrusive reference counting such as that found in
std::tr1::shared_ptr (aka boost::shared_ptr), Loki::SmartPtr, or this
FAQ:
<http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.22>.

When I decided to write my own smart pointer class, I read a little
about the different approaches one can take. I remember having picked
the intrusive one because it looked easier to implement and because it
incurred little penalty on performance.
There are certainly trade-offs to be had -- one of which is the ability
to forward-declare a class. The smart pointer in the FAQ is pretty
simple and straight-forward, though it doesn't have all the options of
shared_ptr and the like. I'm all for exercising your skills by writing
your own smart pointer, but for production code, I'd suggest going with
a tried-and-true smart pointer from the standard library, Boost, or
similar if possible. They're notoriously tricky to get right.

Cheers! --M

Nov 8 '06 #7

Ney André de Mello Zunino wrote:
>
RefCountPtr& operator=(const RefCountPtr& original)
{
return operator=(original.object);
}
RefCountPtr& operator=(T* pointer)
{
// Check for self-assignment and assignment from
// equivalent smart pointer
if (this->object != pointer)
{
stop right there!

operator= should be implemented in terms of copy-construct and swap!

operator=( T* ) is not recommended either. Better to use a method such
as reset( T* )

swap is nice and simple and will never throw.

Nov 8 '06 #8
Earl Purple:
operator= should be implemented in terms of copy-construct and swap!

operator=( T* ) is not recommended either. Better to use a method such
as reset( T* )

swap is nice and simple and will never throw.

Nicely inefficient, yes.

--

Frederick Gotham
Nov 8 '06 #9
Ney André de Mello Zunino wrote:
Gianni Mariani wrote:
>It's important to have some example code that shows the error. BTW,
the Austria C++ smart pointer (at::Ptr<>)does all this and more so if
you want an example, go check it out.

I will have a look at it. Thank you. And let me just clarify that the
reason I decided to create my own smart pointer class was simply as an
opportunity for improving my experience and not because existing
alternatives did not suffice.
Excellent.
>
>Also, your ReferenceCountable class does dot handle copy construct and
assignment correctly and neither does your RefCountPtr.

You are right. I am infringing the "rule of three". I suppose that, in
the case of /ReferenceCountable/, I should make sure both the copy
constructor and assignment operator reset the reference count for the
new object, right? Is there anything else I should take into account?
No. Yes.

Think about what the reference count is for. The reference count is the
number of existing smart pointers that are pointing to the object being
reference counted ! This does not change when the object is assigned.
Since the default assignment operator and copy constructor copy the
reference count, this is not the right behaviour. So you need to ignore
the reference count of the RHS on assignment and set the reference count
to whatever you initial reference count is on copy construct.

>
As for /RefCountPtr<T>/, could you help me see what I am doing wrong in
the copy constructor? As for the assignment operator, I did implement
it, though I did not include the code in the original post. Here it is:

RefCountPtr& operator=(const RefCountPtr& original)
{
return operator=(original.object);
return * this;
}
Pointer assignment should be a template. You want to allow assignment
from types derived from T.
RefCountPtr& operator=(T* pointer)
{
// Check for self-assignment and assignment from
// equivalent smart pointer
if (this->object != pointer)
{
if (object)
{
Is it better that an object "delete" itself of that the smart pointer
calls delete ? You need to also consider using this in a threaded
environment. The first thing you want to do is to place the pointer
being assigned with the incremeneted count in place and the very last
thing is call decrement reference and in your case delete. The FAQ does
have it in the right order, so take a look at the FAQ.
object->decrementReferenceCount();
if (object->referenceCount() == 0)
delete object;
}
object = pointer;
if (pointer)
pointer->incrementReferenceCount();
}
return *this;
}

Is the implementation of the assignment operator for /RefCountPtr<T>/
also incorrect or incomplete?
incorrect ...

Also, consider letting the object do whatever it does on reference count
reaching zero and the smart pointer call whatever interface to manage it.

Also, what you're designing here is an "intrusive" smart pointer. There
is also an "extrusive" smart pointer idea which is what seems to be
targeted for approval for the next version of the standard. You can
find it in the boost shared_ptr implementation.
Nov 8 '06 #10
Earl Purple wrote:
You should declare a destructor in Test and put its implementation into
the compilation unit, even if it is empty (which is probably will be).
Then your compiler won't attempt to call the destructor of your
RefCountPtr class.
That makes sense. Because the definition of the /Foo/ class is included
in the compilation unit for the /Test/ class, Foo's structure will be
already known inside the definition of /~Test()/. The strange thing is
that one of the compilers I tried (Borland's BCC32 5.6) kept on
producing the error messages, even after I explicitly provided the
destructor for /Test/.

Regards,

--
Ney André de Mello Zunino
Nov 8 '06 #11
Ney André de Mello Zunino wrote:
Earl Purple wrote:
You should declare a destructor in Test and put its implementation into
the compilation unit, even if it is empty (which is probably will be).
Then your compiler won't attempt to call the destructor of your
RefCountPtr class.

That makes sense. Because the definition of the /Foo/ class is included
in the compilation unit for the /Test/ class, Foo's structure will be
already known inside the definition of /~Test()/. The strange thing is
that one of the compilers I tried (Borland's BCC32 5.6) kept on
producing the error messages, even after I explicitly provided the
destructor for /Test/.
Can you post a minimal but complete sample that we can cut-n-paste
unchanged to produce the error messages you're seeing?

Cheers! --M

Nov 8 '06 #12

Frederick Gotham wrote:
Earl Purple:
operator= should be implemented in terms of copy-construct and swap!

operator=( T* ) is not recommended either. Better to use a method such
as reset( T* )

swap is nice and simple and will never throw.


Nicely inefficient, yes.
How is it swap inefficient? You are swapping pointers, not what they
point to.

operator= implemented purely in terms of copy-construct and swap can be
inefficient in some cases when there is no need to do any allocations
or de-allocations. For example, if implementing std::string and the new
string fits comfortably into the already allocated buffer.
For such an implementation you should check first and only if a
reallocation is required, then use copy-construct and swap. For that
reason it is difficult to know whether operator= should take
std::string or const std::string & as its parameter. A good workaround
to that is to also have one overload that takes const char * directly
so you never get the implicit conversion.

None of this applies for smart pointers.

Nov 9 '06 #13
>
Pointer assignment should be a template. You want to allow assignment
from types derived from T.
Absolutely not necessary. The one that should preferably be a template
is assignment to another smart pointer where you do want to be able to
assign to smart pointers to types derived from T, or have SmartPtr<
const T take an assignment to SmartPtr< T >
Also, what you're designing here is an "intrusive" smart pointer. There
is also an "extrusive" smart pointer idea which is what seems to be
targeted for approval for the next version of the standard. You can
find it in the boost shared_ptr implementation.
Is it called "extrusive" or "non-intrusive"? I've never heard of the
word "extrusive". Intrusive reference-counting has its uses too, and
code can use more than one type of smart pointer. You also mention a
multi-threaded environment, but that's only an issue on rare occasions
and the majority of the times when it isn't, you don't want to be
locking and unlocking every time you modify a reference count. For
those times when it is an issue, you can use a special
reference-counter. None of this will be in tr1::shared_ptr because the
new standard doesn't yet recognise threads.

In addition, shared_ptr in tr1 will have a standard behaviour, not a
standard implementation. There is no such thing as a standard
implementation and this will remain the case in the next standard, so
although it is likely that tr1::shared_ptr will be implemented on code
similar to that of boost, it isn't guaranteed to be exactly the same
code, nor will it be guaranteed that two different versions of
shared_ptr are compatible with each other, so a library built with one
version that returns shared_ptrs to objects might break if the client
library is using a differerent implementation of the standard library.

To counter that, it is better off if your library does not return
shared_ptrs (or any other class in the standard library, even a
std::string). This is something I would like to see addressed because I
think it's a major flaw in C++ at the moment.

For education purposes, programmers should be taught how to write basic
constructs, even those that are already in the standard library or
boost or any other open-source library, even if they will never use
them in production code. Without this knowledge, who will be the PJ
Plaugers and Peter Dimovs of the next generation? Who will write the
next set of standard libraries that has concurrency issues built in?

Nov 9 '06 #14
mlimber wrote:
Can you post a minimal but complete sample that we can cut-n-paste
unchanged to produce the error messages you're seeing?
Sure! Here it goes. (Note: It would have been much easier for you and
for me if I could post the header and source files as attachments;
however, it seems attachments are not allowed [1], even if they are of
plain-text nature. Can anybody confirm that?)

// ---------- ReferenceCountable.h ---------- //

#ifndef REFERENCE_COUNTABLE_H
#define REFERENCE_COUNTABLE_H

class ReferenceCountable
{
public:
virtual ~ReferenceCountable() {}
void incrementReferenceCount() { count++; }
void decrementReferenceCount() { count--; }
unsigned int referenceCount() const { return count; }
protected:
ReferenceCountable() : count(0) {}
private:
unsigned int count;
};

#endif

// ---------- RefCountPtr.hpp ---------- //

#ifndef REF_COUNT_PTR_HPP
#define REF_COUNT_PTR_HPP

#include "ReferenceCountable.h"

template <class T>
class RefCountPtr
{
public:
RefCountPtr()
: object(0)
{
}

RefCountPtr(T* pointer)
: object(pointer)
{
if (object)
object->incrementReferenceCount();
}

RefCountPtr(const RefCountPtr& original)
: object(original.object)
{
if (object)
object->incrementReferenceCount();
}

~RefCountPtr()
{
if (object)
{
object->decrementReferenceCount();
if (object->referenceCount() == 0)
delete object;
}
}

RefCountPtr& operator=(const RefCountPtr& original)
{
return operator=(original.object);
}

RefCountPtr& operator=(T* pointer)
{
// Handle self-assignment and assignment from
// "equivalent" smart pointer
if (this->object != pointer)
{
if (object)
{
object->decrementReferenceCount();
if (object->referenceCount() == 0)
delete object;
}
object = pointer;
if (pointer)
pointer->incrementReferenceCount();
}
return *this;
}

T* operator->()
{
return object;
}

const T* operator->() const
{
return object;
}

T& operator*()
{
return *object;
}

const T& operator*() const
{
return *object;
}

T* pointer()
{
return object;
}

const T* pointer() const
{
return object;
}

operator void*() const
{
return object;
}
private:
T* object;
};

#endif

// ---------- Foo.h ---------- //

#ifndef FOO_H
#define FOO_H

#include "ReferenceCountable.h"

class Foo : public ReferenceCountable
{
};

#endif

// ---------- Test.h ---------- //

#ifndef TEST_H
#define TEST_H

#include "RefCountPtr.hpp"

class Foo;

class Test
{
public:
Test();
~Test();
private:
RefCountPtr<FoomFoo;
};

#endif

// ---------- Test.cpp ---------- //

#include "Test.h"
#include "Foo.h"

Test::Test()
{
}

Test::~Test()
{
}

// ---------- Main.cpp ---------- //

#include "Test.h"

int main()
{
Test test;
}

And here is the command session with Borland's compiler. g++ compiles
successfully and produces no output.

D:\Temp\RefCountPtrTest>bcc32 -eMain.exe Test.cpp Main.cpp
Borland C++ 5.6 for Win32 Copyright (c) 1993, 2002 Borland
Test.cpp:
Main.cpp:
Error E2315 RefCountPtr.hpp 35: 'decrementReferenceCount' is not a
member of 'Foo', because the type is not yet defined in function
RefCountPtr<Foo>::~RefCountPtr()
Error E2315 RefCountPtr.hpp 36: 'referenceCount' is not a member of
'Foo', because the type is not yet defined in function
RefCountPtr<Foo>::~RefCountPtr()
*** 2 errors in Compile ***

Please, note that the code still contains most of the problems indicated
in this thread. At the moment, the goal is to understand why BCC32 is
behaving the way it is.

[1] http://www.parashift.com/c++-faq-lit...t.html#faq-5.4

Thank you,

--
Ney André de Mello Zunino
Nov 9 '06 #15
* Ney André de Mello Zunino:
>
(Note: It would have been much easier for you and
for me if I could post the header and source files as attachments;
however, it seems attachments are not allowed [1], even if they are of
plain-text nature. Can anybody confirm that?)
Plain-text attachments, such as I hope is the result below (or wherever
your newsreader places it) are practically speaking OK. The problem is,
when such are "allowed", soon we'd have a flood of base64-encoded
attachments etc., from people using e.g. Outlook Express, attached code
with tabs instead of spaces, etc. And that would be UnGood.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
#include <iostream // std::cout
#include <ostream // operator<<, std::endl
#include <string // std::endl

class List
{
private:
std::string myString;

// Internal conversion from and to std::string.
List( std::string s ): myString( s ) {}
std::string asString() const { return myString; }

public:
typedef char Item;

List() {}
List( Item aHead, List aTail ): myString( aHead + aTail.asString() ) {}

Item head() const { return myString.at( 0 ); }
List tail() const { return List( myString.substr( 1 ) ); }
bool isEmpty() const { return myString.empty(); }
};
// An example of an operation built from the above operations.
void display( List s )
{
while( !s.isEmpty() )
{
std::cout << s.head() << ' ';
s = s.tail();
}
std::cout << std::endl;
}

List operator>>=( List::Item h, List t )
{
return List( h, t );
}

List aShortList()
{
return 'r' >>= 'e' >>= 'p' >>= 'a' >>= 'i' >>= 'd' >>= List();
}

void testDisplay() { display( aShortList() ); }

// Another example of an operation built from the above operations.
List asList( List::Item x )
{
return List( x, List() ); // Item followed by List produces List.
}

void testAsList() { display( asList( 'A' ) ); }

// A third example of an operation built from the above operations.
List joined( List a, List b )
{
return (a.isEmpty()? b : List( a.head(), joined( a.tail(), b ) ));
}

void testJoined() { display( joined( aShortList(), aShortList() ) ); }

// A fourth example of an operation built from the above operations.
List appended( List s, List::Item x )
{
return joined( s, asList( x ) );
}

void testAppended() { display( appended( aShortList(), '!' ) ); }

// A fifth example of an operation built from the above operations.
List reversed( List s )
{
return (s.isEmpty()? s : appended( reversed( s.tail() ), s.head() ));
}

void testReversed() { display( reversed( aShortList() ) ); }

int main()
{
testDisplay();
testAsList();
testJoined();
testAppended();
testReversed();
}
Nov 9 '06 #16
Earl Purple wrote:
>Pointer assignment should be a template. You want to allow assignment
from types derived from T.

Absolutely not necessary.
necessary to who ? I use them all the time.

at::Ptr<Base*x = new Derived;

.. The one that should preferably be a template
is assignment to another smart pointer where you do want to be able to
assign to smart pointers to types derived from T, or have SmartPtr<
const T take an assignment to SmartPtr< T >
Yep - that too.
>
>Also, what you're designing here is an "intrusive" smart pointer. There
is also an "extrusive" smart pointer idea which is what seems to be
targeted for approval for the next version of the standard. You can
find it in the boost shared_ptr implementation.

Is it called "extrusive" or "non-intrusive"?
intrusive is where the reference count is part of the object being
reference counted. Win32 COM objects are a great example of
"intrusive". "extrusive" or "non-intrusive" reference counts is where
the smart pointer creates a reference count on the fly for you.
... I've never heard of the
word "extrusive". Intrusive reference-counting has its uses too, and
code can use more than one type of smart pointer. You also mention a
multi-threaded environment, but that's only an issue on rare occasions
and the majority of the times when it isn't, you don't want to be
locking and unlocking every time you modify a reference count.
Sure, but you want the flexibility to have thread safe ones. Something
as basic as lifetime management gets into a threaded environment very
quickly. This is one of the reasons I prefer intrusive reference
counts. The type of reference count is part of the object not the
pointer, although the distinction is irrelevant in practice.
... For
those times when it is an issue, you can use a special
reference-counter. None of this will be in tr1::shared_ptr because the
new standard doesn't yet recognise threads.
There are a number of thread proposals being discussed at the moment.
Reference counts in smart pointers need to be thread safe so you can bet
that they're going to come out with a thread safe option to begin with.
boost's shared_ptr is thread safe last I looked (at least it has
compile mode for thread safety).

Also in the Austria C++ library's smart pointers, (which are intrusive
btw), these have ways of supporting non thread safe and thread safe
reference counts as well as supporting the COM style reference counts
(new objects start with a refcount of 1). The latest and trickiest is
an "atomic" version of the Austria smart pointer which allows multiple
threads to read and write the pointer simultaneously.

While I'm at it, there are also different modes of austria smart pointer
(one I call the "medusa" pointer) where you have different (intrusive)
reference counts for different smart pointer type allowing you to track
when all pointers of a particular type have released the object.

Then on top of all this, there are there flavours of Austria smart
pointer, (Ptr, PtrDelegate, PtrView) which can be used to minimize the
number of redundant increment/decrements when passing around lots of
smart pointers hence reducing the performance hit but it does require a
little "education" on the part of the programmer.
>
In addition, shared_ptr in tr1 will have a standard behaviour, not a
standard implementation. There is no such thing as a standard
implementation and this will remain the case in the next standard, so
although it is likely that tr1::shared_ptr will be implemented on code
similar to that of boost, it isn't guaranteed to be exactly the same
code, nor will it be guaranteed that two different versions of
shared_ptr are compatible with each other, so a library built with one
version that returns shared_ptrs to objects might break if the client
library is using a differerent implementation of the standard library.
Yep. There are going to be teething problems. Oh well.
>
To counter that, it is better off if your library does not return
shared_ptrs (or any other class in the standard library, even a
std::string). This is something I would like to see addressed because I
think it's a major flaw in C++ at the moment.
Really ? Why should we not return std::string ? Anyhow, in Austria
C++ land, return an at::PtrDelegate which basiclly "moves" the pointer
eliminating an increment/decrement...
>
For education purposes, programmers should be taught how to write basic
constructs, even those that are already in the standard library or
boost or any other open-source library, even if they will never use
them in production code. Without this knowledge, who will be the PJ
Plaugers and Peter Dimovs of the next generation? Who will write the
next set of standard libraries that has concurrency issues built in?
shared_ptr has lots of pros/cons and it will work just fine in a
threaded environment. As for concurrency, even though C++ does not
"support" threads, many implementations do. Austria C++ supports
win32/linux (64 & 32 bit) and a little behind on OSX testing and those
platforms have been supporting threads for quite a while.

As for gcc, there have been alot of "fixes" for threading and gcc
threading has been solid for probably 5 years now. Since 4.0, the
compiler even guarantees thread safety on constructs like:

T & f()
{
static T t = expression;

return t;
}
Anyhow, I think it's safe to say that "smart pointers" are a rich topic
for discussion but there are at least 4 implementations that are fairly
complete that have different "pros" and cons and they will probably work
mixed in the same body of code (not that I reccomend that but it will
probably work nonetheless).
Nov 13 '06 #17

Gianni Mariani wrote:
Earl Purple wrote:
Pointer assignment should be a template. You want to allow assignment
from types derived from T.
Absolutely not necessary.

necessary to who ? I use them all the time.

at::Ptr<Base*x = new Derived;
yes but just because you set it to a derived class doesn't mean that
the function should be a template (which would allow you to pass in ANY
type). I'm not sure you understand what templates are for.
Also, what you're designing here is an "intrusive" smart pointer. There
is also an "extrusive" smart pointer idea which is what seems to be
targeted for approval for the next version of the standard. You can
find it in the boost shared_ptr implementation.
Is it called "extrusive" or "non-intrusive"?

intrusive is where the reference count is part of the object being
reference counted. Win32 COM objects are a great example of
"intrusive". "extrusive" or "non-intrusive" reference counts is where
the smart pointer creates a reference count on the fly for you.
I wasn't asking you what they were, I'm fully aware of what an
intrusive and non-intrusive pointer are. I was asking you whether
extrusive was the correct word or whether the correct word is
non-intrusive.
... I've never heard of the
word "extrusive". Intrusive reference-counting has its uses too, and
code can use more than one type of smart pointer. You also mention a
multi-threaded environment, but that's only an issue on rare occasions
and the majority of the times when it isn't, you don't want to be
locking and unlocking every time you modify a reference count.

Sure, but you want the flexibility to have thread safe ones. Something
as basic as lifetime management gets into a threaded environment very
quickly. This is one of the reasons I prefer intrusive reference
counts. The type of reference count is part of the object not the
pointer, although the distinction is irrelevant in practice.
To fully define what intrusive reference counting means, it is where
the obejct itself knows it is being reference counted and can get
access to its reference count. It might also possibly be able to
increase it and decrease it. This can be useful if one construction of
an object, it has to put references to itself elsewhere.

You can fake intrusive reference counting with a non-intrusive
implementation by giving an object a shared pointer to itself.

A place where intrusive reference counting can be a good idea is for a
thread class which can contain a reference to itself and reduce its
reference count when it finishes running. If there are other references
to it, then its object state will be maintained but if not it will
destroy all its resources.
There are a number of thread proposals being discussed at the moment.
Reference counts in smart pointers need to be thread safe so you can bet
that they're going to come out with a thread safe option to begin with.
boost's shared_ptr is thread safe last I looked (at least it has
compile mode for thread safety).
I think you can choose to have all your shared pointers lock or all of
them not lock but you can't pick and choose and that should be an
available option.

If I'm posting messages to a deque< shared_ptr< Msg in a
producer-consumer queue then I will probably want thread-safe reference
counting. Even then I can get by without it - I can lock the mutex (to
the deque), post to the queue then immediately reset my own pointer
before releasing the lock to the deque, so I know there is an object
there and my release happens first

If I have a big collection in a vector< shared_ptr< T for some
class T and I have only one thread doing some manipulation with this
vector, eg sorting it, it would most likely be highly inefficient to
keep locking and unlocking each time a reference count is modified.
Also in the Austria C++ library's smart pointers, (which are intrusive
btw), these have ways of supporting non thread safe and thread safe
reference counts as well as supporting the COM style reference counts
(new objects start with a refcount of 1). The latest and trickiest is
an "atomic" version of the Austria smart pointer which allows multiple
threads to read and write the pointer simultaneously.
While I'm at it, there are also different modes of austria smart pointer
(one I call the "medusa" pointer) where you have different (intrusive)
reference counts for different smart pointer type allowing you to track
when all pointers of a particular type have released the object.

Then on top of all this, there are there flavours of Austria smart
pointer, (Ptr, PtrDelegate, PtrView) which can be used to minimize the
number of redundant increment/decrements when passing around lots of
smart pointers hence reducing the performance hit but it does require a
little "education" on the part of the programmer.
I am not aware of Austria, I am aware of Loki. I think the general
feeling is that a one "fits-all" smart-pointer is what a lot of people
want who don't want to have to think smart themselves. After all, even
not picking the best one is probably better than the old code that
didn't use them at all.

A lot of the time, proper garbage collection could replace
reference-counted smart pointers. Garbage collectors do not generally
use reference counting (because of the circular reference problem). I
don't know exactly how they do work.
To counter that, it is better off if your library does not return
shared_ptrs (or any other class in the standard library, even a
std::string). This is something I would like to see addressed because I
think it's a major flaw in C++ at the moment.

Really ? Why should we not return std::string ? Anyhow, in Austria
C++ land, return an at::PtrDelegate which basiclly "moves" the pointer
eliminating an increment/decrement...
Let's suppose you write a library using Dinkumware and you have a
function that returns a std::string. So it will return a Dinkumware
std::string.

But I am perhaps using Roguewave STL and perhaps their std::string has
a different physical layout (why shouldn't it?). So effectively the
code will just break at run-time.
Anyhow, I think it's safe to say that "smart pointers" are a rich topic
for discussion but there are at least 4 implementations that are fairly
complete that have different "pros" and cons and they will probably work
mixed in the same body of code (not that I reccomend that but it will
probably work nonetheless).
You can use as many different smart pointer implementation as you want
as long as they have different names so they act as different classes.

The danger is when you have two different ones that use the same name.
And tr1::shared_ptr might just be that if there is no one standard
implementation.

Nov 13 '06 #18
Earl Purple wrote:
Gianni Mariani wrote:
....
>at::Ptr<Base*x = new Derived;

yes but just because you set it to a derived class doesn't mean that
the function should be a template (which would allow you to pass in ANY
type). I'm not sure you understand what templates are for.
at::Ptr<Derived*x = new Derived;
at::Ptr<Base*y = x;

Do that one without templates.

....
A place where intrusive reference counting can be a good idea is for a
thread class which can contain a reference to itself and reduce its
reference count when it finishes running. If there are other references
to it, then its object state will be maintained but if not it will
destroy all its resources.
Having a thread object destroy itself is really hard to get right and
depends on a number of non-portable assumptions. The whole idea of
detached threads and the constraints under that are needed to make them
reliable is a big question. In short, don't do it.
>
>There are a number of thread proposals being discussed at the moment.
Reference counts in smart pointers need to be thread safe so you can bet
that they're going to come out with a thread safe option to begin with.
boost's shared_ptr is thread safe last I looked (at least it has
compile mode for thread safety).

I think you can choose to have all your shared pointers lock or all of
them not lock but you can't pick and choose and that should be an
available option.
Curious, why ?
>
If I'm posting messages to a deque< shared_ptr< Msg in a
producer-consumer queue then I will probably want thread-safe reference
counting. Even then I can get by without it - I can lock the mutex (to
the deque), post to the queue then immediately reset my own pointer
before releasing the lock to the deque, so I know there is an object
there and my release happens first
Ah, false assumption. Unless you're really really careful to release
(decrement the reference count) before unlocking the deque mutex, you're
in for surprises unless it's a thread safe reference count. If you do
that, you're deque interface would look rather ugly.
>
If I have a big collection in a vector< shared_ptr< T for some
class T and I have only one thread doing some manipulation with this
vector, eg sorting it, it would most likely be highly inefficient to
keep locking and unlocking each time a reference count is modified.
Thread safe reference counting is done using atomic ("intrisic")
instructions. They do have an overhead but it is certainly not as
significant as a mutex/lock/unlock cycle. However, having said that,
for many use cases, it would probably not make a discernible difference.
>
>Also in the Austria C++ library's smart pointers, (which are intrusive
btw), these have ways of supporting non thread safe and thread safe
reference counts as well as supporting the COM style reference counts
(new objects start with a refcount of 1). The latest and trickiest is
an "atomic" version of the Austria smart pointer which allows multiple
threads to read and write the pointer simultaneously.
>While I'm at it, there are also different modes of austria smart pointer
(one I call the "medusa" pointer) where you have different (intrusive)
reference counts for different smart pointer type allowing you to track
when all pointers of a particular type have released the object.

Then on top of all this, there are there flavours of Austria smart
pointer, (Ptr, PtrDelegate, PtrView) which can be used to minimize the
number of redundant increment/decrements when passing around lots of
smart pointers hence reducing the performance hit but it does require a
little "education" on the part of the programmer.

I am not aware of Austria, I am aware of Loki. I think the general
feeling is that a one "fits-all" smart-pointer is what a lot of people
want who don't want to have to think smart themselves. After all, even
not picking the best one is probably better than the old code that
didn't use them at all.
Sure.
>
A lot of the time, proper garbage collection could replace
reference-counted smart pointers. Garbage collectors do not generally
use reference counting (because of the circular reference problem). I
don't know exactly how they do work.
The "Boehm" collector is a great example of one for C++. The problem
with garbage collectors is unpredictablility. If your application does
not require it, then using a collector is probably a good thing.
However, if your application can't deal with an intermittent pause while
the collector does it's thing, then reference counting + smart pointers
is the next best thing.

As you mention, the problem with reference counting is the cyclic issue
and the fix is a "don't do that" design issue. One of the mechanisms I
use is a "coupling" mechanism which reliably allows objects to decouple
one-another at any time. It's in the Austria C++ library called Twin or
TwinMT for MT code.
>
>>To counter that, it is better off if your library does not return
shared_ptrs (or any other class in the standard library, even a
std::string). This is something I would like to see addressed because I
think it's a major flaw in C++ at the moment.
Really ? Why should we not return std::string ? Anyhow, in Austria
C++ land, return an at::PtrDelegate which basiclly "moves" the pointer
eliminating an increment/decrement...

Let's suppose you write a library using Dinkumware and you have a
function that returns a std::string. So it will return a Dinkumware
std::string.
Ah - nothing like a good standard, let's have lots of them.

This just means you need to "standardize" on the implementation. I
stick to the vendor supplied versions.
>
But I am perhaps using Roguewave STL and perhaps their std::string has
a different physical layout (why shouldn't it?). So effectively the
code will just break at run-time.
>Anyhow, I think it's safe to say that "smart pointers" are a rich topic
for discussion but there are at least 4 implementations that are fairly
complete that have different "pros" and cons and they will probably work
mixed in the same body of code (not that I reccomend that but it will
probably work nonetheless).

You can use as many different smart pointer implementation as you want
as long as they have different names so they act as different classes.

The danger is when you have two different ones that use the same name.
And tr1::shared_ptr might just be that if there is no one standard
implementation.
Nov 15 '06 #19
Earl Purple wrote:
If I have a big collection in a vector< shared_ptr< T for some
class T and I have only one thread doing some manipulation with this
vector, eg sorting it, it would most likely be highly inefficient to
keep locking and unlocking each time a reference count is modified.
You would want to just move the shared_ptr's in that case. There's no
need to adjust the reference counts.
--
Joe Seigh

When you get lemons, you make lemonade.
When you get hardware, you make software.
Nov 17 '06 #20
Gianni Mariani wrote:
>
Also in the Austria C++ library's smart pointers, (which are intrusive
btw), these have ways of supporting non thread safe and thread safe
reference counts as well as supporting the COM style reference counts
(new objects start with a refcount of 1). The latest and trickiest is
an "atomic" version of the Austria smart pointer which allows multiple
threads to read and write the pointer simultaneously.
Which tricky technique is that?
>
While I'm at it, there are also different modes of austria smart pointer
(one I call the "medusa" pointer) where you have different (intrusive)
reference counts for different smart pointer type allowing you to track
when all pointers of a particular type have released the object.

Then on top of all this, there are there flavours of Austria smart
pointer, (Ptr, PtrDelegate, PtrView) which can be used to minimize the
number of redundant increment/decrements when passing around lots of
smart pointers hence reducing the performance hit but it does require a
little "education" on the part of the programmer.
You should put some sort of lexical scoping restrictions on those to limit
their lifespan. Raw pointers with a leash. Not make them assignable or
something. A bit of hackery since C++ doesn't support restricting stuff
to local very well.

--
Joe Seigh

When you get lemons, you make lemonade.
When you get hardware, you make software.
Nov 17 '06 #21
Joe Seigh wrote:
Gianni Mariani wrote:
>>
Also in the Austria C++ library's smart pointers, (which are intrusive
btw), these have ways of supporting non thread safe and thread safe
reference counts as well as supporting the COM style reference counts
(new objects start with a refcount of 1). The latest and trickiest is
an "atomic" version of the Austria smart pointer which allows multiple
threads to read and write the pointer simultaneously.

Which tricky technique is that?
Pull down the latest austria c++ "beta" here:
http://netcabletv.org/public_releases/ (warning - it's big - 100meg -
contains some prebuilt binaries).

In the file at_lifetime_mt.h, it contains a traits class
PtrClassRefWrapperMT that provides thread-safe semantics for smart
pointer objects. It's not really "lock free" because it implements a
lock but it works. The smart pointer template disallows anything but
placing a pointer from a "PtrClassRefWrapperMT" into/from a non thread
safe smart pointer so it ensures that the pointer is held by a second
(non thread safe) smart pointer during the process.

This combined with the ThreadSafePtrLock in at_thread.h allows you to
"atomically lock" a reference counted lock even while other threads
might be trying to change the pointer contents.

This is used in the MT object coupling using the "twinmt"
system/pattern/whatever. This means two separate objects can talk to
each other while maintaining the ability to disconnect at any time which
relieves design constraints on object lifetimes.

The "at::Timer" test cases exercise this feature
(associating/deassociating/getting events from timers simultaneously).

I use the word "tricky" because it's a little convoluted not because
it's non-obvious.

The at:: smart pointers can be given traits class that are used to
manage how to increment and decrement the reference counts as well as
access the pointer. The thread safe traits class uses "compare
exchange" and "scheduler yields" in ways that are similar to a spin
lock. The advantages of using the at:: thread safe smart pointer (apart
from syntactic brevity, is that it only uses the storage required for
the pointer itself and it it likely to ever deadlock because the
lock/unlock is done in the same path and the lock is never exposed
(unless someone uses (T*)0x1 as a pointer value!). Also the number of
instructions from lock to unlock is exceedingly small. On classes with
non virtual reference count inc/dec funtions, this can be as little as 3
instructions (but potentially hundreds of cycles because of the atomic ops).

I've only needed to use it for the cases where the at::twin_mt code is
used to attach/detach/destroy simultaneously for the reference counted
lock, but it is not written to be specific to this case, it is truly
general purpose.

>>
While I'm at it, there are also different modes of austria smart
pointer (one I call the "medusa" pointer) where you have different
(intrusive) reference counts for different smart pointer type allowing
you to track when all pointers of a particular type have released the
object.

Then on top of all this, there are there flavours of Austria smart
pointer, (Ptr, PtrDelegate, PtrView) which can be used to minimize the
number of redundant increment/decrements when passing around lots of
smart pointers hence reducing the performance hit but it does require
a little "education" on the part of the programmer.

You should put some sort of lexical scoping restrictions on those to limit
their lifespan. Raw pointers with a leash. Not make them assignable or
something. A bit of hackery since C++ doesn't support restricting stuff
to local very well.
I'm not sure I know what you mean.

The Austria C++ smart pointer design rules are like this.

1. Use an at::Ptr smart pointer as member variables or places where you
need to "hold a reference" to an object.

2. When passing Austria smart pointers as a parameter if the API can
guarentee that the caller will own the object while the call is in
progress, use an at::PtrView otherwise use an at::PtrDelegate.
PtrDelegate becomes null once the pointer is transferred to a regular
smart pointer.

3. Always use a PtrDelegate when returning a smart pointer and always
place a returned PtrDelegate into an at::Ptr.

4. If you don't care about the overhead of redundant increment/decrement
references, it's safe to pass an at::Ptr everywhere.

Sure, I would like to make it easer, but unless we change the C++
language, I can't see how much easier we can make it. I have not
thought much about the new "move" semantics, but having said that, I
should see if it can eliminate some of the complexity.
Nov 18 '06 #22
Gianni Mariani wrote:
Joe Seigh wrote:
>Gianni Mariani wrote:
>>>
Also in the Austria C++ library's smart pointers, (which are
intrusive btw), these have ways of supporting non thread safe and
thread safe reference counts as well as supporting the COM style
reference counts (new objects start with a refcount of 1). The
latest and trickiest is an "atomic" version of the Austria smart
pointer which allows multiple threads to read and write the pointer
simultaneously.


Which tricky technique is that?


Pull down the latest austria c++ "beta" here:
http://netcabletv.org/public_releases/ (warning - it's big - 100meg -
contains some prebuilt binaries).

In the file at_lifetime_mt.h, it contains a traits class
PtrClassRefWrapperMT that provides thread-safe semantics for smart
pointer objects. It's not really "lock free" because it implements a
lock but it works. The smart pointer template disallows anything but
placing a pointer from a "PtrClassRefWrapperMT" into/from a non thread
safe smart pointer so it ensures that the pointer is held by a second
(non thread safe) smart pointer during the process.
[...]

Ah, I was thinking there was another lock-free refcount algorithm out
there besides the two I use in atomic_ptr at
http://atomic-ptr-plus.sourceforge.net/
one for double compare and swap and another for load reserved/store conditional
architectures.

It's not documented but if you want to see how it works look at the patent
applications by the Sun Research synchronization group
20060037026 Lightweight reference counting using single-target synchronization
20060218561 Code preparation technique employing lock-free pointer operations
use http://appft1.uspto.gov/netahtml/PTO/srchnum.html
If you have the tiff plugin the illustrations are quite nice.

It's not too bad performance wise but I'm tending toward lighter
weight ptrs like fastsmr (RCU+SMR). A hazard pointer load takes
about 14 nsec on a 866Mhz P3 vs. 4 nsec for a raw load.

I don't use these for memory management but for lock-free solutions
to the reader/writer problem. There are also lock-free based proxy
collectors which effectively give you raw pointer access to collections.
There's also a refcounting scheme which I haven't done yet which
uses PDR (PCOW (partial copy on write) deferred reclaimation) to
ensure the refcount doesn't go away on on any in progress refcount
increments. fastsmr, RCU, proxy refcounting are forms of PDR.

>>>
While I'm at it, there are also different modes of austria smart
pointer (one I call the "medusa" pointer) where you have different
(intrusive) reference counts for different smart pointer type
allowing you to track when all pointers of a particular type have
released the object.

Then on top of all this, there are there flavours of Austria smart
pointer, (Ptr, PtrDelegate, PtrView) which can be used to minimize
the number of redundant increment/decrements when passing around lots
of smart pointers hence reducing the performance hit but it does
require a little "education" on the part of the programmer.

You should put some sort of lexical scoping restrictions on those to
limit
their lifespan. Raw pointers with a leash. Not make them assignable or
something. A bit of hackery since C++ doesn't support restricting stuff
to local very well.


I'm not sure I know what you mean.

The Austria C++ smart pointer design rules are like this.

1. Use an at::Ptr smart pointer as member variables or places where you
need to "hold a reference" to an object.

2. When passing Austria smart pointers as a parameter if the API can
guarentee that the caller will own the object while the call is in
progress, use an at::PtrView otherwise use an at::PtrDelegate.
PtrDelegate becomes null once the pointer is transferred to a regular
smart pointer.

3. Always use a PtrDelegate when returning a smart pointer and always
place a returned PtrDelegate into an at::Ptr.

4. If you don't care about the overhead of redundant increment/decrement
references, it's safe to pass an at::Ptr everywhere.

Sure, I would like to make it easer, but unless we change the C++
language, I can't see how much easier we can make it. I have not
thought much about the new "move" semantics, but having said that, I
should see if it can eliminate some of the complexity.
They're basically like a raw reference which is safe as long as you
own one of rhe refcount pointers. I haven't tried it but bascially
you don't make them assignable. They can only be declared and
intialized with a ctor. Declare the ptr that holds the refcount
and then declare the "raw" ptr contructed from the refcount one.
Since they're not assignable the "raw" ptr can't have the refcount
one changed out from under it.
--
Joe Seigh

When you get lemons, you make lemonade.
When you get hardware, you make software.
Nov 18 '06 #23
Joe Seigh wrote:
Gianni Mariani wrote:
....
>Sure, I would like to make it easer, but unless we change the C++
language, I can't see how much easier we can make it. I have not
thought much about the new "move" semantics, but having said that, I
should see if it can eliminate some of the complexity.

They're basically like a raw reference which is safe as long as you
own one of rhe refcount pointers. I haven't tried it but bascially
you don't make them assignable. They can only be declared and
intialized with a ctor. Declare the ptr that holds the refcount
and then declare the "raw" ptr contructed from the refcount one.
Since they're not assignable the "raw" ptr can't have the refcount
one changed out from under it.
I'm still not exactly sure what you're saying. However, if I'm right,
this technique you describe won't work with extrusive smart pointers.
While the at:: smart pointers are currently useful for intrusive
reference counts, I am toying with adding extrusive reference counting
support as well.
Nov 18 '06 #24
"Gianni Mariani" <gi*******@mariani.wswrote in message
news:45***********************@per-qv1-newsreader-01.iinet.net.au...
Joe Seigh wrote:
>Gianni Mariani wrote:
...
>>Sure, I would like to make it easer, but unless we change the C++
language, I can't see how much easier we can make it. I have not
thought much about the new "move" semantics, but having said that, I
should see if it can eliminate some of the complexity.

They're basically like a raw reference which is safe as long as you
own one of rhe refcount pointers. I haven't tried it but bascially
you don't make them assignable. They can only be declared and
intialized with a ctor. Declare the ptr that holds the refcount
and then declare the "raw" ptr contructed from the refcount one.
Since they're not assignable the "raw" ptr can't have the refcount
one changed out from under it.

I'm still not exactly sure what you're saying. However, if I'm right,
this technique you describe won't work with extrusive smart pointers.
While the at:: smart pointers are currently useful for intrusive reference
counts, I am toying with adding extrusive reference counting support as
well.
You amortize the reference counting with PDR by tracking collector objects,
and not individual objects. You can attach an object to the collector, which
will use function pointer to inform it that its in a quiescent state.
more info on PDR:
http://groups.google.com/group/comp....0f4163f8e13f1e
(follow all links...)

here some info on high-performance PDR scheme I invented:
http://groups.google.com/group/comp....q=vzoom&qt_g=1

Nov 18 '06 #25
Chris Thomasson wrote:
....
You amortize the reference counting with PDR by tracking collector objects,
and not individual objects. You can attach an object to the collector, which
will use function pointer to inform it that its in a quiescent state.
more info on PDR:
http://groups.google.com/group/comp....0f4163f8e13f1e
(follow all links...)

here some info on high-performance PDR scheme I invented:
http://groups.google.com/group/comp....q=vzoom&qt_g=1

I don't see enough documentation for me to understand specifically the
alogorithm you're talking about.

The austria C++ system is designed for primarily for ease of use and
correctness (making it harder to make silly errors) so I'm not too
concerned with efficiency as this has never been an issue. When it
does, I'll worry about it.

The Austria C++ "thread safe resource" solution is just a simple
reference counted system (as has been around since forever) with an
ability to make a smart pointer copy of the pointer atomically. Not
exactly innovative but effective.

There is also a RAII thread safe lock called ThreadSafePtrTryLock which
takes a reference counted mutex smart pointer and a reference to a
pointer to a resource and atomically acquires the mutex to the resource.

I have been using these techniques for many years and never ran into
problems. Sure there may be an issue with hot spots on systems with
large numbers of CPUs but I have not yet had that problem.

The question then is, if I can "retrofit" some of these other more
efficient techniques under the same interface? If I can, then everyone
is happy since one day I'll rip out the "ineffinient" system I have and
replace it with a more efficient system and all will be well. If I
can't, then I'll need to either add a new interface (sucks) or break
alot of code using it (sucks more).

Nov 18 '06 #26

Gianni Mariani wrote:
>
at::Ptr<Derived*x = new Derived;
at::Ptr<Base*y = x;

Do that one without templates.
operator= (T * ptr );

part of a template class but not a template pointer. whatever pointer
is passed in must be or implicitly conver to T*. So if T is Base then
you can pass in x because Derived derives from Base.

In addition if the type is a const X then you can pass in a pointer to
non-const because that can implicitly convert to a pointer to const.
Having a thread object destroy itself is really hard to get right and
depends on a number of non-portable assumptions. The whole idea of
detached threads and the constraints under that are needed to make them
reliable is a big question. In short, don't do it.
In reality my threads usually get joined at some point and belong to a
threadpool even if there's only one thread in the threadpool.
I think you can choose to have all your shared pointers lock or all of
them not lock but you can't pick and choose and that should be an
available option.

Curious, why ?
Because you don't always need the overhead as many times the shared_ptr
will only be accessed by one thread at a time. For example, you will
often use shared_ptr in a collection eg vector< shared_ptr< T or
map< string, shared_ptr< T etc.

And the lifetime of those objects is longer than the threads.

Now if one thread is sorting a vector of shared pointers and it is
guaranteed that no other thread is accessing it, either because it's
locked or because the data is local to this thread, then it is wasteful
to have to lock each one.

So sometimes you don't want the overhead, but that doesn't mean there
aren't occasions when you do.
If I'm posting messages to a deque< shared_ptr< Msg in a
producer-consumer queue then I will probably want thread-safe reference
counting. Even then I can get by without it - I can lock the mutex (to
the deque), post to the queue then immediately reset my own pointer
before releasing the lock to the deque, so I know there is an object
there and my release happens first

Ah, false assumption. Unless you're really really careful to release
(decrement the reference count) before unlocking the deque mutex, you're
in for surprises unless it's a thread safe reference count. If you do
that, you're deque interface would look rather ugly.
You don't do anything, you call a method call post() or push() or
whatever you want to call it. Now that call locks a mutex, adds the
item to the queue and then unlocks the mutex.

Now if you also get that call to relinquish ownership (and possibly
null the pointer, eg by resetting the shared_ptr) then there is no
problem. The underlying deque is locked throughout all of this so the
other thread gets no access.
Thread safe reference counting is done using atomic ("intrisic")
instructions. They do have an overhead but it is certainly not as
significant as a mutex/lock/unlock cycle. However, having said that,
for many use cases, it would probably not make a discernible difference.
On what platform? There is no standard atomic instruction as you know.
On standard POSIX systems there is no such thing as atomic_increment().

Of course your particular system may have a more efficient way of doing
it than locking and unlocking a mutex but it is not standard and you
can't guarantee this for all platforms.
The "Boehm" collector is a great example of one for C++. The problem
with garbage collectors is unpredictablility. If your application does
not require it, then using a collector is probably a good thing.
However, if your application can't deal with an intermittent pause while
the collector does it's thing, then reference counting + smart pointers
is the next best thing.
Garbage collection can be useful but deterministic destruction is also
useful. I think D addresses both, and if they ever brought GC into C++
then I hope there would still be destructors for other RAII, including
interacting with C libraries where you are required to call a method
when you have finished with something because I'm certain that GC won't
extend to external libraries written in other languages.
As you mention, the problem with reference counting is the cyclic issue
and the fix is a "don't do that" design issue. One of the mechanisms I
use is a "coupling" mechanism which reliably allows objects to decouple
one-another at any time. It's in the Austria C++ library called Twin or
TwinMT for MT code.
Good. If Austria is an open-source library I may have a look at it.

Nov 19 '06 #27
Earl Purple wrote:
Gianni Mariani wrote:
>at::Ptr<Derived*x = new Derived;
at::Ptr<Base*y = x;

Do that one without templates.

operator= (T * ptr );

part of a template class but not a template pointer. whatever pointer
is passed in must be or implicitly conver to T*. So if T is Base then
you can pass in x because Derived derives from Base.

In addition if the type is a const X then you can pass in a pointer to
non-const because that can implicitly convert to a pointer to const.
Go ahead an try that. I don't remember all the problems but the last
thing you want to do is make it easy to inadvertently convert from a
smart pointer to a regular pointer.
>
>Having a thread object destroy itself is really hard to get right and
depends on a number of non-portable assumptions. The whole idea of
detached threads and the constraints under that are needed to make them
reliable is a big question. In short, don't do it.

In reality my threads usually get joined at some point and belong to a
threadpool even if there's only one thread in the threadpool.
Really ? Having fixed many bugs due to self destructing threads, I
think there are easier, more robust ways to go.
>
>>I think you can choose to have all your shared pointers lock or all of
them not lock but you can't pick and choose and that should be an
available option.
Curious, why ?

Because you don't always need the overhead as many times the shared_ptr
will only be accessed by one thread at a time. For example, you will
often use shared_ptr in a collection eg vector< shared_ptr< T or
map< string, shared_ptr< T etc.

And the lifetime of those objects is longer than the threads.

Now if one thread is sorting a vector of shared pointers and it is
guaranteed that no other thread is accessing it, either because it's
locked or because the data is local to this thread, then it is wasteful
to have to lock each one.

So sometimes you don't want the overhead, but that doesn't mean there
aren't occasions when you do.
The overhead associated with atomic increment and decrement is not that
great. However, with Austria C++ reference counted objects, you can
choose to make them thread safe or not.

>
>>If I'm posting messages to a deque< shared_ptr< Msg in a
producer-consumer queue then I will probably want thread-safe reference
counting. Even then I can get by without it - I can lock the mutex (to
the deque), post to the queue then immediately reset my own pointer
before releasing the lock to the deque, so I know there is an object
there and my release happens first
Ah, false assumption. Unless you're really really careful to release
(decrement the reference count) before unlocking the deque mutex, you're
in for surprises unless it's a thread safe reference count. If you do
that, you're deque interface would look rather ugly.

You don't do anything, you call a method call post() or push() or
whatever you want to call it. Now that call locks a mutex, adds the
item to the queue and then unlocks the mutex.
simple case:
at::Ptr<Msg *> message = new Message();
....
send_message( message );

//message is a pointer available in both threads now.

so unless you're doing things like removing the message from the smart
pointer in send_message, you have a race condition.

This is a classic.
>
Now if you also get that call to relinquish ownership (and possibly
null the pointer, eg by resetting the shared_ptr) then there is no
problem. The underlying deque is locked throughout all of this so the
other thread gets no access.
>Thread safe reference counting is done using atomic ("intrisic")
instructions. They do have an overhead but it is certainly not as
significant as a mutex/lock/unlock cycle. However, having said that,
for many use cases, it would probably not make a discernible difference.

On what platform? There is no standard atomic instruction as you know.
On standard POSIX systems there is no such thing as atomic_increment().
True. However all modern platforms having system specific stuff that
does this. The standard C++ thread discussions seem to all agree that
there should be a standard interface to these low level primitives
through a standard interface.
>
Of course your particular system may have a more efficient way of doing
it than locking and unlocking a mutex but it is not standard and you
can't guarantee this for all platforms.
The platforms that don't support it are probably not target platforms
anyway.
>
>The "Boehm" collector is a great example of one for C++. The problem
with garbage collectors is unpredictablility. If your application does
not require it, then using a collector is probably a good thing.
However, if your application can't deal with an intermittent pause while
the collector does it's thing, then reference counting + smart pointers
is the next best thing.

Garbage collection can be useful but deterministic destruction is also
useful. I think D addresses both, and if they ever brought GC into C++
then I hope there would still be destructors for other RAII, including
interacting with C libraries where you are required to call a method
when you have finished with something because I'm certain that GC won't
extend to external libraries written in other languages.
>As you mention, the problem with reference counting is the cyclic issue
and the fix is a "don't do that" design issue. One of the mechanisms I
use is a "coupling" mechanism which reliably allows objects to decouple
one-another at any time. It's in the Austria C++ library called Twin or
TwinMT for MT code.

Good. If Austria is an open-source library I may have a look at it.
The latest version (public one that is) is here:

http://netcabletv.org/public_releases/

Note - the download is 100meg - it contains a bunch of prebuilt binaries.

It's more like an alpha release....

Nov 22 '06 #28
"Gianni Mariani" <gi*******@mariani.wswrote in message
news:45**********************@per-qv1-newsreader-01.iinet.net.au...
The overhead associated with atomic increment and decrement is not that
great.
Oh, yes they are:
http://groups.google.com/group/comp....c665e616176dce
(read all please)
Indeed!

;^)

Any thoughts?
Nov 23 '06 #29
Chris Thomasson wrote:
"Gianni Mariani" <gi*******@mariani.wswrote in message
news:45**********************@per-qv1-newsreader-01.iinet.net.au...
>The overhead associated with atomic increment and decrement is not that
great.

Oh, yes they are:
http://groups.google.com/group/comp....c665e616176dce
(read all please)
....
Any thoughts?
That thread discusses RW locks. I'm talking about atomic increment. On
IA32 and AMD64 at least these are single instructions that the CPU
synchronizes with other CPU's. It is certainly more expensive that non
atomic instructions but in the scheme of things, it's not that bad.

I'm not saying that having a non-mt safe reference count is a good
option for a reference counted library, I'm just saying that in most
cases you don't need to worry about it.

Austria C++ supports both thread safe and non thread safe reference
counted base classes.
Nov 23 '06 #30

Gianni Mariani wrote:
Earl Purple wrote:
Gianni Mariani wrote:
at::Ptr<Derived*x = new Derived;
at::Ptr<Base*y = x;

Do that one without templates.
operator= (T * ptr );

part of a template class but not a template pointer. whatever pointer
is passed in must be or implicitly conver to T*. So if T is Base then
you can pass in x because Derived derives from Base.

In addition if the type is a const X then you can pass in a pointer to
non-const because that can implicitly convert to a pointer to const.

Go ahead an try that. I don't remember all the problems but the last
thing you want to do is make it easy to inadvertently convert from a
smart pointer to a regular pointer.
You originally said assigning to a pointer not to another smart
pointer.

If you have an implicit constructor from T* then operator=( T* ) will
work even if you haven't defined it but have defined operator=(
smartptr<T)

And one can always do something wrong with it even with boost.

boost::shared_ptr< Derived p1 (new Derived );
boost::shared_ptr< Base p2 ( p1.get() );

Silly but believe me people do it. Or use reset() and pass in a managed
pointer. I'm sure programmers will manage to find a way of abusing
austria pointers too.

Nov 23 '06 #31
"Gianni Mariani" <gi*******@mariani.wswrote in message
news:45**********************@per-qv1-newsreader-01.iinet.net.au...
Chris Thomasson wrote:
>"Gianni Mariani" <gi*******@mariani.wswrote in message
news:45**********************@per-qv1-newsreader-01.iinet.net.au...
>>The overhead associated with atomic increment and decrement is not that
great.

Oh, yes they are:
http://groups.google.com/group/comp....c665e616176dce
(read all please)
...
>Any thoughts?

That thread discusses RW locks. I'm talking about atomic increment.
If you re-read the post, you should find where it specifically talks about
the instructions that are used to actually implment a rw-lock...

On IA32 and AMD64 at least these are single instructions that the CPU
synchronizes with other CPU's. It is certainly more expensive that non
atomic instructions but in the scheme of things, it's not that bad.
Okay... Try to bear with me here:

http://groups.google.com/group/comp....ef173bee4f0c03
(read last paragraph...)

http://groups.google.com/group/comp....a6db6ba9e7fd95
http://groups.google.com/group/comp....b7e857ef440520
See what I am getting at here Gianni?

Austria C++ supports both thread safe and non thread safe reference
counted base classes.
Fine. The more flexibility, the better.

:^)
Nov 24 '06 #32
Chris Thomasson wrote:
....
Okay... Try to bear with me here:

http://groups.google.com/group/comp....ef173bee4f0c03
(read last paragraph...)

http://groups.google.com/group/comp....a6db6ba9e7fd95
http://groups.google.com/group/comp....b7e857ef440520
See what I am getting at here Gianni?
Sure, yes, I agree that atomic ops are more expensive. If they're too
expensive it's usually solved by more code being written (engineering).
If it's not, you write code quicker (sloppier with performance).
Austria C++ allows you to:

a) Have various forms of intrusive reference counts (virtual (COM like),
non thread safe, and thread safe).

b) Have various types of smart pointer that allows you to reduce the
amount of redundant increment/decrement of reference counts. (usually
means littering the interfaces with "PtrDelegate" and "PtrView" in the
right places.

c) Have various types of smart pointer. (not implemented yet, but
conceivably you can have one type of reference count handled by a
"thread safe" smart pointer and a "non thread safe" smart pointer
allowing you to choose which kind of reference count you want for
different parts of the app).

Having said all that, I think all I have needed to do was option b) and
without really analyzing the characteristics of performance.

YMMV
>
>Austria C++ supports both thread safe and non thread safe reference
counted base classes.

Fine. The more flexibility, the better.
Actually, more modern CPU's are doing better and better with cache
coherency. I wrote a program to test it a while ago and posted it on
the net called "cpulat". Occasionally I run it on new CPU's and I have
found that in the last few years, cpu<->cpu latentcies have actually
decreased. The "cpulat" code is broken on the latest AMD AM2 I tried it
on and I think it's to do with visibility, I'll need to look at it
again. Nontheless, the hardware will get better. Anything we can do to
make it easier to write good code from scratch, the better, and if that
means "fixing the CPU", so bet it!
Nov 24 '06 #33
"Gianni Mariani" <gi*******@mariani.wswrote in message
news:45**********************@per-qv1-newsreader-01.iinet.net.au...
Chris Thomasson wrote:
...
>Okay... Try to bear with me here:

http://groups.google.com/group/comp....ef173bee4f0c03
(read last paragraph...)

http://groups.google.com/group/comp....a6db6ba9e7fd95
http://groups.google.com/group/comp....b7e857ef440520
See what I am getting at here Gianni?

Sure, yes, I agree that atomic ops are more expensive. If they're too
expensive it's usually solved by more code being written (engineering). If
it's not, you write code quicker (sloppier with performance).

[...]
Anything we can do to make it easier to write good code from scratch, the
better, and if that means "fixing the CPU", so bet it!
Sadly, the current trend in hardware seems to be extremely strong cache
(e.g., due to TM populatiry)... I would prefer an arch with very weak cache,
and very clear memory model documentation... Why make the cache so damn
strong? Well, TM is partly to blame IMHO, Sad....

;^(...
Nov 25 '06 #34

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

14
by: David B. Held | last post by:
I wanted to post this proposal on c.l.c++.m, but my news server apparently does not support that group any more. I propose a new class of exception safety known as the "smart guarantee". ...
8
by: Axter | last post by:
I normally use a program call Doxygen to document my source code.(http://www.stack.nl/~dimitri/doxygen) This method works great for small and medium size projects, and you can get good...
6
by: zl2k | last post by:
hi, When I considered about preventing memory leaking, the method came up to my mind is using boost smart pointer if possible (use stl::vector instead of type, use smart pointer whenever declare...
14
by: Ian | last post by:
I am looking at porting code from a C++ application to C#. This requires implementing data sharing functionality similar to what is provided by a smart pointer in C++. I have only recently begun...
13
by: Phil Bouchard | last post by:
I am currently writting a smart pointer which is reasonnably stable and I decided supporting allocators for completion because of its increase in efficiency when the same pool used by containers is...
50
by: Juha Nieminen | last post by:
I asked a long time ago in this group how to make a smart pointer which works with incomplete types. I got this answer (only relevant parts included): ...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.