Jump to content

Interface provider inspired by COM


Recommended Posts

Does anyone remember the object model of COM aka ActiveX? There's a base interface, IUnknown, that any COM object has to implement. IUnknown provides three methods:

- QueryInterface()

- AddRef()

- Release()

Interface hierarchies use single inheritance, but a COM object can implement multiple interface hierarchies. It can either implement them directly or aggregate other COM objects that implement them and expose these aggregated object via QueryInterface() to clients. Thus a single COM object can provide access to multiple and maybe unrelated interfaces.

OK, that's COM. Let's get back to LV now. If we want interface inheritance in LV we have almost the same restrictions as above. We have single inheritance and if we want to support multiple unrelated interfaces we have to use aggregation. The COM object model can help us here. Let's take a look at IUnknown again. AddRef() and Release() are methods for reference counting and we all know that references are evil (well, not always, I know:-) so these won't be of much help.

The interesting part is QueryInerface(). We can implement something in LV that very closely resembles that method. Imagine a dynamic dispatch VI that takes a LV object constant representing an interface, queries an internal repository of aggregates and returns the object that implements the desired interface. We don't even need to cast the result because we can use thralling.

We can even relax the rules of the COM object model:

The aggregated objects don't have to follow that model. They can be implemented using interface separation, the can be compound objects implementing QueryInterface() themselves but they can also be just LV objects with arbitrary bases.

Here is an example implementation. Enjoy!

candidus

InterfaceProvider.zip

  • Like 1
Link to comment

I like it. I've always liked the ability to dynamically query an object at run-time for a given interface as is done in COM.

For those who aren't familiar with COM and the trick that's getting played here, I've annotated the key thing that's happening with wire colors.

post-11742-0-63019600-1335798699.png

You can do some pretty cool things with the PRC primitive indeed.

Well done, candidus.

  • Like 1
Link to comment

Can't say I have any suggestions. I've gone down this road before in an attempt to implement something similar to IDispatch. I've been wanting some sort of reflection pretty much since LabVIEW introduced OOP support, but haven't been able to get any further than you.

Link to comment

...we want to support multiple unrelated interfaces...

To be Devil’s advocate, isn’t the need to put the word “unrelated” in there a bit of a problem? I’m not familiar with OOP interfaces, but isn’t the idea that interfaces are different ways of treating the same object? Representing a single object as a collection of different objects with no connections between them might work sometimes, but the instant you have a relationship (say, Child2 needs to call a method on Child1 in order to execute “Child2Info.vi”) then you have a problem.

— James

Fuzzy Idea: How about a design in which QueryInterface, instead of returning a copy of Child2 from Child1, takes a Child1(containing Child2) and returns a Child2(containing Child1)? I.e. the collective object is always the same (and each child can access the other), but the apparent identity switches between different interfaces.

Link to comment

Fuzzy Idea: How about a design in which QueryInterface, instead of returning a copy of Child2 from Child1, takes a Child1(containing Child2) and returns a Child2(containing Child1)? I.e. the collective object is always the same (and each child can access the other), but the apparent identity switches between different interfaces.

An experimental modification:

post-18176-0-18795900-1336481351_thumb.p

Run “Example B.vi"

Interface B.zip

Link to comment

To be Devil’s advocate, isn’t the need to put the word “unrelated” in there a bit of a problem? I’m not familiar with OOP interfaces, but isn’t the idea that interfaces are different ways of treating the same object? Representing a single object as a collection of different objects with no connections between them might work sometimes, but the instant you have a relationship (say, Child2 needs to call a method on Child1 in order to execute “Child2Info.vi”) then you have a problem.

I think we're talking about different kinds of (un)relationship. I thought primarily of unrelated interface hierarchies. In languages like C# or Java you can inherit from a single concrete or abstract class just like in LV, but also from multiple interfaces. In LV you have just single inheritance, so the only way to implement multiple interfaces is aggregation.

That doesn't mean that the aggregated objects are necessarily unrelated: They can be just delegates forwarding the method calls to the object that aggregates them. Here is an example:

ImplementerWithDelegatedInterfaces.zip

But you're right, in my first example they are unrelated. It's probably bad OO style :)

An experimental modification:

Just great!

So a reference version would look like this.

post-941-0-43473300-1336347935_thumb.png

Seems that we have to implement AddRef() and Release() now ;)

Edited by candidus
Link to comment

