How can I trigger onExpanderChange() event in my code? Or is this not possible?
It seems as though there is a bug in the VCV API that neglects to trigger onExpanderChange() when a module is deleted, and that is wreaking havoc with my Venom expander code. Thanks Dan (@soundismovement) for the bug report.
I have a crude fix that will work until VCV fixes the problem (or indefinitely if never fixed). But an even better solution would be if my Venom modules automatically trigger onExpandeChange() within onRemove() in lieu of VCV doing it.
It is an override, so I don’t think the override onRemove() will double trigger upon VCV fix unless I explicitly call the parent onRemove() from within my override.
I don’t think that works because if I make the call within onRemove(), then onExpanderChange() does not see any change because the module has not been deleted yet - get[Left/Right]Expander() still returns the module that is going to be deleted. I need VCV to call the neighbor module’s onExpanderChange() after the module has actually been deleted.
Maybe I am misunderstanding what you want to do, but it sounds like you are describing something that is impossible; once a module has been deleted you cannot call its methods.
Though, if this is what you are trying to do, perhaps there is a solution where you call the module before it is deleted and it sets a flag, and then you can do the work you need in the modules destructor if the flag is set?
Yeah - I really need VCV to properly call onExpanderChange after the module has been deleted. Maybe I could do something in the destructor, but something tells me I run into the same problem - the module is not really gone while the destructor is running, right? Either way, I don’t think I want to go down that road.
I have a hack that works. When one of my modules is deleted, my onRemove increments a plugin global venomModuleDeleteCount variable. All of my modules that rely on cached pointers to Venom expander modules maintain their own venomModuleDeleteCount, and if it does not match the global value, then I update my cached pointers using get[Left/Right]Expander, and then set the local del count to match the global value.
The hack works without any noticeable increase in CPU time.
If I simply abandon the use of cached pointers and always call get[Left/Right]Expander, then CPU usage skyrockets. My mixers support expander chains of arbitrary length, so the performance degradation can be a real problem.
Get…Expander should be an inlined dereference of a member, so shouldn’t consume much cpu. On the other hand, if you’re using dynamic_cast on the result, then that can be slow.
So, if you must use dynamic_cast, you can cache your resolved interface, but check for a null expander (without the cast) for invalidation in the absence of the notification.
just keep a Module *lastLeft;VenomModule *lastVenomLeft and only dynamic cast if the lastLeft changes.
Or just check pointer equality to look for change. The dynamic cast will change the type and do an rtti check but won’t change the pointer address in practice.
That is the whole point of using onExpanderChange - to cache and mintain the VenomModule* pointers. It naturally only does dynamic cast when there has been a change.
When I wrote the code I naively thought the get[LeftRight]Expander was slow, until @pachde set me straight that it is the dynamic_cast slowing things down. But either way, the design saves CPU.
I think when a module is deleted, VCV should automatically update the neighbors’ expander pointer and trigger onExpanderChange. But the trigger is not happening (and I reported this to VCV). VCV must be updating the neighbors’ left/right expander pointers, because if my code calls get[Left/Right]Expander I get the correct values. VCV is simply forgetting to trigger onExpanderChange when updating the pointers after a module deletion.
I was (again naively) hoping I could solve the problem by calling the neighbors’ onExpanderChange myself within onRemove. But I believe this doesn’t work because the module still exists while onRemove is running, so the neighbor pointers have not been updated yet.
So I have abandoned this entire concept, and fallen back to a different hack that reduces the number of times I call dynamic_cast. The hack is working just fine.