Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Achieving Interoperability

, June 01, 2002


June 2002: Achieving Interoperability

Last month, we discussed the power of .NET's multilanguage combination, but noted that C++ achieves it only through a major language update: Managed C++. Fortunately, this isn't the only possible approach. Languages can retain their own properties while benefiting from the common object model and its promise of full interoperability with other .NET languages.

Mapping Languages Into the Object Model

Each .NET language must be mapped into the object model; but who takes care of the mapping? The approach used with managed C++ put the onus on the language designer (to change the language) and the application programmer (to adapt the program). It's possible, however, to let the compiler writers do the job. This is good news for everyone—at least, everyone who isn't in charge of porting a compiler to .NET.

Multiple Inheritance From Classes

Unlike the .NET object model, Eiffel supports full multiple inheritance: A class D may inherit from classes A, B and C. In .NET, at most one of these three parents may be a class; the others must be interfaces, consisting only of methods devoid of any implementation. In Eiffel, there is no separate notion of interface; a class may be completely implemented, completely deferred (the equivalent of a .NET interface), or partially deferred, with some features implemented and others not. This full spectrum of class concreteness is a key feature of Eiffel, enabling you to start system analysis and design with very abstract classes, close to the problem domain, then bring them progressively and seamlessly to a more concrete state with fewer and fewer deferred features.

Each .NET language must be mapped into the object model (see "Mapping Languages Into the Object Model"). The tricky part is deciding who takes care of the mapping. The managed C++ approach put the onus on the language designer (to change the language) and the application programmer (to adapt the program). It's possible, however, to let the compiler writers do the job. This is good news for everyone—at least, everyone who isn't in charge of porting a compiler to .NET—since it means you get the best of all worlds: You can continue to use your language as before, yet rely on components from other languages.

The example of multiple inheritance in the Eiffel language (see "Multiple Inheritance From Classes") illustrates how language implementers can save the day.

Unlike the .NET object model, Eiffel supports full multiple inheritance: Class D may inherit from classes A, B and C. In .NET, at most one of these three parents may be a class; the others, as we know, must be interfaces, consisting only of methods devoid of any implementation. In Eiffel, there is no separate notion of interface; a class may be completely implemented, completely deferred (the equivalent of a .NET interface), or partially deferred, with some features implemented and others not. This full spectrum of class concreteness is a key feature of Eiffel, enabling you to start system analysis and design with very abstract classes, close to the problem domain, then bring them progressively and seamlessly to a more concrete state with fewer and fewer deferred features, until nothing is deferred.

So how can you make D inherit from fully or partially implemented classes A, B and C?

At first glance, you may believe that it won't work: The inheritance structure violates the rules of the .NET object model. The only solution, then, is to follow the C++ route and change the programming language, creating a single-inheritance-only variant. (Such a subset, called Eiffel#, was designed for Eiffel as a first iteration of the .NET version. Eiffel# was only a stepping stone and is now obsolete, as Eiffel for .NET now supports the full Eiffel language with multiple inheritance through the techniques explained in the following paragraphs.)

Taken from another angle, however, the problem is trivial. As long as it enables Eiffel code to use multiple inheritance, the compiler can hide any signs of inheritance from the rest of the world. Assuming, for example, that the classes A, B, C and D have features f, g, h and i, respectively, it suffices to show to the .NET world a class D that includes both its own immediate feature, k, and those inherited from its parents (f, g, h), and has no inheritance relationship to A, B or C. This is known in the Eiffel environment as the flat form of a class: an inheritance-free equivalent with all the features, local and inherited.

With this technique, the inheritance structure will still apply in the Eiffel world, with its associated techniques of polymorphism and dynamic binding; for example, with a1 of type A and d1 of type D, you may have an assignment of the form a1 := d1, but to the rest of the world, D is an orphan class.

The Real Challenge
This solution is not really acceptable, however. If you expose classes from an OO language to other OO languages, you'll want to expose their inheritance structure, also. With the solution shown, C# or VB.NET code wouldn't be able to create an object of type D and, polymorphically, assign it to a variable of type A, even though this is legitimate in view of the inheritance structure, and permitted in Eiffel classes.

