From: Michael Mol on
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.

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Jonathan Lee on
On Dec 16, 8:50 am, Michael Mol <mike...(a)gmail.com> wrote:
> 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


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Maxim Yegorushkin on
On 16/12/09 13:50, Michael Mol wrote:
> 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?

--
Max

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Anders Dalvander on
On Dec 16, 2:50 pm, Michael Mol <mike...(a)gmail.com> wrote:
> The question is, which approach is more appropriate and intuitive for C
> ++, and why?

Each class and function should have responsibility for a single task
or related tasks.

The factory class should have the responsibility to create objects.
The OBJHANDLE class should have the responsibility to perform its
specific tasks.
The destructor of a class should have the responsibility to clean up
resources and such.

For instance:

{
some_smart_ptr<OBJHANDLE> obj(factoryObj.CreateObject());
obj->OperateOnObject(42);
// some_smart_ptr is responsible to delete the object, the
destructor of OBJHANDLE is responsible to clean up its resources.
}

Regards,
Anders Dalvander


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: James Kanze on
On Dec 16, 1:50 pm, Michael Mol <mike...(a)gmail.com> wrote:
> 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.

--
James Kanze

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]