davidsiaw / luacppinterface

A Simple C++ Interface to Lua
MIT License
167 stars 27 forks source link

Coroutines and return values #16

Closed johannlejeune closed 9 years ago

johannlejeune commented 9 years ago

Hello,

I don't know if this is the place to ask questions, but I thought it might also help other people who ask themselves the same questions.

I'm new to Lua scripting and I'm trying to implement scripts into my existing C++ project. I chose your interface mainly because it supports coroutines and it is very easy to use (thanks for that).

Basically, the scripts will tell my application to do things which could take time. After these things are finished, a signal is triggered into my application and once this signal is triggered, I'd like the script to continue. I thought coroutines are exactly what I need to achieve such a behavior, but I don't know if I'm doing it right.

At the moment, my entire script runs inside a coroutine (there is no call to lua.RunScript, I'm creating a coroutine and calling RunScript on the coroutine, maybe I'm wrong). The functions that might take time are created as YieldingFunction. Therefore, they tell my application to do its thing and when it's done, I just have to call Resume for the script to continue and it works like expected for now.

However, I would like to reproduce one behavior and I don't know how to do it (or if it is possible with your interface). When the yielding function is called and calls the appropriate function in my application, it doesn't know yet if it worked. The result (true or false in my case) is only known when calling Resume. I read that yielding functions can return values, but is there a way to pass a value to Resume, which could be retrieved as the return value of the yielding function in the script ?

I don't know if I was clear enough. Don't hesitate to tell me I'm doing it wrong :). Thanks in advance for your help.

davidsiaw commented 9 years ago

To answer your question simply, yielding functions are the same as functions. That means the fact that they yield has nothing to do with the fact that the coroutine has been suspended.

When you call resume you should receive whatever the yielding function has returned.

johannlejeune commented 9 years ago

Thanks for your response. Problem is, I don't know the return value when the yielding function returns. I only know the return value once I resume the coroutine, that's why I'm trying to set that return value when I resume the coroutine.

davidsiaw commented 9 years ago

I don't understand. When the yielding function returns, the Lua code is suspended, so it is meaningless to say you don't know the return value in Lua.

If you wish to access that return value in C++ once the yield has happened however you can set a variable in a [global] table, or a variable somewhere if you want the value before resuming the coroutine.

I could actually be missing your point. Could you provide a little code?

johannlejeune commented 9 years ago

I use yielding function to execute tasks that could take seconds to be done (asynchronously). The yielding function launches the task and if the launch has succeeded, it returns true, else it returns false (and the script is resumed directly after that since it failed). However, when the launch succeeded, the task itself may not succeed. If the task fails, it would be great if I could resume the script and return false (when I previously returned true cause the task launch succeeded). If the task itself succeeded, then I just resume the script as the return value (true in this case) is correct.

Does this help you understand ?

davidsiaw commented 9 years ago

Ah, so you want to return a value through Resume because you can only decide the return value when you want to resume. You can always set a global var ( lua.GetGlobalEnvironment().Set("call_success", false) )

The reason I do not allow passing return values through resume is because the yielding function may return a value. If it returns a value but you pass another return value via resume, then we do not know which value to actually return!

johannlejeune commented 9 years ago

That's it :).

You're right, for the moment, as I didn't managed to do it, I implemented another function "lastActionSucceeded" that acts exactly as you described with a global var.

If I understood it correctly, return values are pushed onto a sort of stack. So if the yielding function returns a value and resume adds a return value too, it would be confusing. Is there a way to "replace" the return value in the "stack" if there is one already set ?

davidsiaw commented 9 years ago

Yes, but you can have more than one return value, which means you need to know how much stuff in the stack to remove.

It also breaks the Principle of Least Surprise, because if the application calls Resume and replaces the return value, from the script's point of view it looks like something very strange happened, especially if the number of values returned is wrong.

johannlejeune commented 9 years ago

Mmmmh, understood. I think I'll keep the lastActionSucceeded() then, unless I manage to find another way :).

Thanks for you help anyway ! Have a good day :).

davidsiaw commented 9 years ago

Sorry about the late reply, your question has made me aware of a bug with yielding functions (they don't return properly!)

davidsiaw commented 9 years ago

Closing since @Moonlight-Angel feels his question is answered.