Reinvent The Wheel

Round is nice, but we can do better!

Lua4Delphi – Implementation Samples Part 1

| 37 Comments

Lua4Delphi is about to be released, and so, for those interested, it is important know how to implement it in your own applications.

[Overview] What are the current capabilities of Lua4Delphi?

As it stands today (and will certainly be in the February 1st release) Lua4Delphi can do the following perfectly:

  • Statically link Lua into your Delphi Win32, Win64 and/or OSX application (distributing your chosen Lua 5.1-compatible DLL with your product)
  • Create one or more Lua Instances in your Delphi application
  • Execute Lua Script Files from your Delphi application
  • Execute abstract Lua Code from your Delphi application
  • Read and Write Lua “Globals” (and define new Globals) from your Delphi application, even check Data Type compatibility (CanBeString, CanBeBoolean etc) for each value!
  • Invoke Global and Local Lua Functions from your Delphi application (with or without Parameter Values), and retrieve values (one or many) returned by that Lua function
  • Define procedures in Delphi to be invoked from Lua. Lua can pass Parameter Values to the Delphi procedures, and the Delphi procedures can return values (one or many) to Lua.
  • Read, Write and Create Lua Tables (and Metatables) from your Delphi application. You can even call Lua functions contained within a Lua Metatable, or provide Delphi procedures as values within the Lua Metatable!
  • Pass UTF8-encoded Unicode Strings to Lua, and retrieve them (note that the default Lua String Type cannot operate on Unicode chars, so you need to use the UTF8String type provided by Lua4Delphi in Lua)

In development right now (with the hope of being included in the February 1st release):

  • Specify/Modify the default Lua Search Path (where Lua’s “require” function looks for Lua files and DLL plugins)
  • Dynamically link Lua into your Delphi application (distributing your chosen Lua 5.1-compatible DLL with your product, but with the ability to determine whether the DLL is present at execution, so you can disable scripting if it is not)
  • Register Delphi Types with Lua (using Wrapper Types generated by Lua4Delphi’s “Wrapper Unit Generator”). This enables you to create new object instances at runtime from Lua code!
  • Register Delphi Object Instances with Lua. This enables you to manipulate Delphi-defined Object Instances from Lua, such as reading and setting Property values, calling methods, and assigning OnEvent hooks as Lua code.

All of the above can be achieved with no understanding of Lua’s C API! Lua4Delphi handles Lua’s C API for you, meaning you need only understand the very logical Class Hierarchy of Lua4Delphi, as well as the Definition Prototypes for things such as Delphi-defined Lua Procedures… all of which are remarkably simple to learn, and easy to remember!

Please note that Lua4Delphi, when implemented in your application(s), does not use or require RTTI! This is great for high performance!

[Contents] Demonstrated in this article…

  • Statically Linking Lua into your Delphi Application
  • Choosing which Standard Lua Libraries to use
  • Registering Global Values with Lua
  • Registering Delphi Procedures with Lua
  • Executing a Lua Script file
  • Calling a Delphi procedure from Lua
  • Type-checking Lua Values passed as Parameters, and reading their values
  • Returning values to Lua from a Delphi procedure
  • Calling a Lua function from Delphi (with Parameters) and reading the returned values

The above might seem like a lot, but it’s really strait-forward, and will quickly become second-nature to you once you start using Lua4Delphi in your Delphi applications!

Statically Linking Lua into your Delphi Application

Lua4Delphi Component Pallet

Lua4Delphi Component Pallet

To the left you can see that Lua4Delphi registers its components on the Component Pallet. Note that the icons for Lua4Delphi components will be changing at some point!

Select the TLua4DelphiStatic component and drop it on your form or Data Module. Really, you should always use Data Modules to house non-visual components, as this allows you to separate your Business Logic (the actual function of your program) from the User Interface (what the user actually sees and interacts with).

Specify a more succinct name

Specify a more succinct name

