Closed cmnrd closed 3 years ago
I suggest we disallow this in the grammar:
reaction(startup) -> foo[4].o
The cost of this is extra dependencies.
That would be an option. Actually, we have the same problem also in this case:
reaction(startup) -> foo.o[4]
What should we do when the multi port or multi reactor as a trigger. I think this has a bit more subtle implications:
reaction(foo[4].o)
reaction(foo.o[4])
If we were to disallow this syntax, we couldn't trigger on a specific port anymore, but would always trigger on all the ports. Then the programmer would be responsible for checking if a given port is present. I think that would be fine, but there is some overhead associated with this solution as it causes more reaction triggerings.
Our experience with multiports in Ptolemy II has never, to my knowledge, presented us with a use case where we would want reactions to be triggered by individual channels of the multi port, so I don’t think this restriction is likely to be burdensome.
So far, the C++ target does not implement the Lingua Franca scoping rules. This means if you do this:
The C++ compiler does not report any errors because
o
was not declared as an effect of the reaction. This has been an open issue for a while and I have been looking for an elegant way to implement the scoping mechanism. What I came up with works well for simple examples as above and is similar to what is done in C and TS. The generator simply brings the declared triggers, sources, and effects in scope by using an alias. It is a bit more complex in reality, but I think you get the idea from this simplified example:Now this gets a bit more complex if contained reactors are involved. For instance, with this program:
the reaction of Bar would be generated as follows:
This is a bit more complicated due to the struct that is needed as a proxy for the Foo reactor, but that is still ok. The compiler should be able to optimize those simple alias assignments away. Also note how
const
can be used in C++ to permit only read access to a port or action (startup in this example).So far so good. But it gets complicated as soon as we use an array instantiation. Consider this example:
How should the generated code look now? It needs to instantiate an array of aliases!
Note that the array initialization must be unrolled and a for loop cannot be used since the reference
o
infoo_t
must be initialized immediately on object creation. This could be avoided by using a pointer rather than a reference, but then the target code would need to use->
as infoo[x]->set(42)
. I am not sure what the compiler would do with this code, but I don't expect it to optimize the array away. So this could be a significant overhead in memory and computation time! The overhead in computation time could be mitigated though, by only creating the array once and keeping it for latter triggerings of the reaction.Now it gets even more complicated if our reaction writes only to one specific instance of foo. For example:
I actually have no idea how to achieve this...
Maybe you have some thoughts on how this could be solved? I assume there would be similar issues in C and Typescript for array instantiations of reactors.
One idea I have is to introduce an alias via
as
keyqord directly in Lingua Franca.What do you think of this?