I am working on a scripting layer for my game engine. Currently I am using a Script as a Class, adding a method to the "Table" named new. This function basically created an instantiated copy of the Class. I call this function from the C API when an instance of the script is needed.
function PlayerController:new(o)
print('A new instance of the PlayerController has been created');
o = o or {}
setmetatable(o, self)
self.__index = self
return o
end
My question is: How can I take the above Lua code, and move that into C so it is not required to be added to every script file I write for this system?
You might want to create a class declaration function to do this for you. Here is a complete "helper.lua" file:
local lib = {type = 'helper'}
local CALL_TO_NEW = {__call = function(lib, ...) return lib.new(...) end}
function lib.class(class_name, tbl)
local lib = tbl or {}
lib.type = class_name
lib.__index = lib
-- Default "new" method
function lib.new()
return setmetatable({}, lib)
end
-- Enable foo.Bar() instead of foo.Bar.new()
return setmetatable(lib, CALL_TO_NEW)
end
return lib
Usage example:
local helper = require 'helper'
local lib = helper.class 'foo.Bar'
-- optional new function if there needs to be some argument handling
function lib.new(args)
local self = {}
-- ...
return setmetatable(self, lib)
end
return lib
lub.class
This is a real world minimal class declaration and setup system. It is used in many luarocks such as xml, yaml, dub, etc:
documentation
source code
Related
This question already has answers here:
Can we call a scenario from one feature in another using karate?
(2 answers)
Closed 1 year ago.
I need variables to be re-used (shared) across scenarios in the same feature file.
Please find below the working way that I'm currently using.
The problem here is that I have to outsource the shared variables to another feature file what seems to be quite cumbersome for such a silly task.
I was wondering if I could define the re-usable variables as an ignored scenario in the same feature file that I can callonce from "myself" (the same feature file) as follows:
File my.feature:
Feature: My
Background:
* url myUrl
# call once explicitly the scenario tagged with '#init'
* def vars = callonce read('my.feature#init')
#ignore #init
Scenario: Return shared variables for all scenarios
* def id = uuid()
# the non-ignored scenarios follow below this line...
Problem: Unfortunately this leads to an endless loop with many errors. It seems like callonce myself (the same file that invokes callonce) runs the Background including the callonce again.
Is the idea shown above possible and if yes, where's my mistake?
Or could you callonce without processing the Background again? Something like adding an argument to callonce or use karate.callSingle(file, dontProcessBackground=true)?
Many thanks.
--
The following works (but is cumbersome):
File my.feature:
Feature: My
Background:
* url myUrl
* def vars = callonce read('my.init.feature')
#one
Scenario: One
* def payload = `{ "id" : "${vars.id}" }`
* request payload
* method post
* status 200
* match $.value == 'one'
#two
Scenario: Two
* def payload = `{ "id" : "${vars.id}" }`
* request payload
* method post
* status 200
* match $.value == 'two'
File my.init.feature:
#ignore
Feature: Create variables to be used across mutliple scnearios
Scenario: Return shared variables for all scenarios
* def id = uuid()
... where uuid() is shared in karate-config.js:
function fn() {
var uuid = () => { return String(java.util.UUID.randomUUID().toString()) };
// ...
var config = { uuid: uuid }
return config;
}
I have to outsource the shared variables to another feature file
There is nothing wrong with using a second file for re-usable stuff. All programming languages work this way.
If this is such an inconvenience, kindly contribute code to Karate, it is an open-source project.
I'm trying to create functions in hylang and use them from python but the created functions don't seem to have access to the environment passed to hy.eval.
import hy
env = dict(x=5)
func = hy.eval(hy.read_str('(fn [] x)'), env)
print(func())
The call to func results in NameError: name 'x' is not defined. I also tried
hy.eval(hy.read_str('(func)'), env)
without luck (same error). Any ideas?
The first parameter of hy.eval is locals, not globals as for Python's eval. Implicitly using the calling environment works fine, though, so you can write this more straightforwardly as
import hy
x = 5
func = hy.eval(hy.read_str('(fn [] x)'))
print(func())
hy.eval doesn't have a globals parameter but it has a module parameter and by looking at the source I found that module.__dict__ is passed as globals to eval. So the following works:
import hy
from types import ModuleType
env = dict(x=5)
module = ModuleType('<string>')
module.__dict__.update(env)
func = hy.eval(hy.read_str('(fn [] x)'), module=module)
print(func())
I am trying to use C library source files inside my Cocoa Framework which has function named
void swe_set_ephe_path(char *path);
Which will basically be
swe_set_ephe_path(”C:\\SWEPH\\EPHE”);
for windows.
This library contains other data files which only work after this function is set.
When imported to Swift the function looks like this
swe_set_ephe_path(path: UnsafeMutablePointer<Int8!>)
Since i want to bundle up all the data files in framework and use it in my application, i have done something like this
public class SwissEphemeris {
public init() {
let path = Bundle.main.bundlePath
let swePath = UnsafeMutablePointer<Int8>(mutating: (path as NSString).utf8String)
swe_set_ephe_path(swePath)
}
}
But it seems it's not working and the functions which needs data to be searched in files are not able to operate.
If anybody interested to look into Swiss library documentation, check here for the link,
https://www.astro.com/swisseph/swephprg.htm#_Toc505244836
There are two problems:
First, the resource files are in the “Resources” subdirectory of the framework, not in the top-level framework directory. You can obtain a path to that directory with
let resourcePath = Bundle(identifier: "com.Abhi.SwissFramework")!.resourcePath!
or with
let resourcePath = Bundle(for: type(of: self)).resourcePath!
I suggest to force-unwrap the optionals because you know that the bundle and the resources directory exist. A failure would indicate a build problem which should be detected early.
Second, the C function takes a char * argument even though it does not mutate the passed string. Here you can use the approach from UnsafeMutablePointer<Int8> from String in Swift:
resourcePath.withCString {
swe_set_ephe_path(UnsafeMutablePointer(mutating: $0))
}
Even better: use the dedicated method withUnsafeFileSystemRepresentation() to get the file system representation of the resource path as a C string:
let resourceURL = Bundle(for: type(of: self)).resourceURL!
resourceURL.withUnsafeFileSystemRepresentation {
swe_set_ephe_path(UnsafeMutablePointer(mutating: $0))
}
I am learning from "Programing in Lua" by Roberto Ierusalimschy, and I found that in the book, the example of Sandboxing uses the function setfenv() to change the environment of a given function, but in lua 5.2 this function is no longer available.
I tried to load some values from a file (a configuration file) into a field in a table, but, in lua 5.2 I can't use setfenv ( so I can load the values in the given environment). After reading some articles about lua 5.2 I found that each function may have (or not) an upvalue called _ENV which serves as the environment, so, I tried the following code:
function sandbox(sb_func, sb_env)
if not sb_func then return nil, "sandbox function not valid" end
sb_orig_env = _ENV
_ENV = sb_env -- yes, replaces the global _ENV
pcall_res, message = pcall( sb_func )
local modified_env = _ENV -- gets the environment that was used in the pcall( sb_func )
_ENV = sb_orig_env
return true, modified_env
end
function readFile(filename)
code = loadfile(filename)
res, table = sandbox(code, {})
if res then
--[[ Use table (modified_env) ]]--
else
print("Code not valid")
end
Replacing _ENV in the 'sandbox' function works well (can't access the regular fields), but, when the 'code' is executed it seems that it ignores that I replaced _ENV, it still can access regular fields (print, loadfile, dofile, etc).
Reading a little more, I found that lua 5.2 provides a function for this purpose, this function is loadin(env, chunk), which runs the given chunk in the given environment, but, when I try to add this function to my code, the function doesn't exist ( Is not present in the global _G field).
Some help will be appreciated.
When you assign to _ENV from within sandbox, you're not overwriting the global environment--you're replacing the _ENV upvalue of the currently running code. Adding calls to print(_ENV) may help you better understand the identities of the tables involved.
For example:
function print_env()
print(_ENV)
end
function sandbox()
print(_ENV) -- prints: "table: 0x100100610"
-- need to keep access to a few globals:
_ENV = { print = print, print_env = print_env, debug = debug, load = load }
print(_ENV) -- prints: "table: 0x100105140"
print_env() -- prints: "table: 0x100105140"
local code1 = load('print(_ENV)')
code1() -- prints: "table: 0x100100610"
debug.setupvalue(code1, 1, _ENV) -- set our modified env
code1() -- prints: "table: 0x100105140"
local code2 = load('print(_ENV)', nil, nil, _ENV) -- pass 'env' arg
code2() -- prints: "table: 0x100105140"
end
The loadin function was present in some pre-release versions of Lua 5.2 but was removed before the final release. Instead, the Lua 5.2 load and loadfile functions take an env argument. You can also modify the _ENV of another function using debug.setupvalue.
I have the following lua script :
module("modoo",package.seeall)
foo=1
bar={12,34}
Which works fine using the cli, such as :
> dofile "mod_modoo.lua"
> =modoo.foo
1
> =modoo
table: 0x86ce058
As far as I understood, it works like a table, but whenever I try loading it as a table, a nil value is pushed onto the stack. Every other table works normally.
I thought using lua_getglobal wouldn't work with modules, but I couldn't find the proper way to load it either ; how should I do it ?
Load Lua modules with require like lua.c does. See http://www.lua.org/source/5.1/lua.c.html#dolibrary
Instead of using module and dofile, it is better practice in Lua 5.1 to simply return a table representing your module when the script is run. All functions and variables should be declared as local within the module script so that the returned table provides the only access point and no globals from other modules are trampled. The module should be imported using require as follows.
mod_modoo.lua:
return { foo = 1, bar = { 12, 34 } }
modoo_test.lua:
> local modoo = require "mod_modoo"
However, it is often nice to bring the module table (i.e. modoo) in as a global without explicitly assigning it. To do so, assign the module table a global reference in addition to returning it from the script. In that case, the module can be used as follows:
mod_modoo.lua:
modoo = { foo = 1, bar = { 12, 34 } }
return modoo
modoo_test.lua:
> require "mod_modoo"
> print(modoo.foo)
1
See the Lua Module Function Critiqued article for more information on the matter.