On the right you can see what the TLua4DelphiStatic instance looks like in the Object Inspector. Because Lua4Delphi endeavours to provide Static, Dynamic and Embedded linking, it has been necessary to suffix the component names with their linkage method so-as to differentiate between them. The down-side of this is that the instance names assigned by Delphi are quite long (Lua4DelphiStatic1). You should specify a better (shorter, more relevant) name.

For the sake of this demonstration, I’m renaming this instance L4D1

OnEvent Methods

OnEvent Methods

To the left you can see that TLua4DelphiStatic provides some OnEvent methods:

  • OnCreate  - This is provided so we can perform some Lua-specific routines when the Lua Instance is constructed. Arguably, you can define these on a Form or Data Module’s OnCreate event, but I consider it best practise to separate Lua-specific operations from the rest, as this makes code maintenance easier.
  • OnDestroy – As with OnCreate, we can use this to define some Lua-specific routine to take place after the Lua instance has been destroyed (such as removing Buttons, Menu Items etc relevant to the Lua aspect of the program).
  • OnException – This enables you to gracefully (and safely) handle any exceptions which occur within Lua4Delphi… such as entering the exception details into a log, or displaying them as we wish on our User Interface. This event provides a certain parameter (var ARaise: Boolean) which, when set to true, will cause the exception to be raised normally. For some exceptions, ARaise will default to False, for criticial exceptions ARaise will default to True!
  • OnLuaError – This enables you to gracefully (and safely) handle any errors raised within Lua (such as typos in Lua code), and also provides the ARaise parameter as with OnException.

NOTE: If you do not assign an Event Handler to either OnException or OnLuaError, these exceptions will be raised as normal (with the standard exception dialog).

Anyway, by simply dropping a TLua4DelphiStatic instance on our Form or Data Module, we have now Statically Linked Lua into our Delphi application… so we can now begin to use it!

Choosing which Standard Lua Libraries to use

Built into Lua 5.1 is a series of Standard Lua Libraries, each responsible for providing a specific portion of Lua’s overall capability.

Lua's Default Libraries

Lua's Default Libraries

  • Base – Provides the core functionality of Lua, including Coroutines. Generally, this option should always be enabled.
  • Debug – As the name suggests, provides debugging capability. You need to decide whether or not this should be available to your application’s end-users. Generally, if you’re allowing users to produce their own Lua scripts for your application, you should enable Debug, otherwise don’t!
  • IO – Provides IO functionality to Lua… such as the ability to output data to files from a Lua script!
  • Math – Provides exactly what its name suggests, and should generally always be enabled.
  • OS – Provides OS-specific functionality to Lua, as well as OS information. If your application is cross-platform, this should be enabled. You can, of course, still use it in single-platform applications… it is quite useful!
  • Package – The package manager is designed for scripters who need to use multiple files and load them in a consistent way. Again, dependant on your application and its scripting needs, it is generally a good idea to leave this enabled.
  • Strings – Provides fundamental String Operations such as Pattern Matching, Substring Extraction, Concatenation and more! Generally this should be enabled. NOTE: If you are using Unicode Chars with Lua, you will need to use the UTF8String type provided by Lua4Delphi to Lua. The two are linked, so enabling Strings will enable UTF8Strings.
  • Table – Provides Lua with the crucial Table and MetaTable functionality. If this is not enabled, you cannot register Delphi Types or Instances with Lua… so you should generally always leave this enabled.

IMPORTANT NOTE: You can enable any of the above properties at run-time, but once they are enabled they cannot be disabled!

USEFUL TIP
If you want to define Debug according to your Build Configuration, place the following inside L4D1‘s OnCreate event…

{$IFDEF DEBUG}L4D1.Options.Libraries.Debug := True{$ENDIF}

Registering Global Values with Lua (from our Delphi application)

Lua4Delphi - Globals - Push Methods

Lua4Delphi - Globals - Push Methods

Lua4Delphi provides two ways of registering Global Values with Lua. Above you can see the more optimized method.

Each of the above methods takes an AnsiString Key (a Name), then a value of the appropriate Type.

Lua4Delphi - Globals - PushString

Lua4Delphi - Globals - PushString

