HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Loading and Using Lua Scripts

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
scriptsusingloadingandlua

Problem

I have been experimenting with integrating Lua into Objective-C. I have tried to do the bare minimum to get Lua to compute values and return them. I did not want to use any external libraries so I have avoided those. All that is required to make this work is to add the Lua files to the project directory. Then all the scripts go in one folder, and when the Lua stuff is initialized, that folder is set up as the root path so it can find the scripts.

I started with some example code that takes input from a UITextField and outputs the result to a UITextField. You can type things into the TextField, and when you hit the evaluate button, it tries to compute the Lua and return a value. The way it is currently configured, it only accepts and returns strings.

LTViewController.h

#import 
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

@interface LTViewController : UIViewController {
    UITextView *output;
    UITextView *input;
    lua_State *luaStack;
}

@property (nonatomic, retain) IBOutlet UITextView *output;
@property (nonatomic, retain) IBOutlet UITextView *input;

-(IBAction) evaluate;
-(IBAction) clearInput;
-(IBAction) clearOutput;

-(IBAction) testFunction;

@end


LTViewController.m

```
#import "LTViewController.h"
#import "LTMyScene.h"

@implementation LTViewController

@synthesize output;
@synthesize input;

#pragma mark - Initialization
  • (void)viewDidLoad {


[super viewDidLoad];

input.font = [UIFont systemFontOfSize:12];
output.font = [UIFont systemFontOfSize:12];

//initialize lua
luaStack = luaL_newstate();
luaL_openlibs(luaStack);

//this can be set to any file in the directory where the scripts are going to be loaded from
NSString *luaPath = [[NSBundle mainBundle] pathForResource:@"testscript01" ofType:@"lua"];
[self configureLuaState:luaStack forPath:[luaPath stringByDeletingLastPathComponent]];

[self loadScripts];
}
-(void) configureLuaState:(lua_State)luaState forPath:(NSString)path

Solution

I am not even remotely familiar with lua or how it works with Objective-C, so I can't really comment on any of these aspects of your code.

What I can do of course, is comment on the Objective-C bits, starting with your header file.

Why is there anything in your header file at all?

#import 
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

@interface LTViewController : UIViewController {
    UITextView *output;
    UITextView *input;
    lua_State *luaStack;
}

@property (nonatomic, retain) IBOutlet UITextView *output;
@property (nonatomic, retain) IBOutlet UITextView *input;

-(IBAction) evaluate;
-(IBAction) clearInput;
-(IBAction) clearOutput;

-(IBAction) testFunction;

@end


The only parts I can see that need to be in your header file are these:

#import 

@interface LTViewController : UIViewController

@end


Everything in here is either an interface builder hook (which should be in the class extension in the .m), or an instance variable. The only thing here that's not an interface builder hook is the lua_State instance variable. Again, I'm not familiar with lua or how it works with Objective-C. This seems like it's probably fine as a private instance variable so we can put it in the .m, and if it needs to be public, let's use a @property so outsiders go through the accessors.

Meanwhile, these #include statements, for one, should go to the .m. Otherwise, any file that imports this file is also including these three files, and they probably don't necessarily want to do this. Also, we can just #import them instead, I believe, and that's almost certainly what we should do. I'm not aware of any instance in which you must #include rather than #import, and #import is definitely better than #include.

@synthesize output;
@synthesize input;

Xcode has been auto-synthesizing for a few versions now, and we're used to the autosynthesis to the underscored variable name. These two lines can just be eliminated.

input.font = [UIFont systemFontOfSize:12];
output.font = [UIFont systemFontOfSize:12];


Given that these variables are references to IB, why not just set the font size in IB?

Barring that, we don't have to instantiate two UIFont objects--we can do one.

UIFont *systemFont12 = [UIFont systemFontOfSize:12.0f];
input.font = systemFont12;
output.font = systemFont12;


cur_path = [cur_path stringByAppendingString:@";"];
cur_path = [cur_path stringByAppendingString:path];
cur_path = [cur_path stringByAppendingString:@"/?.lua"];


This can be replaced with this single line:

cur_path = [cur_path stringByAppendingFormat:@";%@/?.lua", path];


NSString *outputNS = [NSString string];
for (int i = nresults; i > 0; i--) {
    outputNS = [outputNS stringByAppendingFormat:@"%s ", lua_tostring(luaStack, -1 * i)];
}


This should be replaced with NSMutableString:

NSMutableString *outputString = [NSMutableString string];
for (int i = nresults; i > 0; --i) {
    [outputString appendFormat:@"%s ", lua_tostring(luaStack, -1 * i)];
}


int error = luaL_loadstring(luaStack, [input.text
                          cStringUsingEncoding:NSASCIIStringEncoding]); //first part needed to load string
//return early if anything went wrong with the first part of loading
if (error != 0) {
    [self displayError];
    return;
}

error = lua_pcall(luaStack, 0, LUA_MULTRET, 0); //second part needed to load string
//return early if anything went wrong with the second part of loading
if (error != 0) {
    [self displayError];
    return;
}


Okay, first of all, this:

[input.text cStringUsingEncoding:NSASCIIStringEncoding]


is way too big to embed in the function call. Let's save it to a local variable.

char *inputText = [input.text cStringUsingEncoding:NSASCIIStringEncoding];


Now, let's do our early returns something more like this:

if (luaL_loadstring(luaStack,inputText) != 0) {
    [self displayError];
    return;
}

if (lua_pcall(luaStack, 0, LUA_MULTRET, 0) != 0) {
    [self displayError];
    return;
}


In fact, we could even drop the != 0 as any non-zero will evaluate to YES here and enter the if block.

- (BOOL)shouldAutorotate {
    return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
        return UIInterfaceOrientationMaskAllButUpsideDown;
    } else {
        return UIInterfaceOrientationMaskAll;
    }
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}


These three can be completely eliminated. The first two can be set up in your project settings easily and it's unlikely that this controller has special rotation rules relative to the rest of the project. The last does nothing but call super, so literally nothing it wouldn't do already without being there.

- (void)dealloc {
    lua_close(luaStack);
}


In the days of ARC, it's rare that we need to do much of

Code Snippets

#import <UIKit/UIKit.h>
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

@interface LTViewController : UIViewController {
    UITextView *output;
    UITextView *input;
    lua_State *luaStack;
}

@property (nonatomic, retain) IBOutlet UITextView *output;
@property (nonatomic, retain) IBOutlet UITextView *input;

-(IBAction) evaluate;
-(IBAction) clearInput;
-(IBAction) clearOutput;

-(IBAction) testFunction;

@end
#import <UIKit/UIKit.h>

@interface LTViewController : UIViewController

@end
input.font = [UIFont systemFontOfSize:12];
output.font = [UIFont systemFontOfSize:12];
UIFont *systemFont12 = [UIFont systemFontOfSize:12.0f];
input.font = systemFont12;
output.font = systemFont12;
cur_path = [cur_path stringByAppendingString:@";"];
cur_path = [cur_path stringByAppendingString:path];
cur_path = [cur_path stringByAppendingString:@"/?.lua"];

Context

StackExchange Code Review Q#59731, answer score: 6

Revisions (0)

No revisions yet.