Quote:
Originally posted by dthomson
Hi,
I'm finding it difficult to determine exactly what the new "proxy" type syntax in Slice means exactly.
Take my burgeoning Pengo example:
Code:
module Pengo
{
// ...
interface GameElement
{
Position getPos();
};
// ...
interface Gettable extends GameElement
{
void get();
};
// ...
interface PlayerCharacter extends GameElement
{
void pickUp(Gettable* element);
};
// ...
};
The important interface here is "PlayerCharacter" near the bottom. I obviously want to pass a reference (or proxy) to the "pickUp" operation, but why do I need the "*" syntax? What wrong with the IDL syntax ie just "void pickUp(Gettable element);"?
Now, I can appreciate that you may just want the syntax to reflect the nature of the argument passing more clearly, to avoid confusion with "pass-by-value" semantics.
Unfortunately, "void pickUp(Gettable element)" also compiles in Slice! And, according to "diff", different C++ code is produced for this. The documentation is completely silent on what this "plain" syntax means for interfaces. Perhaps this is not supposed to be allowed at all? If it *is*, what does it mean?
|
Consider the following (extending your example):
Code:
class Thing implements Pengo::gettable {
// State members here...
};
The class implements a
gettable. So, when we pass a
gettable, we have to decide whether we want to pass it by value or by reference because the run-time type of the argument may be something that is derived from
gettable, and the derived thing could be a class instead of an interface.
The difference in the generated code (in C++) is that a
gettable* is passed as a
gettablePrx, but a
gettable is passed as a
gettablePtr, giving you pass-by-reference and pass-by-value semantics, respectively.
Now, let's assume that we have no derivation of anything (no derived classes or interfaces), and simply write something such as following:
Code:
interface foo { /* ... */ };
interface bar {
void op(foo p); // Note: foo, not foo*
};
In the client code, I get a
foo proxy from somewhere and then try to pass that proxy as the parameter to
op. It turns out that you cannot do that because a
fooPrx is not type compatible with a
fooPtr. The signature of
op is:
Code:
void op(const fooPtr &, const Ice::Context & = Ice::Context());
When you look at the generated code, you will find that
fooPrx is actually a typedef:
Code:
typedef ::IceInternal::ProxyHandle< ::IceProxy::foo> fooPrx;
So, we have a generated class called
::IceProxy::foo, which is the actual proxy instance that contains the code to invoke operations on the remote object, and we have
fooPrx, which is a smart pointer to the underlying proxy instance. This also explains why you never have to call things such as
duplicate() or
release() in the C++ mapping. (The
Prx handles are loosely analogous to CORBA
_var references.)
On the other hand, we have
fooPtr, which is also a typedef:
Code:
typedef ::IceInternal::Handle< ::foo> fooPtr;
So, for pass-by-value, the formal parameter type is
IceInternal::Handle<::foo>, but for pass-by-reference, the formal parameter type is
IceInternal::ProxyHandle< ::IceProxy::foo>. As I mentioned previously,
IceProxy::foo is the actual proxy class, whereas
::foo is the skeleton class from which you derive class and interface implementations. The two type hierarchies are in no way related, so there is no way to pass a proxy to an operation that uses pass-by-value semantics.
The upshot of all of this is that, if you define a Slice parameter as
foo, you *must* pass a class instance when invoking the operation, whereas, if you define Slice parameter as
foo*, you *must* pass a proxy.
Cheers,
Michi.