It is important to note that Keys must always start with a Letter or Underscore, and absolutely cannot contain Spaces or any characters other than Letters, Numbers and Underscores.
Everything in Lua is case-sensitive, so whatever Key you define from your Delphi application, this is the exact name your Lua code must reference!

Lua4Delphi does not perform any “sanity checks” on Keys or Values. It is up to you as the developer to ensure that these are valid and properly-formatted. The reason for the absence of “sanity checks” is that they reduce performance… and I simply don’t consider these checks necessary as the same limitations apply when defining Identifiers in Delphi! Use some common-sense and you won’t encounter any problems!

There is another way of registering Lua Globals from your Delphi code…

Lua4Delphi - Globals - "Logical Assignment"

Lua4Delphi - Globals - "Logical Assignment"

To the left you can see an alternative method for registering Lua Globals. This method (which I call “Logical Assignment”) is arguably easier to read, but has a minor performance impact (it’s very-slightly slower than the PushString method).

Ultimately it is up to you to choose which method you like best, and whether the performance impact (which is slight, but noticeable if you’re registering many thousands of Globals) is worth the alternative structure of the code. Whichever method you choose, however, should be used consistently throughout your code, so-as to avoid confusion.

Registering Delphi Procedures with Lua

There are three ways of registering Delphi methods with Lua, but whichever you choose, you will need to format your Delphi method accordingly!

The Unmanaged “Pure C” approach

Lua’s C API has its own method prototype for C-defined functions to be invoked from Lua code. In Delphi, they look like this:

function MyLuaFunction(L: PLuaState): Integer; cdecl;

Note how the above declaration uses the cdecl calling convention, takes a specific parameter, and returns an Integer.

You can (if you wish) use this Unmanaged “Pure C” approach if you so choose, but you will need to implement these methods in a very specific way:

implementation

function MyLuaFunction(L: PLuaState): Integer;
var
  LLua: TLuaAbstract;
begin
  // "LuaLinkType" is a Global Variable in Delphi!
  // It provides a Class Reference for either:
  //   TLuaStatic, TLuaDynamic or TLuaEmbedded
  // as appropriate!
  LLua := LuaLinkType.Create(L);
  try
    // we can now use LLua to communicate with Lua
    // using standard Lua C API methods, without the
    // "L" Parameter (as this is automatically passed)
    LLua.lua_pushstring('Hello World');
    // The Result MUST be the number of values we're
    // sending back to Lua!
    Result := 1;
  finally
    LLua.Free;
  end;
end;

The above method can be registered with Lua as follows:

  L4D1.Globals.PushFunction('MyLuaFunction', MyLuaFunction);

Now, this Unmanaged “Pure C” approach has some very crucial limitations. The most important of these is that these methods cannot be bound to an Object Type (such as a Class), making their use with Forms unnecessarily complicated.

Another annoying limitation with the Unmanaged “Pure C” approach is that you will need to have a moderate understanding of Lua’s C API in order to communicate values between Delphi and Lua.

Lua4Delphi, however, makes it much easier!

The Managed “Unbound” approach

I define an “Unbound” method as being a Function or Procedure not bound to an Object Type. ShowMessage in the Dialogs unit is an example of an “Unbound Procedure”.

We define a Managed “Unbound” Lua Procedure in our Delphi application as follows:

procedure MyLuaProcedure(const AStack: TL4DMethodStack);

We can now implement MyLuaProcedure as follows:

procedure MyLuaProcedure(const AStack: TL4DMethodStack);
begin
  // Test to see if Value 1 can be displayed as a String
  if AStack[1].CanBeString then
    // If it can, show a Message Dialog with its Value
    ShowMessage(AStack[1].AsString);
  // Return a String Value to Lua
  AStack.PushString('Thank You!');
end;

As you can see (above), Lua4Delphi’s Method Stack Manager makes it much easier to communicate between Delphi and Lua. The Stack Manager is also automatically memory-managed, so you don’t need to worry about creating or destroying an instance.

We can register Managed “Unbound” procedures with Lua as follows:

  L4D1.Globals.PushFunction('MyLuaProcedure', MyLuaProcedure);

