Factories, handles, and handle wrappers

M

Michael Mol

Let's say you have a factory which returns handles to objects that it
allocates. You pass these handles to the factory whenever you want to
use the object or destroy it. The factory's "OperateOnObject" class
verifies that the handle is within its object collection, and then
marshals the OperateOnObject call to the class's internal
OperateOnObject method. All external operations on the object
instance are required to pass through the Factory in order to
guarantee that the object instance is still valid at the time of call.

This leads to code roughly like this. (Not using exceptions here, but
rather return error codes.)

// For the sake of scope, consider this as a global singleton factory.
// For the sake of concerns over initialization and destruction,
assume that's dealt with in code not shown.
class SomeFactory
{
public:
OBJHANDLE CreateObject();
ERRCODE DestroyObject(OBJHANDLE);
ERRCODE OperateOnObject(OBJHANDLE objHandle, int someArgument);
protected:
OBJCOLLECTION objCollection;
} factoryObj;

// In some function, somewhere
OBJHANDLE objHandle = factoryObj.CreateObject();
factoryObj.OperateOnObject(objHandle, 42);
factoryObj.DestroyObject(objHandle);



Providing objHandle as the first argument of every call to a distinct
marshalling class instance proves tedious, so a means of overloading
OBJHANDLE is desired such that explicit references to factoryObj are
unnecessary. The resulting OBJHANDLE (a wrapper for the type formally
known as OBJHANDLE) would not likely behave specially at construction
or destruction, nor likely have a custom copy constructor.

The two approaches under consideration are:

OBJHANDLE objHandle = factoryObj.CreateObject();
objHandle->OperateOnObject(42);
// or
(*objHandle)OperateOnObject(42);
objHandle->DestroyObject();

and an approach which is used as follows:

OBJHANDLE objHandle = factoryObj.CreateObject();
objHandle.OperateOnObject(42);
objHandle.DestroyObject();

Both approaches would have the exact same effects on the state of
factoryObj and the members of OBJCOLLECTION, which would also be the
same as the old code where the old OBJHANDLE type was passed as the
first argument.

The question is, which approach is more appropriate and intuitive for C
++, and why? I ask because this is a matter of matter of debate
between another coworker and I, and the third coworker consulted had
no opinion.
 
M

Maxim Yegorushkin

> Let's say you have a factory which returns handles to objects that it
> allocates. You pass these handles to the factory whenever you want to
> use the object or destroy it. The factory's "OperateOnObject" class
> verifies that the handle is within its object collection, and then
> marshals the OperateOnObject call to the class's internal
> OperateOnObject method. All external operations on the object
> instance are required to pass through the Factory in order to
> guarantee that the object instance is still valid at the time of call.
>
> This leads to code roughly like this. (Not using exceptions here, but
> rather return error codes.)
>
> // For the sake of scope, consider this as a global singleton factory.
> // For the sake of concerns over initialization and destruction,
> assume that's dealt with in code not shown.
> class SomeFactory
> {
> public:
> OBJHANDLE CreateObject();
> ERRCODE DestroyObject(OBJHANDLE);
> ERRCODE OperateOnObject(OBJHANDLE objHandle, int someArgument);
> protected:
> OBJCOLLECTION objCollection;
> } factoryObj;
>
> // In some function, somewhere
> OBJHANDLE objHandle = factoryObj.CreateObject();
> factoryObj.OperateOnObject(objHandle, 42);
> factoryObj.DestroyObject(objHandle);

In other words, you've got:

1) A factory that creates objects.
2) Those objects implement an interface, which is currently belongs to
factory class.
3) You'd also like for the factory to check whether the object reference
is valid.

You can refactor this to simply things.

1) Extract object interface from the factory.

struct SomeObject {
// former SomeFactory::OperateOnObject
virtual ERRCODE Operate(int someArgument) = 0;
virtual ~SomeObject() = 0;
};

Using such object now does not require a factory object, i.e. you can
call Operare() directly on the object.

2) Make factory return smart-pointers to SomeObject. The objects it
creates implement SomeObject interface.

typedef boost::shared_ptr<SomeObject> SomeObjectPtr;

class SomeFactory {
public:
SomeObjectPtr createSomeObject();
...
};

Now the factory function returns a smart-pointer. This smart-pointer
takes care of destroying the object when it is no longer used. No manual
object destruction required.