Are your examples meant to involve by-value or by-reference objects? I can see how they work for by-reference objects, but they wouldn’t work with by-value objects (since your making copies at every wire branch).

I want them to work with both. Do I have to use references inside my delegates or is bundling/unbundling inside IPE structures enough?. There's so much to learn... :lightbulb:

Link to comment

By value will work fine so long as you don't expect any modifications you make to the returned interface to affect the original object. This is perfectly valid in some cases, but not in others...

Link to comment
I want them to work with both. Do I have to use references inside my delegates or is bundling/unbundling inside IPE structures enough?. There's so much to learn... :lightbulb:

It will only work with by-value objects in limited cases. Every wire branch leads to a separate object. For example, in your last attachment, you have an “ImplementorInit” VI that returns five entirely independent “Implementer” objects; two in Child 1(at parent and child levels), two in Child 2, and an overall object that holds Children 1 and 2. If these were five references to a single by-ref object then you would be able to work on that object from any of your methods. But by-value you are working with different objects; changing one has no effect on the others.

In the code I posted, I’m trying to keep all the by-value objects together, with no copies, so any method in one interface can call methods on any or all of the over interfaces. Child2 can access and modify the Child1. Child1 can access and modify Child2.

— James

BTW: Daklu has an interface framework in the code repository. He uses a by-ref object (using a DVR).

Link to comment
  • 3 months later...

Reading through old threads I missed...

Daklu has an interface framework in the code repository. He uses a by-ref object (using a DVR).

The demo code included in the framework uses a by-ref object, but the framework works just as well with by-value objects. It's entirely up to the class developer to implement their Interfaceable classes using by-ref or by-value techniques.

Link to comment

Hi Daklu,

What do you think of my collection-of-objects-that-can-switch-between-type-identities idea posted above?

I do the same kind of type switching between the working object and the Interface object in the Interface Framework. It's the only way to get it to work with by-value classes. That said, there are some important differences between what you are doing and how the Interface Framework is implemented.

I am reluctant to call your example an Interface implementation. In your (and candidus') example each Interface class has its own data fields. Interfaces as a programming construct don't have their own data fields. The Interface just provides an alternate means of accessing the working object. Calls through the Interface operate on the working object, not on the Interface itself. What you have is more like a schizophrenic object--lots of separate identities rolled up into a single unit. I'm not saying there's no value in it, just that it doesn't strike me as Interface-ish.

(I am curious if you have a use case in mind. If your intent is simply to aggregate a bunch of functionally distinct classes into a unified whole, we have ways to do that that are much simpler to understand and use. Namely, composition and delegation.)

It is certainly possible to change the example code to remove the Interface objects' private data and have their methods delegate to the working object. That would be more Interface-ish, but it still doesn't quite fit the bill imo. One of the properties of Interfaces is the class designer controls what Interfaces the class supports. In the example code the class user controls what Interfaces are bundled with the working object. As a potential user of the framework, the mental picture I have is a bunch of objects thrown together in a bag where I can only pull out a single object at any given instant, instead of a single object with different views.

That could probably be "fixed" by limiting access to the SetAggregates base class method. That would bring it even closer to an Interface as commonly understood. (Or at least how I understand them.) But it also highlights another potential issue. There is only a single IBase class that all Interfaces and Interfaceable objects inherit from. Interfaces are not the same as classes. (Though some languages may implement them as classes under the hood.) Interfaces are a different construct with their own restrictions and limitations.

Since Labview doesn't natively support Interface constructs, we use classes to implement them. However, I think it is beneficial to continue considering Interfaces a distinct concept from classes. Maintaining separate class hierarchies for Interfaces and Interfaceables helps keep code understandable. If you made all three changes then I'd be willing to call it an Interface. Of course, if you did all that you'd also have essentially the Interface Framework, so take my opinion as worth about as much as the paper it's written on.

Ultimately any Interface implementation is going to be influenced by what properties of Interfaces the developer thinks are important. When I created the IF I was primarily interested in being able to create groups of objects with related functionality outside of the class hierarchy and operate on them using a single set of VIs, so that's what I implemented. Since then I've talked to others who are more interested in an Interface's must implement nature, as a way to make sure a class implements all the methods defined by the Interface. (As near as I can tell the must implement property is a side effect of the desire to create functional groups, but I'm not going to claim they are wrong for wanting that.)

---------

As an aside, there are a couple things about the original example project I thought were odd.