The Managed “Object Method” approach

This is probably the most useful approach when registering Delphi Methods with Lua. Fundamentally, it’s the same as The Managed “Unbound” approach, only we bind our method to an Object Type:

type
  TForm1 = class(TForm)
  private
    procedure MyLuaObjectProcedure(const AStack: TL4DMethodStack);

We can now implement MyLuaProcedure as follows:

procedure TForm1.MyLuaObjectProcedure(const AStack: TL4DMethodStack);
begin
  // Test to see if Value 1 can be displayed as a String
  if AStack[1].CanBeString then
    // If it can, show it in a Memo control
    Memo1.Lines.Add(AStack[1].AsString);
  // Return the Form's Caption to Lua as a String
  AStack.PushString(Caption);
end;

The advantage of using the Managed “Object Method” approach is that our Lua-invoked Delphi procedure can communicate directly with members of the Object (in this example, Memo1 and the Form’s Caption property).

We can register Managed “Object Method” procedures with Lua as follows:

  L4D1.Globals.PushFunction('MyLuaObjectProcedure', MyLuaObjectProcedure);

Notice how the above line is exactly the same, no matter what approach we use? That’s because PushFunction is always Overloaded, to handle all three approaches properly.

IMPORTANT NOTE:

We must never invoke a Delphi method registered with Lua from our Delphi code directly!

Executing a Lua Script File

We now know how to register Values and Methods with Lua from our Delphi application, but none of this is very useful without knowing how to execute Lua Scripts, to operate on those Values and Methods!

Lua4Delphi - InjectLuaFile Method

Lua4Delphi - InjectLuaFile Method

Lua4Delphi makes it remarkably simple with the use of the InjectLuaFile (as shown above) function.

The first parameter (AFileName) is the path and file name of our Lua Script. The second (optional) Parameter (AAutoExecute) allows us to specify whether or not this script should be executed immediately. Generally, you won’t want to specify a value for AAutoExecute, but the option is there for “advanced developers”.

Should an exception occur (such as File Not Found) or Lua encounter an error when executing the script, the exceptions will be raised as normal unless you have provided Event Handlers for OnException and OnLuaError (in which case the exception will be handled as per your implementation’s instruction).

InjectLuaFile will return True if the script executed without incident, or False if any error is encountered.

The reason the function is called InjectLuaFile rather than “LoadFromFile” is that “Load” implies that only one script could be loaded at a time… however, you can inject as many Lua scripts as you like!

Calling a Delphi procedure from Lua

I shall now provide for you a very simple “case-example”, where we provide a Wrapper Method in Delphi to expose Delphi’s ShowMessage procedure to Lua.

From our Delphi code, we would type the following:

implementation

procedure LuaShowMessage(const AStack: TL4DMethodStack);
begin
  if AStack[1].CanBeString then
    ShowMessage(AStack[1].AsString)
  else
    raise Exception.Create('Value not a String');
end;

The above code also demonstrates Type-checking Lua Values passed as Parameters, and reading their values.

We now register LuaShowMessage with Lua:

implementation

procedure TForm1.L4D1Create(Sender: TObject);
begin
  L4D1.PushFunction('ShowMessage', LuaShowMessage);
end;

From our Lua Script we can now use the following:

ShowMessage("Hello World, From Lua");

Note: We can use either Double- or Single-quotes in Lua, but both the starting and ending quotes must match!
Note 2: In some cases, the Semi-colon is optional (it’s optional in the example shown above), but in others it is necessary. Please read up on the Lua language to learn more!

The result of executing the above line of Lua code is exactly what you would expect:

Lua4Delphi - ShowMessage Example

Lua4Delphi - ShowMessage Example

So, as you can see… registering Delphi methods with Lua and consuming them from Lua is very strait-forward!

Returning values to Lua from a Delphi procedure

You’ve now seen how you can register Delphi methods with Lua, and invoke those methods from Lua passing Parameters. This section will now demonstrate how to return one or more values to Lua from your Delphi methods.

Let’s start with something simple…