We won't stop at this first solution, but it serves to illustrate a key observation regarding the possibility of providing on .NET a language feature that's not supported by the .NET object model, such as multiple inheritance or genericity. A gut reaction like ".NET can't support a multiple-inheritance language" is just silly. Supporting a particular language feature is a question for compiler writers. If you can compile multiple inheritance into C or into Intel machine code, there's no reason you couldn't do it for the .NET virtual machine and its Intermediate Language (IL).

But that's only part of the question: In this multilanguage environment, can we compile the construct and show other languages a reasonably accurate view of the original structure? We may not be happy with a compilation technique, in the multiple-inheritance example, that handles multiple inheritance but shows A, B, C and D to the rest of the world as if they were totally unrelated classes.

This is the true challenge of compiling a language on .NET that has multiple inheritance or some other advanced feature not directly supported by the object model: How much of the original set of properties can we retain in the view that we present to components written in other languages?

Relying on Interfaces
Let's see if we can show our partner languages a better view of our inheritance graph than just a flat structure with no inheritance links. One way to give them more clues would be to designate as "favorite," for each class, at most one parent class. Based on some criteria, D would admit to our foreign-language friends that, for example, it inherits from A, hiding the other classes. The advertised inheritance structure would then conform to the .NET rules.

This solution, however, is unattractive. Under what criterion could we possibly choose which parent to retain? Multiple inheritance is precisely intended to let a class combine several equally important abstractions.

Shadowing Classes by Interfaces

For each Eiffel class, the Eiffel for .NET compiler produces both an interface and an implementation class. The implementation class inherits only from its associated interface. The original inheritance structure between Eiffel classes is preserved among the .NET interfaces: You can see that D inherits from A, B and C, just as in the original Eiffel class hierarchy.

Eiffel for .NET offers a better solution: It takes advantage of the limited form of multiple inheritance permitted by .NET—multiple inheritance from interfaces (see "Shadowing Classes by Interfaces"). For each Eiffel class, the Eiffel for .NET compiler produces an interface named after the original class, as well as a related implementation class named by prefixing the original class name with "IMPL" and a period. Thus, if we originally have Eiffel class D inheriting from classes A, B and C, the complier generates .NET interfaces A, B, C and .NET classes IMPL.A, IMPL.B and IMPL.C. Each implementation class will inherit from the associated interface and from nothing else. The original inheritance structure between Eiffel classes, single or multiple, is preserved among the .NET interfaces: You can see D inherit from A, B and C, as the corresponding classes did in the original Eiffel.

All this is set up to allow writers of non-Eiffel modules to use the Eiffel classes through the resulting .NET interfaces, such as A, and not have to know about the implementation classes such as IMPL.A or anything else in the above scheme. A C# or VB .NET programmer may declare variables of the corresponding types, as an Eiffel programmer would in:

A your_A_variable; /* Declare variables with the appropriate types */<br>
  D your_D_variable;

You may then use polymorphism to assign a value of type D to a target whose type is any one of A, B or C; for example:

your_A_variable = your_D_variable

If a non-Eiffel class inherits from D, browsing tools—such as those of Visual Studio .NET—will show A, B and C among its ancestors.

