Jump to content

variable scope checked at compile time only?


Recommended Posts

Is variable scoping (public, private, protected) in LabVOOP only performed at design/compile time, or is it also checked on each access at runtime?

I would think checking access permissions on each call to a variable at runtime would be prohibitively slow compared to compile-time-only enforcement of scope rules, but I'm wondering if LabVOOP might be doing this anyway to support the plug-in architecture design pattern using the Get LV Class Default Value node. This node gives you the incredible capability to load a child class into memory while an application is running and begin invoking override methods. If a method in this child class attempts to access a private data member in the parent class, what prevents it?

I'm hoping the only thing that prevents it is the fact that the child class should be broken at design time by the LabVIEW compiler, and attempting to load it using the Get LV Class Default Value should gracefully report an error that the child class has errors, but I'd like get a solid answer to this question.

Motivation for the question is curiosity and performance relative to C++, but I confess I have not attempted to do any benchmark testing.

I'm using LabVIEW 8.6 Pro on Win XP. Thanks,

Nate

Link to comment

OK, this is all guessing...

QUOTE (Nate @ Dec 18 2008, 11:11 AM)

It's certainly checked at compile time, because if you violate the rules, your VI will have a broken run arrow. I doubt it needs to be checked at runtime.

QUOTE (Nate @ Dec 18 2008, 11:11 AM)

... If a method in this child class attempts to access a private data member in the parent class, what prevents it?

The child VI can't access private parent data. The child VI will have a broken run arrow. If you try to load it dynamically in any way, you should get the normal error code for trying to run a broken VI.

QUOTE (Nate @ Dec 18 2008, 11:11 AM)

Oh, you beat me to it. Seems easy enough to test (I have time to write posts, but not to write code, sorry). I can't give you a solid answer since I'm not privy to the internals, but I just can't imagine this working any other way. Dynamically called VIs can be run by systems which don't even have compilers (using the run-time engine) so there's pretty much no way this could be evaluated differently at runtime (again, this is speculation).

QUOTE (Nate @ Dec 18 2008, 11:11 AM)

... but I confess I have not attempted to do any benchmark testing.

Well, let us know what you find out.

Link to comment

QUOTE (Nate @ Dec 18 2008, 01:11 PM)

Is variable scoping (public, private, protected) in LabVOOP only performed at design/compile time, or is it also checked on each access at runtime?
Hello, Nate. As the lead architect for LabVOOP, I can give you some authoritative answers.

  1. With regards to accessing data in the private data control, Paul is correct. All the data members of a class are always private, so checking that a VI does not attempt to unbundle or bundle any class other than the one it is a member of is easily done at compile time and doesn't need to be done any time else.
  2. With regards to accessing member VIs in a class, scope checking is done in four places:

    1. At compile time
    2. When a VI loads into memory
    3. When a VI tries to open a strict VI reference to another VI in memory (does not apply to a non-strict VI reference)
    4. When a VI tries to call the Run method of a non-strict VI reference

There is not any checking done at runtime for individual subVI calls -- we assume those are all checked when the VI came into memory or when it was edited after loading into memory.

QUOTE

I'm hoping the only thing that prevents it is the fact that the child class should be broken at design time by the LabVIEW compiler,

LV classes are designed to be binary independent of their ancestors, so a built app can load a revised ancestor class without having to have re-compiled versions of the descendant classes [see note below]. As long as the public API is unchanged, the children should be unbroken. But, like VIs, we check for the interfaces matching when we load the class into memory. Again, there is no overhead once we get running, but we do take a few processor cycles to verify that the dynamically loaded class is everything its parent said it should be.

[NOTE] When we were surveying folks years ago for what they would expect/need from LabVIEW classes, one of the significant items mentioned was this binary independence. "If my app dynamically loads a parent and its children into memory, I want the ability to ship to my users a new rev of the parent class without having to redistribute all the child classes, and it should work as long as the parent public and protected API is unchanged." To the best of my knowledge, with the exception of a single known issue (see below), this works. And yet thus far, 2.5 years after LabVIEW classes became available, I have yet to hear of any customer actually using this feature. If any of you are using it, I'd like to know how that's going.

The lone exception mentioned above is if you add a new private VI to the parent class, if any descendant class already had a VI of that name, it will break. Private VIs in the parent class should not affect the child classes in any way, but the bug is that they steal the VI name for all their descendant namespaces. This bug has existed since the original introduction of classes in LV8.2 and my team has repeatedly prioritized other work ahead of fixing it since we have never heard of anyone being actually affected by it -- it was found by users doing exploration of class features in the early days after the release. The bug would possibly get a lot more attention if anyone were actually doing independent rev distribution of classes for built applications.