3) Using a smart-pointer makes the possibility of using an already
destroyed object highly unlikely. Checking whether the object reference
is valid may be not necessary any more.

New usage:

SomeFactory factory;

// later in some function or scope
{
SomeObjectPtr object = factory.createSomeObject();
object->Operate(123);
} // now object goes out of scope and gets destroyed automatically

This looks to be simpler and more intuitive, isn't it?
 
M

Michael Mol

On 16/12/09 13:50, Michael Mol wrote:

In other words, you've got:

1) A factory that creates objects.
2) Those objects implement an interface, which is currently belongs to
factory class.
3) You'd also like for the factory to check whether the object reference
is valid.

The code exists, and it's been in the field for over a year. What I'd
*like* is an abstracted pass-through to work with existing code.
You can refactor this to simply things.

1) Extract object interface from the factory.

   struct SomeObject {
       // former SomeFactory::OperateOnObject
       virtual ERRCODE Operate(int someArgument) = 0;
       virtual ~SomeObject() = 0;
   };

Using such object now does not require a factory object, i.e. you can
call Operare() directly on the object.

Which is the type of apparent behavior I'd like. However, the factory
object can't be removed entirely, as it also manages the relationship
of object instances to each other and the system resources they happen
to extract. (A bit of internal behavior I hoped wasn't necessary to
describe in order to ask about intuitive of dereferencing syntax.)
2) Make factory return smart-pointers to SomeObject. The objects it
creates implement SomeObject interface.

   typedef boost::shared_ptr<SomeObject> SomeObjectPtr;

   class SomeFactory {
   public:
        SomeObjectPtr createSomeObject();
        ...
   };

Now the factory function returns a smart-pointer. This smart-pointer
takes care of destroying the object when it is no longer used. No manual
object destruction required.

The object represents an abstraction of a system resource, and there
are a lot of operating factors that weigh in on when it's appropriate
to free that system resource. It normally doesn't happen during the
weeks-long run of an application, though it does on occasion.
3) Using a smart-pointer makes the possibility of using an already
destroyed object highly unlikely. Checking whether the object reference
is valid may be not necessary any more.

New usage:

   SomeFactory factory;

   // later in some function or scope
   {
     SomeObjectPtr object = factory.createSomeObject();
     object->Operate(123);
   } // now object goes out of scope and gets destroyed automatically

This looks to be simpler and more intuitive, isn't it?

Because of the pervasive and high-traffic use of the subsystem
OBJHANDLE is part of, and because of the nature of the application,
"highly unlikely" is inevitability, and minimizing risk while meeting
the client's feature desires close to their scheduling desires is the
order of the day. Given the choice between such a large-scale
refactoring and dealing with a new formulation of the code or staying
with a tedious syntax, I'd stay with the tedious syntax.

I understand what you're saying, and for new code design, that would
be fine. However, the current code exists, has been fielded for over a
year, is a core and frequently-trafficked component where that traffic
is fairly sensitive--and it's stable.

That's why my original fielded question laid out two options; Either
can be implemented with few risks to stability, and I was looking for
a discussion regarding of wrapping a handle such that it may be
operated with a more terse syntax.
 
J

Jonathan Lee

The two approaches under consideration are:

OBJHANDLE objHandle = factoryObj.CreateObject();
objHandle->OperateOnObject(42);
// or
(*objHandle)OperateOnObject(42);
objHandle->DestroyObject();

and an approach which is used as follows:

OBJHANDLE objHandle = factoryObj.CreateObject();
objHandle.OperateOnObject(42);
objHandle.DestroyObject();

The question is, which approach is more appropriate and intuitive for C
++, and why? I ask because this is a matter of matter of debate
between another coworker and I, and the third coworker consulted had
no opinion.

I have two thoughts:
1) The second example is an odd sort of indirection. Are all your
methods called "DoSomethingToObject"? It seems like your handler
class does nothing except "hold" the object. Which leads me to
think
2) If you're going to use indirection, use dereference. But change
the method names. Instead of
objHandle.OperateOnObject(42);
this seems much more natural:
objHandle->Operate(42);
The notation suggests you are acting on the held object. No
need for "OnObject" or similar.

So if you insist on keeping the method names, the "." notation makes
more sense. If you're willing to change, "->" would be even better.

--Jonathan
 
M

Maxim Yegorushkin