What about creating the corresponding objects? Interfaces don't have instances or constructors; the objects you'll want to create are instances of the IMPL … classes, created using the appropriate constructors. The solution is to generate yet another .NET class, called CREATE.A, for any non-deferred Eiffel class A. The generated class contains all the constructors (Eiffel's creation procedures) for A, which non-Eiffel clients will use to create instances of A. This is easy to explain in the class documentation.

Note how this architecture takes advantage of various .NET mechanisms. In particular, Impl and Create are .NET namespaces. The result is to export the full power of Eiffel's multiple inheritance to any .NET language, even though the .NET model doesn't support multiple inheritance on its own.

The Language Bus
The implementation of multiple inheritance in Eiffel helps us understand the relationship between .NET's multilanguage nature and its object model.

At first, the existence of a single object model seems to contradict the claims of language openness. This has led some people to dismiss those claims as hype and state that .NET really imposes a single language, or at least a single set of semantics. This view seems confirmed by the example of managed C++, but the example of multiple inheritance shows that it's wrong. In .NET, languages aren't forced to adopt the common object model; any language can keep its own view of the world, as long as the compiler is able to translate it to that common model. In our original picture above (see "Mapping Languages Into the Object Model"), the responsibility of mapping rests with the compiler, not with the application programmers. It's also the compiler's task to let other languages retain as much as possible of the original view when they access the original software elements.

We've seen this work in the case of multiple inheritance: By cleverly using a common mechanism (multiple inheritance from interfaces), we can give other languages an essentially accurate view of the original inheritance structure, and let them pretend that they support multiple inheritance—even when they don't.

The Language Bus

What the common object model provides is not a stranglehold forcing all languages to support a single view, but a kind of language bus, enabling all languages to cooperate by agreeing on a basic set of common mechanisms. With the multiple inheritance example, we were able to present to the bus, and hence to other languages, a view that doesn't lose any essential property of the original model.

So what the common object model provides is not a stranglehold forcing all languages to support a single view; instead, it's a kind of language bus, enabling all languages to cooperate by agreeing on a basic set of common mechanisms, as depicted in "The Language Bus" diagram, a reinterpretation of the earlier figure.

Multiple inheritance was a felicitous case, since we were able to present to the bus, and hence to other languages, a view that doesn't lose any essential property of the original model.

Advanced Properties Not Shown to the Rest of the World
We won't always be as fortunate, however, since some high-level constructs may be impossible to emulate in the object model; for example, genericity. As noted previously, unmanaged C++ (through its templates) and Eiffel let you define type-parameterized classes known as generics. Generics ensure type safety. For example, you can define two LISTs with elements of type EMPLOYEE and INTEGER, respectively:

emplist: LIST [EMPLOYEE] ; intlist: LIST [INTEGER]

The compiler will then accept

emplist.extend (e) and intlist.extend (n) but reject emplist.extend (n) and intlist.extend (e).

Like Java, the .NET model and, consequently, languages such as C# and VB.NET do not support genericity: If you want the equivalent of a generic list class, you let it manipulate values of the most general .NET type, Object, and you cast back and forth between Object and the actual types needed, such as EMPLOYEE and INTEGER. This means that instead of compile-time checks of a generic language, you must rely on runtime type checks—a penalty in both software reliability and performance.

Here too, as with multiple inheritance, a generic language can continue to benefit from its own mechanisms and enforce its own rules; however, in this case, we don't find a ready-made technique to emulate these mechanisms in the common object model. So when a generic class such as LIST is made available to the rest of the .NET world, it will appear exactly as if it were a nongeneric .NET class, describing lists of Object values.

In the final article, we will discuss how to fully benefit from the language interoperability promised by .NET. Come back next month for a description of a key property of .NET, which, surprisingly, most current .NET books don't cover or even mention: the CLS, or Common Language Specification.

Multiple Languages in .NET
Rules to remember when compiling with the Intermediate Language

  • No language needs to change for .NET.
  • The first task of a .NET compiler for any language L is to map the constructs of L to those of the .NET virtual machine, just as it would do for any other target machine, so that modules written in L can make full use of L's capabilities and communicate with each other exactly as they would outside of .NET.
  • The compiler's second task, to let L play its full part in the .NET interoperability game, is to produce, for each of the constructs of L, an equivalent or emulation in the .NET object model, allowing other languages to interact with L modules in terms of common concepts (see the figure, "The Language Bus," above).
  • The compiler may be able, for some constructs, to retain all of the original properties, as in the case of Eiffel's multiple inheritance; or it may have to forsake some of the more advanced properties, as in the case of genericity.

 


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.