1. The existence of both an IBase class and an IBaseDefaultImpl class. I'm not sure the methods defined by the IBase class provide enough functionality to actually implement a functional Interface or Interfaceable object. In other words, any class implementing IBase is going to have to expose other methods to be usable, so there's really no point in having an abstract IBase class. I'd combine them into a single class.

2. I don't see the point of the parent/child objects in the example. It seems irrelevant to what the code is trying to demonstrate and only adds complexity where none is needed.

Link to comment

Thanks Daklu, that helps me understand “Interfaces” better. I hadn’t appreciated the fact that interfaces should have no private data themselves, and I see now how your implementation can work with by-value objects (I suggest adding a by-value example if you ever have the time). My design above was motivated by what I perceived as a desire to create some kind of combined object that can simultaneously belong to two independent inheritance trees (complete with the private data of both trees).

Link to comment

I suggest adding a by-value example if you ever have the time

At this point I don't have any plans to revisit it. I released the IF nearly 3 years ago after a year of off-and-on development, and almost immediately discovered I didn't need it. Even with the IF implementating Interfaces and Interfaceable is fairly cumbersome and rigid. I've found adapters to be a simpler and more flexible solution in nearly all cases where I wanted to use an Interface.

I still hope LV will someday support Interfaces (or Traits) natively and hide all that complexity from us developers. However, seeing as how the idea has only garnered 26 votes after 3 years on the Idea Exchange, and after having Interfaces lose a straw poll to stability and some other feature at the last architectural summit, I'm starting to wonder if we'll ever get them--or any other meaningful extension to LVOOP for that matter. It's unfortunate too. Although I love developing in LV I'm beginning to see the day when I'll "outgrow" (for lack of a better term) it on the distant horizon.

Thanks Daklu, that helps me understand “Interfaces” better.

I hope it didn't come across as overly critical of your efforts. I sat on the response for a couple hours trying to decide whether to post it as is or rewrite it to soften it up. Eventually I decided I didn't have the time or energy to rewrite it. Laziness triumphs again!

Link to comment
I've found adapters to be a simpler and more flexible solution in nearly all cases where I wanted to use an Interface.

I had wondered about that, once I understood interfaces to have strict rules about private data, as one can’t make an interface the descendant of any class that has any private data.

I hope it didn't come across as overly critical of your efforts. I sat on the response for a couple hours trying to decide whether to post it as is or rewrite it to soften it up. Eventually I decided I didn't have the time or energy to rewrite it. Laziness triumphs again!

Oh, it was just an idea I threw together in response to this conversation. Anyway, criticism is better than apathy. :)

Link to comment

[This comment led me to believe there might still be some misunderstanding about Interfaces. In typical fashion, what started as a brief response grew because I don't know when to shut up.]

I had wondered about that, once I understood interfaces to have strict rules about private data, as one can’t make an interface the descendant of any class that has any private data.

You can't make an Interface a descendant of any (non-Interface) class, period. I think some languages allow an Interface to inherit from another Interface, but since Interfaces contain no data and no implementation there's not much point in doing it as far as I can tell.

The Interface Framework really isn't the best way to learn about and understand Interfaces. It blurs the line between Interface types and class types too much. Also, the IF uses the concept of "concrete Interfaces" that are instantiated at runtime. I do this to emulate the ability to typecast a class into one of its Interfaces. Languages with native support for Interfaces don't allow an Interface to be instantiated. i.e. Concrete Interfaces are an oxymoron.

In many languages (java and vb come to mind) classes do not "inherit" from Interfaces; rather they use the keyword implements to indicate to the compiler that the class supports the Interface.


interface ISleepable {

  void sleep()

}

 

class Baby {

  implements ISleepable

 

  private bool isSleeping

 

  void sleep() {

	this.isSleeping = True

  }

}

[/CODE]

In C# the syntax for implementing an Interface is the same as the syntax for inheriting from superclass. Classes are still restricted to a single parent class, though they can implement as many Interfaces as necessary.

[CODE] // Class inherits from the Person class class BabyA : Person { } // Class implements the ISleepable Interface class BabyB : ISleepable { } // Class inherits from the Person class and implements the ISleepable and IGrowable Interfaces class BabyC : Person, ISleepable, IGrowable { } [/CODE]

As near as I can tell the idea of classes "inheriting" from Interfaces is a relatively recent convention. I prefer the "implements" terminology. It's less confusing to me.

Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.