implementation

procedure LuaAdd(const AStack: TL4DMethodStack);
begin
  if (AStack[1].CanBeDouble) and (AStack[2].CanBeDouble) then
    AStack.PushDouble(AStack[1].AsDouble + AStack[2].AsDouble)
  else
    raise Exception.Create('Both values must be numeric');
end;

// Register with Lua
procedure TForm1.L4D1Create(Sender: TObject);
begin
  L4D1.Globals.PushFunction('Add', LuaAdd);
end;

The above code simply adds two values (passed from Lua as Parameters) and returns the result to Lua.

If we call this code from Lua like this:

Foo = LuaAdd(10, 5);

The value of Foo (a Lua Global) will be 15.

Lua also supports returning multiple values from a Delphi-defined Lua method! This opens up an entire new world of possibilities when compared with Delphi functions (which can only return a single value).

Let’s presume we want to improve our LuaAdd method, so it can handle an undefined number of Parameters, and will return the progressive total for each:

procedure LuaAdd(const AStack: TL4DMethodStack);
var
  I: Integer;
  LTotal: Double;
begin
  LTotal := 0;
  for I := 1 to AStack.Count do
  begin
    if (AStack[I].CanBeDouble) then
    begin
      LTotal := LTotal + AStack[I].AsDouble;
      AStack.PushDouble(LTotal);
    end else
      raise Exception.Create('All values must be numeric');
  end;
end;

The above code iterates through all Parameters passed to LuaAdd when invoked from Lua, adds their value to the Total, then pushes the running total back to Lua. This means that we’re returning the same number of values to Lua as were passed to LuaAdd as Parameters.

So, let’s take a look at the Lua portion of this example:

FooA, FooB, FooC, FooD, FooE, FooF = LuaAdd(10, 5, 7, 12, 1337, 3.14159);

With the above Lua code, the results would be:

  • FooA = 10
  • FooB = 15
  • FooC = 22
  • FooD = 34
  • FooE = 1371
  • FooF = 1374.14159

As you can see, this is a particularly powerful feature… and hopefully you can already imagine a host of uses within your work!

Calling Lua functions from Delphi (including Parameters and Return Values)

Just as we can invoke registered Delphi methods from our Lua code, we can invoke functions defined in Lua from our Delphi code!

In Lua:

function LuaAdd(ValueA, ValueB)
  return ValueA + ValueB;
end

The above Lua code simply adds two values together, and returns the total to Delphi.

To invoke this method from Delphi:

with L4D1.Globals[LuaAdd].CallFunction([5, 10]) do
begin
  ShowMessage(Values[1].AsString);
  Cleanup;
end;

The above Delphi code invokes the Lua method LuaAdd, passing two Integer values (5 and 10) as Parameters. It then retrieves the result, and displays it in a Message Dialog.
We then call Cleanup; to remove this returned value from Lua’s stack. It is necessary to call Cleanup; when invoking Lua methods from Delphi, otherwise Lua’s stack gets “contaminated” by orphan (unwanted) values.

You’ll notice I use a with clause in Delphi, but you can also assign the item returned by CallFunction to a Variable! Just define a variable of the type TL4DMethodResultStack.

So.. the result of the above code looks like this:

Lua4Delphi - CallFunction

Lua4Delphi - CallFunction

In exactly the same way as Delphi can return multiple values from functions invoked by Lua, Lua can return multiple results from functions invoked by Delphi.

In Summary…

As it stands today, Lua4Delphi provides deep integration and interoperability between Lua and Delphi. No matter how complex your scripting requirement, Lua4Delphi can provide.

I am aware that this article is quite long, and contains a substantial amount of information to digest… however I’m sure you will quickly agree that integrating Lua into Delphi applications using Lua4Delphi is remarkably simple, and opens up a whole new world of possibility!

Next time…

The next article in this series will expand on this one, and introduce Lua Tables and Lua MetaTables: how to read them, how to write to them, how to compose them.. from Delphi.

Author: Simon J Stuart

