Jump to content

Tomi Maila

Members
  • Posts

    849
  • Joined

  • Last visited

  • Days Won

    9

Posts posted by Tomi Maila

  1. I wanted to test the for loop trick with N=0 for getting a default value of a LabVIEW class instances. I tested with LV 2010 on OS X. It doesn't seem to work at all. See the attached picture and project for LV 2010.

    In the top wire I would expect the wire to have a default instance of the Parent class but it has a LabVIEW Object. The second wire has Child class instance default value as expected. The third and fourth wire have Parent class default value when I was expecting Child class default value for both wires.

    Did I understand something incorrectly?

    post-4014-0-90494800-1296332211_thumb.pn

    Default Value Propagation Fail.zip

  2. Just had a strange behavior with a property node. Class wire was wired to a property node with 4 read properties. The value of the class wire (non-DVR) was right according to the probe just before the property node. There was a private data called DBL one of the read properties accessed. When the property node executed, the accessor was executed, but the object flowing from the input terminal of the accessor was the default value of the class, not the value of the wire connected to the property node. The property node returned incorrect value i.e. the default value. When the property node was replaced with a read accessor, it worked properly. When I replaced the read accessor again with a property node, it worked properly. So it seems that there is some, possibly optimization, that takes place, and the result of which, the accessor does not get executed with proper input buffer wired to its input terminal. This would not happen normally, but under some rare circumstances where possibly the classes have hanged when the property node already was placed on the block diagram.

    Tomi

  3. Did anyone have any success on this? I would like to add a page for annotating classes and class items such as methods. LabVIEW scripting provides a way of annotating items but these annotations are not exposed via LabVIEW IDE user interface. I would like to add a page similar to library items settings that would allow adding custom annotations to each item.

  4. If someone wants to create a localized version of a class they cannot modify, they should wrap the class and any parent classes that have methods/properties that need to localized too.

    I really hope that NI will not localize class property names either as then the child class developers' classes would only work with one localized version of LabVIEW. This would be a nightmare to LabVIEW Tools Network tools developers. Assume there is a class in <vi.lib> that uses localized properties. LabVIEW tool developer creates a tool has a class that inherits from this class in <vi.lib>. The tool is distributed via NI Tools Network. Now someone in Germany downloads that tool with German version of LabVIEW. It will not work, because the localized property names are not matching with the localized version of LabVIEW. The German LabVIEW developer cannot used but German versions of reusable LabVIEW tools. Is this really desired behavior?

  5. The long name must be the same.

    ...

    You need to copy the class file and set the localized names on it (using LabVIEW, a text editor, or scripting), then ship the localized class file with your localized product.

    Mike, there is a contradiction in what you are saying. You are saying that the long names can be localized, but on the other hand the long names must be the same the whole inheritance hierarchy, i.e. the long name together with the DataName specify the interface of the property to child class developers.

    Assume I create a class A. I create a property Length with the following names

    DataName = Dimensions:Length

    Name = Length

    LongName = Dimensions:Length

    ShortName = Dim.Length

    I publish my class for developers and people in the LabVIEW community create decedent classes of it using these property names. Now I want to localize my class for Finnish market (I am originally from Finland). So I create a localized version of the lvclass file for Finnish market with the following names.

    DataName = Dimensions:Length

    Name = Length

    LongName = Mitat:Pituus

    ShortName = Mitat.Pituus

    This should be possible, as you told the Long and Short names can be localized. However, now according to the requirement that the Long Names must always be the same throughout the inheritance hierarchy, the descendant classes created by the community no longer are compatible with this localized class.

    My interpretation is that localization should never change any interfaces of a class. If long name is required to be the same, then interface is changed.

    EDIT: Just tested the above mentioned behavior and changing the localized names indeed breaks the inheritance hierarchy of your class. If you are intending to allow third party developers to inherit from a class that you develop, my recommendation is that never use localized names.

  6. Believe it or not, there are four names. All are exposed via scripting; three are exposed in the editor. Here's a first second draft of something I typed for a KB:

    ...

    I hope that sufficiently explains the names associated with a property node. Let me know if you need more information.

    Thanks Mike, that was a comprehensive answer. Now I assume I know how to change those names in scripting and editing lvclass files. But how do you have an access to all those names in the class editor (LabVIEW project)?

    Second question is that do the names in an inheritance hierarchy need to be the same or is it enough that the DataName is unique? If there are conflicting definitions for long name and short name, will LabVIEW show an error? I ask this because if I create a class, specify the long and short name for a property, then someone creates a child class for that class and wants to override the property accessors, does he or she need to set the long and short names to be exactly the same or would LabVIEW use those of the parent class that first defined the property?

    Third question, how do I localize the long name and short name, not likely I am going to do that but just in case I might?

  7. Hi All,

    Happy Holidays! I am back for more. JKI has been keeping me busy. Property Definition folder LabVIEW project item has properties DataName, LongName and ShortName. When you look at LabVIEW class file, the XML contains all three types of names for a property definition folder as well. However LabVIEW IDE does not expose these names to the user, at least not in a way that I can change anything but one common name.

    Is there a difference between all these three names? Does LabVIEW differentiate between the names? If not, is this planned for the future?

    Cheers,

    Tomi

  8. Felix, I am sorry we really don't have functional programming at all in LabVIEW. What we have is OOP. The blog post was intended for more general audience of dataflow programming languages than just LabVIEW developers. In functional programming you can pass all functions as a parameter to other functions.

  9. I posted a new article concerning the general concepts behind design pattern that may be clear to many LabVIEW developers but not software engineers of other programming languages. Further I updated the example LabVIEW project with a worker pool that supports recursive worker instance reusage.

    Please copy paste the link below, direct linking to LAVA is prohibited by a LAVA bug.

    expressionflow.com/2009/11/10/unlimited-parallelism-concurrency-with-recursive-dataflow

    Unlimited parallelism & concurrency with recursive dataflow

    Tomi

    • Like 2
  10. I was thinking of an alternate solution, that may be little more complex. Assume you have a sequential worker pool and a parallel worker pool. Sequential worker pool is a loop that dequeues commands and executes them in a sequence. Parallel worker pool on the other hand consists of a single sequential worker pool and a recursive call to parallel worker pool itself. Within the parallel worker pool, the sequential worker pool and parallel worker pool are running in parallel. Neither of these, however listen to the main queue, but a new queue is created for both the parallel worker pool and sequential worker pool instances. In parallel to both of the two worker pools, there is a dispatcher loop. The dispatcher loop listens the main queue. Once it contains elements, it either submits the element to one of the two queues representing the two worker pools, parallel and sequential running in parallel. If the sequential worker pool is busy executing a task, then the dispatcher submits the incoming task to the parallel worker pool. If sequential worker pool is free, then it gets the task.

  11. Ah, indeed. That doesn't make very much sense, does it? I think what I needed was a while loop inside the worker pool VI to accomplish what I was describing.

    Now if there is initially a single task in the queue, the outermost workerpool instance starts executing it. The next worker pool exits because of a timeout. Now submitting a next task to worker pool needs to wait for the first task to finish. So it doesn't get executed in parallel with the first task. If the first task is locked waiting for second task to do something prior to proceeding, then the program ends up into a dead-lock.

    One question: what does this give us over just using a parallel for loop? I vaguely feel there's some differences, but I'm not 100% sure what they are.

    The number of workers in the worker pool for parallel loop is limited by a development time set upper limit. Furthermore the number of workers is fixed at the time when the loop starts executing. Unlike the proposed architecture, you cannot dynamically at runtime increase the number of workers as needed. Therefore it suits to parallelizing a mathematical analysis where tasks may be independent but doesn't suit very well for situations like dynamically adding listeners for a network connection.

    One thing that came to mind as an interesting use case for this was in the context of a Queued State Machine.

    State machine architecture is indeed something I had in mind when I came up with the worker pool design pattern. To be more precise, an ability to have parallelly executing substates to represent a single outer state. I am not yet ready with my state machine design but I will blog on it once I have solved the problems I am still having.

  12. I might add one thing to your Queue-based pattern.

    This is exactly the kind of discussion I wanted to raise. I would like the community to get help from the community to develop the best way to use this design idea. Ragglefrock, in your example there is a problem. The recrursive call to the worker pool exits immediately if there are no longer stuff to process. The outer most worker pool exits after it has finished its own task. So that worker pool doesn't wait indefinitely for new commands but exits after it has processed the first command in the queue. Indeed it functions as the dataflow worker pool, executing all the commands that are in the queue when the worker pool is started and then exits.

  13. So all we are really talking about is a "macro" for creating polymorphic vis?

    Something similar. We are talking about some technique of creating code acting similar to polymorphic VIs, without the need of duplicating the code. But not only that. We are also talking about custom _polymorphic parametrized types_. An example of a polymorphic type in LabVIEW is array. Array can contain any datatype imaginable and array type includes the information of the array element type. Even though LabVIEW has polymorphic parametrized types such as arrays and queues, developers are currently not allowed to develop new such types.

    Tomi has a much better understanding of the differences between c++ templates, type parameters, and other generic implementations. He might have more to say regarding this question.

    Unlike with C++ templates, with generics the possible runtime types are not known at compile time. Of course the all possible types are known, but that type space is much too large, so a binary code cannot just be generated to all possible types. Generics are not replaced by the exactly typed counterparts at compile time like C++ templates. Instead a generic binary code is generated, or alternatively binary code is generated just-in-time before at execution time.

    [*This leads me to another question for Tomi. As far as I know generics in other languages don't produce new source code--there is a clear distinction between pre-compiled code and post-compiled code. In Labview that distinction is muddied... in a sense all our block diagrams are post-compiled code. Would the code generated by a generic be saved as a separate vi, in which case the feature behaves almost like a toolkit, or do you create it dynamically on an as-needed basis?]

    As I mentioned above, a generic binary code for generic functions is generated. So for generic classes there would be a single generic class. For generic VIs there would be a single generic VI with a single binary code. As LabVIEW VIs have a state (controls, uninitialized sift registers), this is the only way to implement generics in LabVIEW I can think of. In LVOOP method VIs with dynamic dispatch input terminals, the method call is bind to actual method VI at runtime. In a similar manner in generic programming, the method calls to primitives or other subVIs are bind at runtime depending on the runtime type of the generic. As the type checking is happening at compile time, there always exists a method or primitive to be called. Runtime type checks can never lead to a situation where the generic code cannot be executed.

    EDIT: I was checking details of C# implementation of generics. When primitive types like Float and Int are used for type parameter, a separate code is generated at runtime. When class types are used, then code is shared betwen different types. That is List[Float] and List[int] would have their own binary code but List[string] and List[List[Float]] would share the binary code. In all cases, the code is generated at runtime, when first needed.

  14. I think I see what you are saying.

    Err.. I think you misunderstood. Sorry, I need to try to be more clear. I am talking about only static i.e. compile time type systems. Forget dynamic languages for now, I am not referring to dynamic type systems. I don't think dynamic type systems fit to LabVIEW dataflow paradigm very well.

    In a statically typed language like LabVIEW, type checks are done at compile time. In LabVIEW the type checks are done actually at development time, but it doesn't matter for now. The whole idea of static type checking is to prohibit type mismatch at runtime. This far we both agree.

    Now generic programming IS NOT the same thing as dynamic type system. Generic programming can be made 100% type safe using type parameters and compile time type checking with a static type system. All type related bugs can be avoided like currently in LabVIEW. The actual type comes into play when you place a VI that uses generic types on a block diagram. When you connect a wire with a specific type to a input that depends on some type parameter T, the type parameter T gets bound to the type you connected. That fixes the type of all inputs and outputs that depend on this particular type parameter. In dynamic programming, the type of the output terminals would be of type Dynamic. With type parameters the type of the output is fixed to something specific instead.

    As an example, consider a method creating a LIFO (last in, first out) stack of anything. In dynamic programming you would have an input of type Dynamic. The output terminal returning the stack would be of type Dynamic as well. In type parameter based generic programming the input would be of generic type T and the output would be of type Stack[T] (stack of items of type T). When you connect say integer I32 to the input of the create stack method, then the output would become fixed to a type Stack[i32] (stack of items of type I32).

  15. I agree there are times when Labview generics would be useful. How would you implement them in a way that maintains Labview's "everything is data" paradigm?

    I guess you are referring to the fact that VIs using generic types need to be runnable. That is indeed a good question. What is needed is a default value for generic types used as type parameters. For unrestricted type parameters this should be rather easy; there is no need for any particular value. So unrestricted type parameters would behave the same as LabVIEW Object class. The default value would equal to the top most class in the class hierarchy. The default value of a generic type with a single upper bound can be represented with the default value of the upper bound type itself. The default value of a generic type with a single lower bound can be represented with a default value of the parent class of all other classes, which currently is LabVIEW Object class.

    The more challenging is the case when multiple upper bounds have been used for a single generic type. The usage of multiple upper bounds from different branches of class hierarchy is possible only if either mixin classes are supported or interfaces are supported (I prefer mixin classes over interfaces), or there is some other kind of support for multiple inheritance. Let's consider the option of mixin classes which would in a LabVIEW implementation have a default value. Let S be any type that inherits from any two classes U1 and U2. For this to be possible, S needs to be a class in type space defined by direct sum of U1 and U2. The same applies for any generic class T that has upper bounds U1 and U2. So the default value of the generic type parameter T that has upper bounds U1 and U2 would be a direct sum of default value of U1 and U2. In less mathematical terms, the default value of T would be a data structure similar to a two element cluster with default value of class U1 and default value of class U2. For more that two upper bounds, this would generalize as a direct sum of upper bounds {Ui}.

    So far we have assumed upper bounds are non-overlapping. That is for any two upper bounds U1 and U2, there is no class Q in the inheritance hierarchy for which Q is a common ancestor for both U1 and U2. The easiest solution would simply not allow this kind of type bounds and also disallow multiple inheritance from common ancestors trough two different inheritance branches, unless of course this ancestor class is LabVIEW Object or similar built-in class. There are other options as well, but they are not relevant for the discussion now.

    • Like 1
  16. I don't get it :unsure:

    Labview IS a strictly typed language (only functions of the same type can be connected and a conscious decision to "cast" must be made to other types) as opposed to (say) PHP which isn't (anything can be assigned to virtually anything). Delphi is another example of a strictly typed language whereas C is loosely typed (I think).

    The add function you describe is a strictly typed primitive with polymorphism which, in other strictly typed text based langauges, would be an overloaded function.

    So I don't really see what your getting at. What am I missing?

    Maybe you are misunderstanding the concepts of static (strict) and dynamic type systems. Static type system is a type system that doesn't allow runtime type errors to occur whereas for dynamic type system runtime type mismatches can occur. Static type system can still be very flexible, indeed almost as flexible as dynamic type system. Type parameters allow creating statically typed functions and classes etc. with polymorphism without actually needing to duplicate code. You can for example write a QueueClass class that behaves exactly the same as queue primitive functions in LabVIEW including similar kind of polymorphism for both queue element type and queue reference type. Unlike with dynamic languages, you could never connect something with incorrect type to QueueClass methods, in exactly the same way as you cannot do the thing with LabVIEW primitives. So what you could get with proposed type parameter bases type system is LabVIEW primitive like flexible polymorphic behavior for custom developed types.

    Type parameters is one way to do generic programming. Quoting wikipedia: "Generic programming is a style of computer programming in which algorithms are written in terms of to-be-specified-later types that are then instantiated when needed for specific types provided as parameters." Type parameters is about specifying to-be-specified-later types defining relations between these generic to-be-specified-later types as well as setting limits to generic to-be-specified-later types. So you can do a lot of

  17. Hi

    I posted a new article to ExpressionFlow regarding type parameters. For me it is the one of the most missed features in LabVIEW. Check out the post.

    Introduction to Type Parameters

    EDIT: Seems like the links to expressionlfow still don't work from LAVA. Please copy paste from below

    expressionflow.com/2009/10/23/introduction-to-type-parameters/

    Tomi

  18. Is there official NI terminology for the concept I called fully connected? What do you think should be the best term for the concept? Somehow it resembles mathematical concept of connected manifolds. Maybe reverse or inverse connected could be more clear. They somehow tell that input need not always terminate to output terminal but instead output terminal must always originate from input terminal.

    I can't say it has an official name but that wire pattern over-laps with the rules for working in-place. This thread on the Dark-side

    Inplaceness is a competely parallel orthogonal concept. It is a compile time optimization technique used by LabVIEW to reuse buffers. The concept of inverse fully connected wires is about type propagation, if runtime output type can be guaranteed to be a function of runtime input type(s), irregardless how the VI is executed.

×
×
  • Create New...

Important Information

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