Let's say you have a factory which returns handles to objects that it
allocates. You pass these handles to the factory whenever you want to
use the object or destroy it. The factory's "OperateOnObject" class
verifies that the handle is within its object collection, and then
marshals the OperateOnObject call to the class's internal
OperateOnObject method. All external operations on the object
instance are required to pass through the Factory in order to
guarantee that the object instance is still valid at the time of call.

This leads to code roughly like this. (Not using exceptions here, but
rather return error codes.)

// For the sake of scope, consider this as a global singleton factory.
// For the sake of concerns over initialization and destruction,
assume that's dealt with in code not shown.
class SomeFactory
{
public:
OBJHANDLE CreateObject();
ERRCODE DestroyObject(OBJHANDLE);
ERRCODE OperateOnObject(OBJHANDLE objHandle, int someArgument);
protected:
OBJCOLLECTION objCollection;
} factoryObj;

// In some function, somewhere
OBJHANDLE objHandle = factoryObj.CreateObject();
factoryObj.OperateOnObject(objHandle, 42);
factoryObj.DestroyObject(objHandle);

In other words, you've got:

1) A factory that creates objects.
2) Those objects implement an interface, which is currently belongs to
factory class.
3) You'd also like for the factory to check whether the object reference
is valid.

You can refactor this to simply things.

1) Extract object interface from the factory.

struct SomeObject {
// former SomeFactory::OperateOnObject
virtual ERRCODE Operate(int someArgument) = 0;
virtual ~SomeObject() = 0;
};

Using such object now does not require a factory object, i.e. you can
call Operare() directly on the object.

2) Make factory return smart-pointers to SomeObject. The objects it
creates implement SomeObject interface.

typedef boost::shared_ptr<SomeObject> SomeObjectPtr;

class SomeFactory {
public:
SomeObjectPtr createSomeObject();
...
};

Now the factory function returns a smart-pointer. This smart-pointer
takes care of destroying the object when it is no longer used. No manual
object destruction required.

3) Using a smart-pointer makes the possibility of using an already
destroyed object highly unlikely. Checking whether the object reference
is valid may be not necessary any more.

New usage:

SomeFactory factory;

// later in some function or scope
{
SomeObjectPtr object = factory.createSomeObject();
object->Operate(123);
} // now object goes out of scope and gets destroyed automatically

This looks to be simpler and more intuitive, isn't it?
 
J

James Kanze

In other words, you've got:
1) A factory that creates objects.
2) Those objects implement an interface, which is currently belongs to
factory class.
3) You'd also like for the factory to check whether the object reference
is valid.
You can refactor this to simply things.
1) Extract object interface from the factory.
struct SomeObject {
// former SomeFactory::OperateOnObject
virtual ERRCODE Operate(int someArgument) = 0;
virtual ~SomeObject() = 0;
};
Using such object now does not require a factory object, i.e. you can
call Operare() directly on the object.
2) Make factory return smart-pointers to SomeObject. The objects it
creates implement SomeObject interface.
typedef boost::shared_ptr<SomeObject> SomeObjectPtr;
class SomeFactory {
public:
SomeObjectPtr createSomeObject();
...
};
Now the factory function returns a smart-pointer. This smart-pointer
takes care of destroying the object when it is no longer used. No
manual object destruction required.

Except that boost::smart_ptr won't necessarily work here---it will
render destruction non-deterministic, and will cause objects to "leak"
as soon as there are any cycles.

Of course, his solution won't work either, since without garbage
collection, there's absolutely no way to ensure that the invalid
pointer
remains invalid.
3) Using a smart-pointer makes the possibility of using an already
destroyed object highly unlikely.

True. But it does so by not destroying the objects when they should
be
destroyed, and possibly never. The cure is as bad as the disease.
 
M

Maxim Yegorushkin

Except that boost::smart_ptr won't necessarily work here---it will
render destruction non-deterministic,

