anuragraghavan / franca

Automatically exported from code.google.com/p/franca
0 stars 0 forks source link

"Polymorphic" keyword to make polymorphic structs explicit #39

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
This issue is related to RFC Issue 19 
(https://code.google.com/a/eclipselabs.org/p/franca/issues/detail?id=19).

Structs currently pose a special challenge for us when they are to be used in a 
polymorphic way. As an example for further explanations, these Franca 
definitions shall be used:

    method testMethod {
        in {
            TopStruct t
        }
    }

    struct TopStruct {
        Int32 a
        Boolean b
    }

    struct SubStruct1 extends TopStruct {
        enum1 c
    }

    struct SubStruct2 extends TopStruct {
        enum2 d
    }

    enumeration enum1 {
        Val1_1 = "0"
        Val1_2 = "1"
    }

    enumeration enum2 {
        Val2_1 = "1"
        Val2_2 = "3"
    }

When calling the method "testMethod" within some generated code, it obviously 
should be possible to pass a struct either of type TopStruct, but also of type 
SubStruct1 or SubStruct2 as input parameter.

The problem starts there:

1. A service receiving such a method call may know whether the struct provided 
is of type TopStruct or of one of the  other two possible types, but has no way 
of knowing which of the subtypes was sent. Whether we are on D-Bus or on any 
other middleware, the subtypes will have have an identical signature. This 
requires the implicit addition of information (e.g. an integer value before the 
struct, or wrapping all possible struct data types in a variant type) that 
clarifies which it is. This addition of information however changes the 
signature of the method.

2. This addition of information is NOT necessary in the basic case, i.e. if 
there only the TopLevel struct type is defined, but neither of the subtypes. 
Also, in legacy (non-Franca) services normally nothing but a struct will be 
sent, i.e. no additional (unnecessary) type information will be provided, or it 
is provided in a different way. This implies that legacy services would be 
incompatible if this additional information is sent implicitly when using 
Franca generated code.

With 2, it is not sensible to always generate the additional information. With 
1, the additional information becomes necessary once an input parameter can be 
polymorphic.

Automatic recognition of polymorphic data types and the respective addition or 
omission of the additional information has another problem: If e.g. a developer 
starts with his basic interface design, providing only the TopStruct data type 
and starts to implement his client on top of it, and then another developer 
implementing the service extends this interface and introduces the subtypes, 
the wire format of the data sent by the client and received by the service 
becomes incompatible, due to the client not providing the additional 
information the service requires. This scenario can be extended to arbitrary 
similar use cases.

Original issue reported on code.google.com by rauw...@itestra.de on 15 May 2013 at 12:34

GoogleCodeExporter commented 9 years ago
//continuation of above

Adding a "polymorphic" keyword to the Franca level in order to identify 
polymorphic types (presumably none besides structs) would move this design 
choice to the interface developer and would ensure compatibility between 
different Franca interfaces and to non-Franca interfaces.

Original comment by rauw...@itestra.de on 15 May 2013 at 12:51

GoogleCodeExporter commented 9 years ago
Syntax proposal:

struct MyStruct polymorphic {
  // ... put elements here ...
}

Proposed semantics:

- The flag "polymorphic" may be set only if the struct doesn't inherit from 
another struct (i.e., either "extends" or "polymorphic").
- If polymorphic is set, structs given as method arguments may be used in a 
polymorphic way. The underlying infrastructure (or: any downstream tool) has to 
provide the necessary implementation for supporting this.
- If polymorphic is not set, root structs (those with no base struct) will be 
sent as-is, even if there are derived structs. If a derived struct is provided 
as actual parameter for a method call which expects its base struct, object 
slicing might be supported (i.e., only the base struct's elements will be 
transported, all elements from the derived struct will be ignored). In OO 
languages, this will likely be supported anyway.
- If polymorphic is not set, derived structs can still be given as method 
arguments. The elements of the base struct will simply be aggregated.
This scheme applies not only to methods, but to broadcasts as well. It extends 
naturally to inheritance hierarchies with more than 2 levels (at least I hope 
so).

This feature is an extension which doesn't break compatibility of the IDL as 
well as downstream tools. If there is no veto for this RFC (see the deadline 
CW23), it will be implemented in Franca IDL ASAP.

Original comment by klaus.birken@gmail.com on 15 May 2013 at 3:07

GoogleCodeExporter commented 9 years ago

Original comment by klaus.birken@gmail.com on 15 May 2013 at 3:08

GoogleCodeExporter commented 9 years ago
Sorry for the late feedback. Assuming you're still interested in my comments, I 
would like to add my 5 cents:

- I would propose to specify the "polymorphic" flag there, where the actual 
datatypes are used (i.e. as method arguments, attributes or broadcast 
arguments).
- This way the interface designer has the possiblity to decide by himself if 
e.g. methods shall support polymorphic datatypes or not. Following your 
proposal, the original datatype designer (can differ from the interface 
designer) decides this, once the root struct will be defined.

What is your opinion on that point?

Original comment by christop...@googlemail.com on 11 Jun 2013 at 12:53

GoogleCodeExporter commented 9 years ago
With regard to your point:

- This way the interface designer has the possiblity to decide by himself if 
e.g. methods shall support polymorphic datatypes or not. Following your 
proposal, the original datatype designer (can differ from the interface 
designer) decides this, once the root struct will be defined.

That is exactly what we want by design, as otherwise it becomes very difficult 
to create a functional RPC implementation which does not break compatibility 
between base and extended interfaces. What we wish to support is that if you 
have a service which uses an interface, say A, and a client which uses an 
interface B, which extends A, they can communicate. If B could declare the 
datatype polymorphic on its own, we would have no way to ensure wire 
compatibility with interface A.

Original comment by scha...@itestra.de on 11 Jun 2013 at 1:02

GoogleCodeExporter commented 9 years ago
Final RFC result (deadline 2013/cw23): There has been no veto against the 
proposed solution. The polymorphic-keyword will be implemented as described in 
the attachments and comments above.

Original comment by klaus.birken@gmail.com on 12 Jun 2013 at 9:43

GoogleCodeExporter commented 9 years ago

Original comment by klaus.birken@gmail.com on 12 Jun 2013 at 9:45

GoogleCodeExporter commented 9 years ago
Implementation available, will be part of v0.8.8.

Original comment by klaus.birken@gmail.com on 12 Jun 2013 at 11:30

GoogleCodeExporter commented 9 years ago
With regard to comment #5:
May my statement was unclear: 
- I did not mention that the datatypes can be defined separately "polymorphic" 
within different interface descriptions.
- I try to explain my idea based on a simple example: Assume we have an 
interface A containing a method B with an argument C of type D. In this case, I 
proposed to specify for argument C, if it shall be used polymorphic or not.
- In the mentioned example, it is clear for all "customers" of the interface 
(service, client), if the datatype D used for argument C shall is polymorphic 
or not. And then I do not see compability issues, right?

Original comment by christop...@googlemail.com on 14 Jun 2013 at 8:21

GoogleCodeExporter commented 9 years ago
Re-check solution according to comment #9.

Original comment by klaus.birken@gmail.com on 21 Jun 2013 at 11:25

GoogleCodeExporter commented 9 years ago
Here is an example which doesn't work when polymorphic is used for a method 
argument, not for the whole struct inheritance hierarchy:

interface Base {
    method doSomething {
        in { BaseStruct arg }
    }

    struct BaseStruct polymorphic {
        UInt16 a
    }
}

interface Derived extends Base {
    method doSomething {
        in { DerivedStruct arg /* polymorphic */ }
    }

    struct DerivedStruct extends Base.BaseStruct {
        UInt16 b
    }
}

A platform (especially Common API) may support full polymorphic calls in that 
situation, i.e., if the client is calling method Derived.doSomething and the 
server implements Base.doSomething, both should have a common understanding 
about how the struct is serialized. If polymorphic, it is serialized as a 
tag/data combination, if not polymorphic, it is serialized as a simple compound 
of actual values. This requires the generator to have a unique setting for the 
polymorphic-flag across the whole struct-inheritance tree. It is not sufficient 
to attach it to e.g. an argument, because there might be different settings for 
the same method in different interfaces.

If we agree on this example, the current implementation (Franca 0.8.8 and 
later) seems to be the only possible solution here.

Original comment by klaus.birken@gmail.com on 28 Jun 2013 at 3:36

GoogleCodeExporter commented 9 years ago

Original comment by klaus.birken@gmail.com on 11 Jul 2013 at 11:27