Link to comment

Thanks for the post Stephen, I was hoping you'd chime in on it. Everything you said is as I expected, just good to get confirmation. Though I admit I don't understand the significance of loading strict VI references vs. non-strict when it comes to scope checking...

I think being able to compile the parent class and child classes independently is a great feature. I haven't had an opportunity to "deliver" an overriding class to a customer, or an application with a plug in architecture using child classes, but I'm delighted to have those as options. Thank you for the extra work I'm sure this must have taken the LV R&D team to make this happen.

I've read the Decisions Behind the Design, I've taken the GOOP training at NI Week, and I've done a fair sized project with LabVOOP now that does make heavy use of inheritance and dynamic dispatching, but I still have mixed feelings about data members being private only. Public data is bad, agreed, but having a protected data option would be very convenient, and I almost think it should even default to protected. The reason I say this is the same motivation as the customer requests that binaries be independent for parent and child classes. If I want to create a child class that extends the functionality of the parent class, it's almost guarenteed I'm going to need access to the data in the private class. If the author of the parent class did not forsee all the ways their class might be extended (which is an impossible task) and did not provide public or protected accessor/mutator methods for data in the parent class, AND if the author of the child class does not have access to the source code of the parent class to add accessor/mutators as needed, then the ability to extend functionality becomes very limited.

However, Decisions Behind the Design says this decision was not only made based on best-practices OOP theory, but also technical requirements in order to isolate the compilation of the child classes from the parent class. If that's the case, then I think you made the best decision in the long run (we just haven't experienced the benefit of that decision yet since the awareness/use of LabVOOP has not risen to the level where people will realize the power they've been given). I would ask that NI advertise this reasoning more in LabVOOP training and Dev Day presentations.

A real world example of when default protected data would have been nice: Sometime between LV 8.2.1 and 8.6 the NI Report Toolkit moved to OO. Unfortunately we had some code that was accessing some data in the Report Toolkit which became private when we upgraded to 8.6. This broke our code of course, but the easiest fix for us would have been to create our own class that inherits from the Report Toolkit class so we could access that data and go about our merry way. Now, I know what you're going to say, that's not the best way to do it because it means we're still directly accessing your data structures (breaking encapsulation), and if NI changes their private data structure it would break our class, breaking our code once more. Your right, but it illustrates that the use case of "deliver a class as a binary without access to the source" can only really be done if the author of the parent class knows exactly how all customers might want to extend the class, OR the author of the parent class creates protected accessor/mutator methods for all data members just to support future unknown child classes.

Instead we had to rewrite a good bit of code to avoid using that private data. Again, for the sake of protecting ourselves from future NI code changes, that is the right thing to do, and therefore the decision to make all data private is the safest thing a class producer can do to avoid breaking child classes on future updates. But in cases where the child class author does not have access to the parent class source, its also very inconvenient and limits the customer's ability to extend your classes.

Thanks for listening, your contribution to this forum is invaluable,

Nate

Link to comment

QUOTE (Nate @ Dec 21 2008, 03:00 PM)

But in cases where the child class author does not have access to the parent class source, its also very inconvenient and limits the customer's ability to extend your classes.
I'm only going to contest a single word of that sentence. Change "extend" into "hack", and I agree with you. Essentially, if access to the data is limited to the API, then, yes, you are limited to only the things that the author of the original class foresaw you needing to be able to do. But that could be stated another, more positive way: you are limited only to those things that the author actually planned for and (hopefully) tested. Setting the internal values of the parent class to arbitrary values in the child class may move your class in directions that may destabilize the object or give rise to unintended behavior -- even crashes, perhaps (not in LV [if we in R&D have done our jobs correctly], but perhaps in a DLL that you call out to]).

I realize that all of this argument is fairly philosophical, and yet I think the philosophy is a strong one for a language in LabVIEW's design space. LV classes really tried to balance the ideal against the practical, to weigh guiding users into good designs against forcing them into ridiculous academic constructs. The privacy of data was a major step in simplifying dialogs, simplifying design options, and supporting various other features through subVI connections only instead of having to constantly check for data connections too. We are not a hacker's language. We are a design, test and control language that seeks to be used to communicate directly with hardware, to maintain "always on" systems, and to provide all of this to folks who are not trained as computer scientists but are scientists and engineers in their own domains who need the computer to serve them instead of the other way around. Being a bit more "ideal" seems like a good thing in such situations, as long as we don't go overboard with it. For the moment, I think the balance is preserved, but I keep listening to feedback from users to make sure we keep it balanced as we go forward.

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.