I would say that the destruction is perfectly deterministic -- when the
last copy of the shared_ptr<> referencing that particular object is
destroyed. (Although, in practice, I normally prefer factories to return
> and will cause objects to "leak" as soon as there are any cycles.

It is interesting that you mention non-deterministic destruction and
leaks due to cycles with regards to one subject. To me it looks like
leaks due to cycles are an inherent problem of precisely deterministic
destruction, when some form of reference counting is used. For example,
Perl and Python use deterministic destruction and in these languages
leaks are possible due to cycles. Java, on the other hand, offers
non-deterministic destruction which does not suffer from cycles (but
suffers from non-determinism, which to me is a bigger evil than cycles).
Of course, his solution won't work either, since without garbage
collection, there's absolutely no way to ensure that the invalid
pointer
remains invalid.


True. But it does so by not destroying the objects when they should
be
destroyed, and possibly never. The cure is as bad as the disease.

Feeling a bit thick, could you possibly elaborate the last point?
 
M

Maxim Yegorushkin

The code exists, and it's been in the field for over a year. What I'd
*like* is an abstracted pass-through to work with existing code.


Which is the type of apparent behavior I'd like. However, the factory
object can't be removed entirely, as it also manages the relationship
of object instances to each other and the system resources they happen
to extract. (A bit of internal behavior I hoped wasn't necessary to
describe in order to ask about intuitive of dereferencing syntax.)


The object represents an abstraction of a system resource, and there
are a lot of operating factors that weigh in on when it's appropriate
to free that system resource. It normally doesn't happen during the
weeks-long run of an application, though it does on occasion.


Because of the pervasive and high-traffic use of the subsystem
OBJHANDLE is part of, and because of the nature of the application,
"highly unlikely" is inevitability, and minimizing risk while meeting
the client's feature desires close to their scheduling desires is the
order of the day. Given the choice between such a large-scale
refactoring and dealing with a new formulation of the code or staying
with a tedious syntax, I'd stay with the tedious syntax.

I understand what you're saying, and for new code design, that would
be fine. However, the current code exists, has been fielded for over a
year, is a core and frequently-trafficked component where that traffic
is fairly sensitive--and it's stable.

That's why my original fielded question laid out two options; Either
can be implemented with few risks to stability, and I was looking for
a discussion regarding of wrapping a handle such that it may be
operated with a more terse syntax.

I see now.

One possible improvement is to wrap the object handle and a reference to
a factory in a class, so that its instances can be used as standalone
objects in the new code that can take advantage of the scoped resource
management. This can well be the interface shown above.

This way you could have two interfaces to the component -- the existing
interface and the new one. This allows to gradually evolve to using the
new interface without breaking the existing code.

The key goal here is to take advantage of RAII / scoped resource
management. Otherwise you may well be better off using plain C.
 
M

Michael Mol

Feeling a bit thick, could you possibly elaborate the last point?

The problem is that a particular scalar value for OBJHANDLE may be
reused, and it's possible (however unlikely) that that value will be
reused in the future when allocating a new instance. If a stale,
invalid value of OBJHANDLE is kept around somewhere, and that value
happens to be reused in the creation of a new instance, then that
stale handle is pointing to an instance of an object that isn't the
same as the one it was *created* to reference.

The validity gate, merely maintaining a roster of currently-valid
values, has no way of distinguishing between two time-distinct
instances that happened to have the same handle value.

Think of it like US social security numbers. Those numbers are reused
after an individual dies. If one only had the SSN as an identifier,
there would be no means on paper distinguish between the deceased and
the newborn, and anything tied strictly to that SSN could pass forward
accidentally.

I would hope most systems that touch SSNs would have a mechanism in
place to prevent such roll-forwards, but the Factory in the case of
OBJHANDLE doesn't, as Mr. Kanze pointed out.
 
M

Maxim Yegorushkin

I have not (yet) used Python, but they claim to garbage-collect cycles as
of version 2.0:

http://docs.python.org/whatsnew/2.0.html#garbage-collection-of-cycles

This also means non-deterministic destruction, I guess.

Non-deterministic only when there are cycles though, I would guess.
I remember there was a long thread about garbage collection in C++ and IIRC
the outcome was that the garbage collector should release the memory, but
should not invoke any destructors, in order to avoid non-deterministic
behavior (postponing the destructor call forever is considered
deterministic).

It probably depends on one's definition of deterministic.

I think the major source of determinism in C++ is the scoped resource
management based on destructors (RAII). IMO, it is more powerful and
general mechanism applicable to pretty much any type of resource
(memory, mutexes, locks, connections, etc..) rather than garbage
collection limited to one type of resource which is memory and a
ridiculous prehistoric try/finally construct for anything else.
 
J

James Kanze

Let's say you have a factory which returns handles to objects that it
allocates. You pass these handles to the factory whenever you want to
use the object or destroy it. The factory's "OperateOnObject" class
verifies that the handle is within its object collection, and then
marshals the OperateOnObject call to the class's internal
OperateOnObject method. All external operations on the object
instance are required to pass through the Factory in order to
guarantee that the object instance is still valid at the time of call.
This leads to code roughly like this. (Not using exceptions here, but
rather return error codes.)

This is overly complicated; most importantly, it renders client code
unnecessarily complicated. The simple solution for this is:

1. Install the Boehm garbage collector and use it for these objects.

2. Define a flag in each object, which is set in the constructor, and
reset in the destructor.

3. Check this flag each time you use the object.
 
J

jp

Let's say you have a factory which returns handles to objects that it
allocates. You pass these handles to the factory whenever you want to
use the object or destroy it. The factory's "OperateOnObject" class
verifies that the handle is within its object collection, and then
marshals the OperateOnObject call to the class's internal
OperateOnObject method. All external operations on the object
instance are required to pass through the Factory in order to
guarantee that the object instance is still valid at the time of call.

This leads to code roughly like this. (Not using exceptions here, but
rather return error codes.)

// For the sake of scope, consider this as a global singleton factory.
// For the sake of concerns over initialization and destruction,
assume that's dealt with in code not shown.
class SomeFactory
{
public:
OBJHANDLE CreateObject();
ERRCODE DestroyObject(OBJHANDLE);
ERRCODE OperateOnObject(OBJHANDLE objHandle, int someArgument);
protected:
OBJCOLLECTION objCollection;

} factoryObj;

// In some function, somewhere
OBJHANDLE objHandle = factoryObj.CreateObject();
factoryObj.OperateOnObject(objHandle, 42);
factoryObj.DestroyObject(objHandle);

Providing objHandle as the first argument of every call to a distinct
marshalling class instance proves tedious, so a means of overloading
OBJHANDLE is desired such that explicit references to factoryObj are
unnecessary. The resulting OBJHANDLE (a wrapper for the type formally
known as OBJHANDLE) would not likely behave specially at construction
or destruction, nor likely have a custom copy constructor.

The two approaches under consideration are:

OBJHANDLE objHandle = factoryObj.CreateObject();
objHandle->OperateOnObject(42);
// or
(*objHandle)OperateOnObject(42);
objHandle->DestroyObject();

and an approach which is used as follows:

OBJHANDLE objHandle = factoryObj.CreateObject();
objHandle.OperateOnObject(42);
objHandle.DestroyObject();

Both approaches would have the exact same effects on the state of
factoryObj and the members of OBJCOLLECTION, which would also be the
same as the old code where the old OBJHANDLE type was passed as the
first argument.

The question is, which approach is more appropriate and intuitive for C
++, and why? I ask because this is a matter of matter of debate
between another coworker and I, and the third coworker consulted had
no opinion.

OBJHANDLE objHandle = factoryObj.CreateObject();
factoryObj.OperateOnObject(objHandle, 42);
factoryObj.DestroyObject(objHandle);

Technically, wouldn't this be

OBJHANDLEWRAPPER objHandleWrapper = factoryObj.CreateObject();
factoryObj.OperateOnObject(objHandleWrapper, 42);
factoryObj.DestroyObject(objHandleWrapper);

Given that, you should be able to operate directly on the wrapper
with:

objHandleWrapper.OperateOnObject(42);

Or, you could extract the handle from the wrapper and then use the
original syntax:

OBJHANDLE objHandle = objHandleWrapper.GetHandle();
factoryObj.OperateOnObject(objHandle, 42);

And obviously you could pass a pointer to the wrapper to another
function, and operate on that with:

SomeClass.SomeFunction(&objHandleWrapper);

void SomeClass::SomeFunction(OBJHANDLEWRAPPER* pObjHndWrpr)
{
pObjHndWrpr->OperateOnObject(42);
}

Just my thoughts...
 
M

Michael Mol

I have two thoughts:
1) The second example is an odd sort of indirection. Are all your
methods called "DoSomethingToObject"? It seems like your handler
class does nothing except "hold" the object. Which leads me to
think
2) If you're going to use indirection, use dereference. But change
the method names. Instead of
objHandle.OperateOnObject(42);
this seems much more natural:
objHandle->Operate(42);
The notation suggests you are acting on the held object. No
need for "OnObject" or similar.