Automation and Productivity Systems Specialist, Author of various Components, Libraries and Tools for Embarcadero Delphi, Embarcadero Technology Partner, Founder and CEO of LaKraven Studios Ltd, Father of 2 (+ 2 dogs), Credited Technical Editor, Published Technical Author, Seeker of peace!

37 Comments

  1. a cool Delphi Project! ;-)

  2. Great details Simon, good work … the integration look very streamlined.

    Since Lua 5.2 is quite hot (just released in December: http://www.lua.org/versions.html#5.2 ) and the Lua binding API didn’t seams to be changed … it will be possible to support it in Lua4Delphi ?

    PS: Any hints about the Monkey Groomer “scripting” with Lua (mentioned in past) ?

    • Lua 5.2 support will come shortly after all features of L4D (based on Lua 5.1) are available.
      I’m using Lua 5.1 specifically because LuaJIT uses the 5.1.4 base. If there was a LuaJIT for 5.2, I’d have already switched to that ;)

      Yes, MonkeyGroomer is being built with Lua4Delphi at its core, as Lua scripts will actually control the conversion of projects.

  3. Just curious, is TLuaEmbedded (Lua “Embedded” mode) using statical linking of the Lua library/objs ?

    That should be definitely awesome to support both for performance reasons and for embedability to create smart/scriptable but still portable x-plat applications (no install, no dependencies)

  4. Great work Simon!!! Really good job!

  5. Great work, Simon! You mentioned compatibility with LuaJIT, but this is a completely (and amazing) different engine. Do you support LuaJIT or just regular Lua VM? I saw some fantastic benchmarks on LuaJIT outperforming even Java and C#. It would be great if you target it.

  6. Great! Is there some evaluation version of Lua4Delphi?

    • Lua4Delphi itself is FREE for everyone, and is being released on Feb 1st.

      There’s a “Professional” pack which sits on top of it, providing vast amounts of automation to save lots of development time!

  7. Pingback: Lua4Delphi Released! | Reinvent The Wheel

  8. Very good job, Simon. I have changed the sources of your component to Lazarus 0.9.31 under Win7 x64 and it works very nice for me. Thanks a lot, man.

    • What have you changed to accomplish that?
      Keep in mind that the release available today is NOT the final release! In fact, this is only about 25% of the total system…. most of which is very-much specific to Delphi 2010-XE2!

      • I do have changed all filename which contains a . to an underline character and have create the component at runtime. I have tried to make a package for Lazarus, but the property editors (and some others more) are too Delphi specific. I have used your demo project and it works.

  9. will this library supports read/write delphi objects methods and properties using RTTI?

    • The next update (which I’m literally wrapping up right now) will introduce the framework for the generated Wrapper Units.

      Wrapper Units basically contain a series of Wrapper Types, enabling you to register Delphi types for use with Lua. This includes registering Delphi Classes, Components and their Instances, and does indeed support read/write Properties.
      The Wrapper Units themselves do NOT use the RTTI (the Wrapper Generator used to produce the Units does use RTTI)… which basically means no RTTI will be used in your applications in order for Lua4Delphi to work (a major win when it comes to performance).

      You will be able to do things in Lua like this:

      TheCaption = Button1.Caption;
      Button1.Caption = ‘Hello World’;

      So yes, you will be able to access read/write object methods and properties from Lua with Lua4Delphi, only you won’t need to use the RTTI to do it!

  10. Thank you for your answer and for your work.
    Well, can I ask you about this example?

    with L4D1.Globals[LuaAdd].CallFunction([5, 10]) do
    begin
    ShowMessage(Values[1].AsString);
    Cleanup;
    end;

    Small notice: “LuaAdd” – function name should be decorated with apostrophes, and now it seems that result have property VALUE, not VALUES.

    But, the main question is – it doesn’t works. No mistakes, Lua engine seems to found the function, but result is empty.

    • I believe the WYSIWYG editor has removed the “single-quotes” from around the Global Identifier… an issue I’ve had before (annoying, I know).

      “Values” is a typo, as I always use singular context when the method or property relates to a single item within an Array. Sorry about that one.

      You say the result is “empty”, how do you mean? You mean that “Value[1].AsString” returns an empty string?

      • Yes, I do. And 0 if Value[1].AsInteger.

        • I’m not experiencing this particular issue…. it was one I had before the very first update, and had resolved in the first update.
          I discovered that it was being caused by INLINING, and so in the Lua4Delphi.inc file I defined {$INLINE AUTO}, which made the results accessible.

          Are you sure your copy of Lua4Delphi is up-to-date? Either was, make sure Inline is set to AUTO in Lua4Delphi.inc and do a Clean and Build on your project.

          • The type of returned value is ‘Table’. But I didn’t found ability to enumerate the table elements or got element by index.

          • Okay, can you post your Lua code please?

            If it’s returning as type Table, then you must be using { and } around the return values, instead of ( and ).

            Table enumeration (reading AND writing) is coming in this update (it’s what I’m finishing off at this moment).

  11. Here is Lua code:
    —–
    function myfunction(x, y)
    return “sdfsdfsd” — x+ y

    end
    —-
    also, I tried to return 26 (the constant), x+y, and so on.
    I step into debug, and lua.get_top return me 15. 15 elements of stack after such a short code – is it normal?

    • Okay, I think I know what’s causing this issue.

      About to release another update (this is a BIG update)…. I belive genuinely it’ll fix ALL known bugs at this time, including the one you’re suggesting with incorrect numbers of items on the Stack.

      I’ve just spent 4 hours fixing a mistake where stack elements weren’t being popped when they should, or in the right numbers.

    • (x+y) is a comment

      • Yes, I was aware of that… but the code should still have returned a single String value rather than a Table + a bunch of non-existent values.

        This update absolutely fixes the issue (and a whole bunch of other issues).

        Just be ready to make a minor change to all of your Delphi-defined Lua methods (TL4DMethodStack has been changed to IL4DMethodStack)

  12. Hi Stuart!
    I got the last update of your library and check again my simple function. I got the result, but at [-1] position on the stack:
    with L4D1.Globals['myfunction'].CallFunction([6, 11]) do
    str := Value[-1].AsString; // <- 17
    Is it correct?

    • The next question.
      I decide to make a stress-test:
      for i := 0 to 100000 do
      L4D1.Globals['myfunction'].CallFunction([6, 11]);
      and the program terminates without any messages. Can you do that is this case throw some trappable exception?

      • This happens if you don’t call Cleanup.

        Try this instead:

        for I := 0 to 100000 do
        with L4D1.Globals['myfunction'].CallFunction([6, 11]) do
        Cleanup;

        It is very important that you call Cleanup whenever you’re done with a Lua function’s results. If you don’t they don’t get popped from the stack properly. This is something I’ve covered in EVERY webinar on Lua4Delphi.

    • I’ve literally no idea why yours is getting results on a negative Index instead of positive. It could be that your DLL is handling the result stack differently (though this SHOULDN’T be the case).

      I’ll get back to you on this, but if you could send me your source code that would be a massive help.

  13. Hey Stuart.

    I wonder how can i load a .lua file, and get a value from some variable ?

    • Firstly, it’s “Simon”, not “Stuart”. Don’t worry…. common mistake ;)

      Assuming your Lua4Delphi instance is called “L4D1″ you can load a Lua script file thusly:

      L4D1.InjectLuaFile(‘C:\MyLuaFile.lua’);

      And to retrieve a Global value from Lua:

      ShowMessage(L4D1.Globals['MyVariableName'].AsString);

      I recommend you attend the next webinar to go through the vast array of features in Lua4Delphi (even more in the coming update).

  14. Thank you very much about the incredible fast response.
    I have tried to load this script (containing inside test.lua file) :

    IpW = 5
    UserW = 6
    PassW = 7

    trough the method you have suggested:

    but it keeps rising the follow error :
    “An error occured while executing Lua code: attempt to call a string value.”

    - What could possible be wrong ?

    P.S. : I am very sorry about the way i refer you.

Leave a Reply

Required fields are marked *.

*


*