Comments (6)
Hello, how are you doing?
I'm pretty sure that you can do something like this (maybe it's easier to do):
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub struct RGBColor {
r: u8,
g: u8,
b: u8
}
#[allow(dead_code)]
impl RGBColor {
pub fn new(red: u8, green: u8, blue: u8) -> Self {
Self {
r: red,
g: green,
b: blue,
}
}
pub fn to_u32(&self) -> u32 {
let (r, g, b) = (self.r as u32, self.g as u32, self.b as u32);
(r << 16) | (g << 8) | b
}
pub fn lua_color(lua: &Lua) -> LuaResult<LuaTable> {
let exports = lua.create_table()?;
exports.set("new", lua.create_function(RGBColor::lua_new)?)?;
Ok(exports)
}
pub fn lua_new(_: &Lua, (r, g, b): (u32, u32, u32)) -> LuaResult<RGBColor> {
Ok(RGBColor::new(r as u8, g as u8, b as u8))
}
}
impl UserData for RGBColor {}
On impl UserData for RGBColor {}
you can add methods, just like the docs does: https://docs.rs/mlua/0.4.1/mlua/trait.UserData.html
And then you can add the constructor with: lua_table.set("color", RGBColor::lua_color(&lua)?)?;
.
The lua_table
is a lua table (created with let lua_table = lua.create_table()?;
) that is global: globals.set("rho", lua_table)?;
.
The problem with your code is that your struct on action: Option<fn(&mut Player, &mut Player)>,
requires a fn (https://doc.rust-lang.org/std/primitive.fn.html) function, but mlua creates an mlua::function::Function
, you probably wants to check (and changes to) https://docs.rs/mlua/0.4.1/mlua/struct.Function.html and I think that you are going to get some lifetime problems, but it's probably fine to resolve.
You may and want to check this issue: mlua-rs/rlua#73
from mlua.
As I mentioned before I'd like the library to be extensible using Rust as well as (and optionally) Lua, so using mlua::function::Function
in my Action
struct ties me to mlua which isn't ideal.
The constructor you've shown looks fine, but it doesn't handle closures, while my library does need closures (I'm planning to use Lua as a declarative configuration language with bits of behavior mixed in, kind of like JSON with closures, a-la AwesomeWM).
Please do correct me if I'm wrong, but I don't think your suggestion can help my use case.
BTW, the rlua issue you mentioned was an interesting read, but what I understood from it was that this simply cannot be done. Am I correct?
from mlua.
I'm pretty sure that you can do what you want, I don't know exatcly if that way is possible.
Anyway, here is an example that may help you.
extern crate mlua;
use std::sync::{Arc, Mutex};
use mlua::prelude::*;
use mlua::{UserData, UserDataMethods};
#[derive(Clone, Copy)]
#[allow(dead_code)]
struct Action {
value: i32,
f_value: f32,
}
impl UserData for Action {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("print", |_, this, ()| {
println!("f_value: {}", this.f_value);
Ok(())
});
}
}
#[derive(Clone)]
struct ActionsList {
pub actions: Vec<Action>
}
#[allow(dead_code)]
impl ActionsList {
pub fn new() -> Self {
Self {
actions: Vec::new()
}
}
pub fn add_action(&mut self, action : Action) {
self.actions.push(action)
}
pub fn get_action(&self, id : usize) -> Action {
self.actions[id].clone()
}
}
fn main() -> mlua::Result<()> {
// Based on: https://doc.rust-lang.org/book/ch16-03-shared-state.html
// Creates the action list
let list = Arc::new(Mutex::new(ActionsList::new()));
// Creates the lua context
let lua = Lua::new();
// We are going to make our functions calls inside a scope
lua.scope(|scope| {
let globals = lua.globals();
// Creates the lua function: newAction(value, f_value)
globals.set("newAction", scope.create_function(|_, (v, f): (i32, f32)| {
list.lock().unwrap().add_action(Action {
value: v,
f_value: f
});
Ok(list.lock().unwrap().actions.len()-1)
})?)?;
// Loads the script
lua.load(r#"
local test1 = newAction(32, 293.0)
local test2 = newAction(399, 39.0)
print("Test 2: " .. tostring(test2).."\n")
function onAction(action_id, action)
print(string.format("Receveid action_id, action: %d, %s", action_id, tostring(action)))
action:print()
if action_id == test1 then
print("Action: test1\n")
elseif action_id == test2 then
print("Action: test2\n")
end
end
"#).exec()?;
// Run the test
let on_action_fn : mlua::Function = globals.get("onAction")?;
on_action_fn.call::<_, ()>((0, list.lock().unwrap().get_action(0)))?;
on_action_fn.call::<_, ()>((1, list.lock().unwrap().get_action(1)))?;
Ok(())
})?;
Ok(())
}
So I send the action_id
as an information to lua and when an action needs to be executed the code on onAction
will be responsible for it.
Console output:
Test 2: 1
Receveid action_id, action: 0, userdata: 0x55da910b0a18
f_value: 293
Action: test1
Receveid action_id, action: 1, userdata: 0x55da910b1078
f_value: 39
Action: test2
from mlua.
Thanks for taking the time to write the example.
So from what I understand you use action_id
s or indexes to refer to actions?
That still doesn't address my issue of wanting to "tie down" (for lack of a better term) a LuaFunction
as a Fn
.
What if Action
had a dyn Fn
field and you wanted to be able to construct Action
s from both Lua and Rust, passing a Lua anonymous function when constructing from Lua?
One possibility I can think of would be to use an enum like this:
enum FunctionType<'a, 'lua> {
LuaFunction(LuaFunction<'lua>),
RustFunction(&'a dyn Fn(&mut Player, &mut Player)),
}
And then having the function object member in Action
be of that type.
However, that means I have to either compile my library with Lua all the time (and not as an optional feature) or spread a bunch of #[cfg(feature = "lua")]
all over the place (at least once in the enum definition and 2+ times when checking if a function is a LuaFunction
or a RustFunction
, I think?).
I will probably resort to doing that if there's no better option but I'd like to avoid it if at all possible.
Edit: it should be noted that C++'s sol2 does allow for exactly the functionality I'm looking for.
from mlua.
You can try something like this:
#[derive(Builder, Clone, Default)]
#[builder(default)]
pub struct Action {
name: String,
description: String,
health_added: i64,
cost: i64,
#[builder(setter(strip_option))]
action: Option<Arc<Box<dyn Fn(&mut Player, &mut Player)>>>,
}
impl LuaUserData for Action {}
pub struct Player;
impl LuaUserData for &mut Player {}
Arc
is needed to satisfy the Clone
requirements.
and then
let lua = Lua::new().into_static();
let action_constructor = lua.create_function(|lua, arguments: LuaTable| {
let mut ret = ActionBuilder::default();
if let LuaValue::Function(action) = arguments.get("action")? {
ret.action(Arc::new(Box::new(move |p1: &mut Player, p2: &mut Player| {
lua.scope(|scope| {
let p1 = scope.create_nonstatic_userdata(p1)?;
let p2 = scope.create_nonstatic_userdata(p2)?;
action.call::<_, ()>((p1, p2))
}).expect("action failed");
})));
}
Ok(ret.build())
})?;
should work.
Please note, that I explicitly leaked the Lua instance to make it 'static
. And likely you would need unsafe
block to consume the static reference and drop it at the end, like unsafe { drop(Lua::from_static(lua)) };
from mlua.
I can't believe I missed that - simply calling the Lua function from inside the Rust closure.
Thanks, I'll fiddle around with the code. Since this is a way to achieve my goal I'll close the issue.
from mlua.
Related Issues (20)
- Can't re export lua_module in own lib
- Can module mode works with async? HOT 2
- Difficulty creating a wrapper type HOT 1
- Support Audio manipulation HOT 2
- Support UI HOT 2
- Error compiling module to wasm32-unknown-emscripten
- Terra integration?
- Lifetime Issues with Scope HOT 3
- In a module best way to deal with async HOT 4
- Serialization: Recursive table detection yields false positives HOT 1
- Allow setting luau compiler options when loading with `require` HOT 2
- Builds fail with useless errors if user's global Cargo target-dir is changed HOT 3
- Send feature flag HOT 2
- bug: `c_void` not a Sync HOT 3
- Limiting script execution time (a question and a bug report) HOT 2
- Deserialize from a table with function HOT 2
- Async functions from different modules treated as normal coroutines HOT 4
- How to convert json to `mlua::Value` like `c-json.decode` HOT 4
- how to method UserData method self.vars? HOT 2
- table.concat expect got "hello" HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from mlua.