So if you insist on keeping the method names, the "." notation makes
more sense. If you're willing to change, "->" would be even better.

I should have used different method names for the example; The ones
given were intended to be generic, but I should have used somthing
similar to DoSomething(). The actual method names in place are closer
to, e.g. GetThroughput(), AttachListener(), DetachListener(), etc.

OBJHANDLE itself is currently actually implemented as a pointer to the
class in question, but typedef'd in a way that direct dereferencing is
impossible (to force checking for validity, rather than risk
dereferencing a NULL, destroyed or otherwise invalid pointer). The
container class in question, as written, contains that value.

If I'm reading your post correctly, then since the wrapper class
doesn't contain the object itself, but rather access to the object,
then you would suggest that the -> syntax is preferred.
 
J

James Kanze

I would say that the destruction is perfectly deterministic -- when the
last copy of the shared_ptr<> referencing that particular object is
destroyed.

In other words, undeterministic, at least in practice. Possibly
never,
and almost certainly later than required.
(Although, in practice, I normally prefer factories to return
auto_ptr<> to emphasize the fact that the ownership of a newly created
object is being transferred).

I too find that auto_ptr is a good choice when creating objects,
especially when additional actions are needed until they're capable of
taking care of themselves. Sometimes between the creation and the end
of the transaction, of course, release will be called on the auto_ptr,
so that it will no longer delete anything, but until the object is
fully
registered and installed in the runtime system, auto_ptr provides a
nice
(essential?) safety net.
It is interesting that you mention non-deterministic destruction and
leaks due to cycles with regards to one subject.

