02-12-2022 12:03 PM
I do a lot of bundling actor messages to interfaces and eventually stumble across sending a message to an actor that does not inherit the needed interface. This may happen when taking the wrong enqueuer from a cluster or whatever, but it gives no hint during design-time is sometimes is hard to find.
Last time this happened to me, I wondered if I could not just make a subclass enqueuer, that is returned only from those actors that I choose (those that implement the according interfaces) and is used to speak with the actor, preventing me from using the wrong one. This is propably a bad Idea (like most things that interfere with the basics of the AF) and I can not do this, because the enqueuer class is hardcoded into the actor starters and its creation can not be overridden.
But I can subclass the enqueuer class and store it where I want, and I can return it from my actor using a custom method, but this is pointless as long as I cannot somehow cast the actual enqueuer to this more specific class.
So my more philosophical question is: May there be any good way, to bind the type of an actors enqueuer to the actual skills of an actor?
Solved! Go to Solution.
02-12-2022 12:13 PM
Honestly I think this is a great idea and is one of my criticisms of Actor Framework.
I think it should be possible with Interfaces, but then again I do seem to remember a conversation with AQ, where he said that is not possible for some technical reason. Maybe my memory is faulty. I'm sure he'll chime in.
02-12-2022 07:58 PM
02-12-2022 08:07 PM
Ok. Yeah that is pretty much what I remembered.
thanks for the link.
02-12-2022 08:09 PM
if you want that behavior, just use DQMH. It is typesafe. It's impossible to send a message to a DQMH module that it is not expecting.
02-13-2022 02:39 AM
Thanks for pointing me to that discussion, I really tried to find anything about this topic but missed this one.
Ok, so the "automatic" binding of an enqueuer to the interfaces of its actor is not (yet?) possible, but maybe letting the author doing it manually when he wants to, is. This could easily be achieved by letting one "set" the type of the enqueuer that is returned by the launch and read VIs (eg. by creating an overidable "Create Enqueuer Object"), even though the user has to cast it to the right type... maybe a begin.
DQMH may be an alternative, but I really like the new way of using actors with interfaces and want to stick to it. The type unsafety of the enqueuer is a little nasty, but not really a big problem right now.
02-13-2022 03:05 AM
My understanding was that basically to do it in a satisfactory way would require some (not insignificant) language changes to support more than the most basic arrangement (a basic suggestion being my first post, the problems with extending that to a more general case being in AQ's reply).
You might have better luck creating an override of Actor that changes the handling of unexpected messages.
More specifically, I think you'd want to change the behaviour on error, which is probably the To More Specific in the Do.vi, so that error would be generated and then you'd want to catch it and do something different, like continue the Actor's execution and log the error or something.
However, you could also just try catch errors in the general sense, as you said, this error is typically the equivalent of a typo and so probably it's better to fix it than code a large collection of changes to work around it. (An exception being if you have a collection of enqueuers and you know some will error when you try send, in which case catching then handling is the better choice, perhaps via inheritance)
02-13-2022 09:58 AM
I'd want to understand the specifics of your use case a little better before I endorse this 100%, but if you're looking to make sure that messages from inherited interfaces only get to actors that support them, this utility might also work for you:
https://www.vipm.io/package/zyah_solutions_lib_zyah_af_msg_forwarding/
(Full disclosure, my company created this.) One situation where we find this extremely useful is when you have an application with dynamically loaded plug-ins that could have different sets of interfaces from which they inherit.
If that sounds useful to you, you can read more about the details of the utility here:
https://www.zyahsolutions.com/blog/af-msg-forwarding-utility
Or even watch my presentation about it from GDevCon NA here:
02-13-2022 02:25 PM
@Taggart wrote:
Honestly I think this is a great idea and is one of my criticisms of Actor Framework.
I'm not an expert in OOP principles and may be off base with this, but doesn't the way actors are used break encapsulation? It's always seemed "leaky" to me.
I feel like there is a layer missing. That within a given actor's lvlib, both the messages and the actor itself should be private. The public class should be a wrapper that exposes each message for that actor as a method of that class. Now instead of getting the enqueuer and using it to call the Send method of some message object, which due to the reasons AQ stated in the other thread really must accept the generic enqueuer as its parameter, you are getting the wrapper object and calling a method on that class.
In my mind, this eliminates two problems with directly utilizing generic enqueuers. The first being the issue in the original post of getting the wrong enqueuer from a cluster or other data structure, and the second is the similar error of storing an enqueuer in the wrong place. With the wrappers in place, you wouldn't be able to store the object in (e.g.) the cluster element unless the type was a common ancestor, in which case when you get the object back out you would need to explicitly cast to the more specific class in order to call any methods not belonging to the ancestor class. (If using such a cast, it seems reasonable to me to expect that you should know to handle the error that could occur if the cast is not valid.) Of course, the wrapper itself would need to store the enqueurer, but I can't think of any good reasons why it would store more than the one, so getting them mixed up should not be an issue.
This could be a step too far, but I think that properly designing a base class might even allow abstracting away the fact that the concrete class uses AF at all, allowing children to be built around other frameworks... Although that might add additional complexity that would be fraught with its own dangers.
I admit that I haven't attempted to build any of this, nor even really mapped it out formally. There could be some glaring reasons not to do this that I'm not seeing.
Actually, as I think more about it, I am seeing some issues around inheritance between concrete classes and some of the library/class limitations in LabVIEW. For example, now that I think more about it, marking the actor and messages private might make inheritance between concrete classes challenging since the actor could not be inherited from and messages would be inaccessible. Unfortunately, classes cannot contain classes, so protected is not an option. Community could be a work around, but requires each child to be declared a friend to the parent library. I dislike in general the idea that the parent class's containing library needs knowledge of the existing children, and this would especially be an issue for PPLs. Thinking through some scenarios to decide how big an issue I really think this is...
Well... This was a lot of spitballing while I procrastinated on going out to shovel some snow... By all means, tell me where else I'm wrong. 😅
02-13-2022 06:29 PM
@JimB. wrote:I feel like there is a layer missing. That within a given actor's lvlib, both the messages and the actor itself should be private. The public class should be a wrapper that exposes each message for that actor as a method of that class. Now instead of getting the enqueuer and using it to call the Send method of some message object, which due to the reasons AQ stated in the other thread really must accept the generic enqueuer as its parameter, you are getting the wrapper object and calling a method on that class.
In my mind, this eliminates two problems with directly utilizing generic enqueuers. The first being the issue in the original post of getting the wrong enqueuer from a cluster or other data structure, and the second is the similar error of storing an enqueuer in the wrong place. With the wrappers in place, you wouldn't be able to store the object in (e.g.) the cluster element unless the type was a common ancestor, in which case when you get the object back out you would need to explicitly cast to the more specific class in order to call any methods not belonging to the ancestor class. (If using such a cast, it seems reasonable to me to expect that you should know to handle the error that could occur if the cast is not valid.) Of course, the wrapper itself would need to store the enqueurer, but I can't think of any good reasons why it would store more than the one, so getting them mixed up should not be an issue.
You can do this. You might want more than one, but presumably as the library developer, you have less chance of mixing the enqueuers up than the library user (maybe also you, a day/week/month later, but now you're thinking about different things!)
Doing this can also make it easier to provide blocking calls, in some cases, if that's useful to you.
@JimB. wrote:This could be a step too far, but I think that properly designing a base class might even allow abstracting away the fact that the concrete class uses AF at all, allowing children to be built around other frameworks... Although that might add additional complexity that would be fraught with its own dangers.
I admit that I haven't attempted to build any of this, nor even really mapped it out formally. There could be some glaring reasons not to do this that I'm not seeing.
Actually, as I think more about it, I am seeing some issues around inheritance between concrete classes and some of the library/class limitations in LabVIEW. For example, now that I think more about it, marking the actor and messages private might make inheritance between concrete classes challenging since the actor could not be inherited from and messages would be inaccessible. Unfortunately, classes cannot contain classes, so protected is not an option. Community could be a work around, but requires each child to be declared a friend to the parent library. I dislike in general the idea that the parent class's containing library needs knowledge of the existing children, and this would especially be an issue for PPLs. Thinking through some scenarios to decide how big an issue I really think this is...
The solution there would be to put more than one Actor in the same library. Or if you (or your tooling) really likes the scripted one-actor-per-library structure, then place the multiple libraries in another library (you can't have class in class, but you can have library in library) and make them private in that scope (not inside their individual libraries). Then no problem with them accessing each other. Still a problem if you want to build a PPL and have a later-designed and not-included-in-that-PPL child class inherit though.
For me an irritating (sort of, maybe not the right word) issue with private classes doing the bulk of the work in a PPL is that they become a little harder to debug (obviously only relevant if you build the debug PPL, not the release version).
You can't look at the class or its methods in the Project Explorer, although you can get to it by navigating through the block diagrams of public VIs, or via View > Browse Relationships > Unopened SubVIs
Might be something you'd be willing to give up to create your wrapper though.