Normal, since they're the two major problems with shared_ptr. (The
other major problem with boost::shared_ptr is that it's too easy to
end
up with two separate counters. This can be easily cured by using
invasive shared pointers, however.)
To me it looks like leaks due to cycles are an inherent problem of
precisely deterministic destruction, when some form of reference
counting is used.

Leaks due to cycles are an inherent problem of reference counting,
yes.
But they have nothing to do with destruction, per se.
For example, Perl and Python use deterministic destruction and in
these languages leaks are possible due to cycles. Java, on the other
hand, offers non-deterministic destruction which does not suffer from
cycles (but suffers from non-determinism, which to me is a bigger evil
than cycles).

Java doesn't offer "destruction" at all---deterministic or
otherwise---at the language level. Nothing in Java, however, forbids
defining a function (traditionally named "dispose") which "destructs"
the object. And is called at a deterministic time.

Of course, this is also possible in C++ (with some sort of garbage
collection for recovering memory), but the name (and the tradition)
says
that it is the destructor which destructs or disposes of the
object---which terminates its lifetime.
Feeling a bit thick, could you possibly elaborate the last point?

Objects have defined, deterministic lifetimes. In some cases, they
can
(or should) continue to exist even when there is no visible pointer to
them. (To be useful, there must be a pointer to them somewhere, but
this can be a pointer hidden somewhere in the OS, which causes them to
receive external events.) And in many cases, they need to be
destructed
even if there are pointers to them. The logic is the opposite of
smart
pointers or garbage collection: you don't destruct the object or free
the memory because there are no more pointers to it; you eliminate all
pointers to it because it's lifetime has logically ended. And in most
applications, the relationships between objects (represented by
pointers) do not form a tree or a DAG. There will be cycles. Which
means that if you're counting uniquely on shared_ptr, destructors
won't
be called when they should be, and in many cases, they won't be called
at all, because of the cycles.

Of course, with boost::shared_ptr, you can use boost::weak_ptr to
break
the cycles. But this requires practically the same amount of work as
doing all of the management by hand, and it still doesn't solve the
problem of determinism: an external event determines that the object's
lifetime should end, not whether or not there is still a pointer to
it.
 
J

James Kanze

[...]
Yes.

One could consider it a fundamental problem in C++, that memory
managment is linked to destruction. On the other hand, C++ does have
mechanisms which can be used to automatically trigger destruction in
certain cases (on stack objects, in particular), which can be
particularly useful in those cases where they're applicable.
It probably depends on one's definition of deterministic.

Deterministic means that it happens at a specific, well determined
point
in the program logic. (At meta-levels, everything is deterministic,
since the computer is nothing other than a large finite statement
machine. And at an even higher level, quantum physics mean that
nothing
is deterministics. Neither of these levels are of any practical
consideration when trying to develop working software, however.)
I think the major source of determinism in C++ is the scoped resource
management based on destructors (RAII).

The destruction of variables defined at local scope is deterministic.
And an incredibly powerful and useful idiom. std::shared_ptr does
provide deterministic destruction *IF* (and only if) there is only one
pointer to the object, and that pointer is defined at local scope.
Generally, however, such cases are better handled by not using a
pointer
at all, or by using auto_ptr.
IMO, it is more powerful and general mechanism applicable to pretty
much any type of resource (memory, mutexes, locks, connections, etc..)

More than just resources. It supports full transactional semantics.
rather than garbage collection limited to one type of resource which
is memory and a ridiculous prehistoric try/finally construct for
anything else.

The difference is simple: destructors are only automatically called on
local objects, which have a lifetime defined by the language. For
dynamically allocated objects, whose lifetime depends on external
events, they must be called explicitly. Calling a destructor ends the
object's lifetime.

Garbage collection addresses a completely different issue. It ensures
that the memory for an object will not be reused as long as there are
any pointers to that object, *even* if the object's lifetime has
ended.
It's really necessary for safety reasons: with garbage collection, you
can mark a object as dead in its destructor, and verify that it is not
dead in all member functions. When memory management is linked to the
lifetime of the object, you always run the risk of recycling the
memory
of a dead object while there are still pointers to it, with the result
that using the pointer will access a different object. (This
represents
a serious security hole on systems which are openly accessible, such
as
web servers.)
 
M

Michael Mol

This is overly complicated; most importantly, it renders client code
unnecessarily complicated. The simple solution for this is:

1. Install the Boehm garbage collector and use it for these objects.

2. Define a flag in each object, which is set in the constructor, and
reset in the destructor.

3. Check this flag each time you use the object.

While I'll admit that the current code has flaws in this area, how
does setting a class member "AmIValid" flag guard against
dereferencing an uninitizialized or wild pointer? In either case, I
have an instant crash. (In most such circumstances, the current code
triggers a hard breakpoint in debug, and a null-op return in release.
The only remaining circumstance hasn't been seen, but is presumably
possible.)

More fundamentally, how does it guard against a stale pointer that
winds up pointing to memory overwritten due to some other heap
allocation? Any non-zero value in that flag would indicate a valid
instance, as far as the code that peeks at that flag is concerned.

(If some of the context is missing, I'm sory; Some of the relevant
replies in comp.lang.c++ don't have comp.lang.c++.moderated in the To
or Followup-To fields.)
 
M

Michael Mol

On Dec 16, 9:50 pm, Maxim Yegorushkin<[email protected]>
wrote:
On 16/12/09 13:50, Michael Mol wrote: [snip]
Except that boost::smart_ptr won't necessarily work here---it will
render destruction non-deterministic, and will cause objects to "leak"
as soon as there are any cycles.
Of course, his solution won't work either, since without garbage
collection, there's absolutely no way to ensure that the invalid
pointer
remains invalid.
While the identifier space is currently sparse enough that that hasn't
been detected as a problem (normally, fewer than twenty allocations
occur within the lifetime of the application), that's an interesting
point. I originally sought to deal with it in the beginning by using
sequential integer values, but settled back on the raw pointer value
to deal with resulting code complexity.

That sequential counter would need to be embedded both in the object and
in the handle. Comparing these two integers detects whether the object
being referred to is still the same object. This method, however, does
not protect from accessing an object which exists no more.

Having a simple map in the Factory solves the handle->pointer
conversion. (That's actually how I wrote the first draft.) There's
overhead finding the element in the map, but there's overhead in
finding the handle in the existing OBJCOLLECTION.
To reduce the possibility of overflow that counter only needs to be
incremented on an allocation following a deallocation, since several
allocations in a row with no intervening deallocation always return
unique pointers.

So what you're suggesting is creating a UID that's essentially pointer
+incrementor. An interesting idea. In this particular scenario, I
could get away with a 16-bit (or even an 8-bit) incrementor.
 
D

DeMarcus

* Sorry for the obtuse language; OBJHANDLE is intentionally not
directly associated with the type of the class it identifies, because
it's imperative that those class instances not be used without
checking for their validity. If I go ahead and call it a pointer, then
confusion would arise based on the assumption that it's already
directly usable as such. Hence the term "handle". I'm open to
becoming familiarized with clearer language and/or terminology.

Instead of OBJHANDLE, what about call it an OBJ_PROXY?

http://en.wikipedia.org/wiki/Proxy_pattern
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,981
Messages
2,570,188
Members
46,731
Latest member
MarcyGipso

Latest Threads

Top