17

I'm currently embedding Lua and using it as a glorified intelligent config file. However, I think I'm missing something since people rave about the uses of Lua.

For example, I can easily explain why you might use shell scripting instead of C by showing this example (admittedly , boost regexp is overkill):

#include <dirent.h> 
#include <stdio.h> 
#include <boost/regex.hpp>

int main(int argc, char * argv[]) {
    DIR           *d;
    struct dirent *dir;
    boost::regex re(".*\\.cpp$");
    if (argc==2) d = opendir(argv[1]); else d = opendir(".");
if (d) {
    while ((dir = readdir(d)) != NULL) {
            if (boost::regex_match(dir->d_name, re)) printf("%s\n", dir->d_name);
    }

    closedir(d);
}

return(0);

and compare it to:

for foo in *.cpp; do echo $foo; done;

Are there any examples that you can give in Lua which can make it 'click' for me?

EDIT: Maybe my problem is that I don't know Lua well enough yet to use it fluently as I'm finding it easier to write C code.

EDIT2:

One example is a toy factorial program in C++ and Lua:

#include <iostream>

int fact (int n){
    if (n==0) return 1; else
    return (n*fact(n-1));
}

int main (){
    int input;
    using namespace std;
    cout << "Enter a number: " ;
    cin >> input;
    cout << "factorial: " << fact(input) << endl;
    return 0;
}

Lua:

function fact (n)
    if n==0 then
        return 1
    else 
        return n * (fact(n-1))
    end
end

print ("enter a number")
a = io.read("*number")
print ("Factorial: ",fact(a))

Here, the programs look alike, but there's clearly some cruft in the include, namespace and main() declarations you can get rid of. Also remove variable declarations and strong typing.

Now are people saying this is the advantage which adds up over a larger program, or is there more to it? This doesn't stand out in the same way as the bash example.

btwiuse
  • 2,585
  • 1
  • 23
  • 31
bugmenot77
  • 621
  • 3
  • 6
  • 14
  • Duplicate of http://stackoverflow.com/questions/591523/when-is-it-good-to-use-embedded-script-language-like-lua? – Reed Copsey Jun 02 '09 at 17:46
  • Learning other programing languages will make you a better programmer. You learn to think differently, even in C. – 0x6adb015 Jun 02 '09 at 18:32
  • The near duplicate is approaching the same kind of question from a different direction. Cross linking them is good, but I don't see this as an "exact" duplicate myself. – RBerteig Jun 05 '09 at 00:28
  • The only "real" advantages of Lua over C++ is that you _don't have to compile it_, and there are a lot of premade Lua interpreters that you can integrate really easily. Sure, you could use C++, but then you've either gotta interpret it, or dynamically link files to your code to use it, and that's just a pain. With Lua, you set a couple of convenient globals (or however you decide to make it work), run an interpreter you didn't have to write, and _bam_, it works. – Nic Feb 16 '17 at 15:08

9 Answers9

13

Using a scripting language such as Lua has many other benefits.

A couple of advantages to Lua vs. C++:

  • It's often shorter in terms of development time due to the high-level nature, as in your example.
  • It doesn't require recompilation to change behavior.
  • Behavior can be changed on non-development machines.
  • Prototyping is very fast and easy, since you can just tweak logic at runtime.
Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • 3
    ... functionality can be modified and augmented/improved by non-developers – none Jun 02 '09 at 19:31
  • 1
    @none: Good point, but I still consider Lua a programming language in its own right, so IMO, you still need a "developer" (although perhaps a less experienced one). – Reed Copsey Jun 02 '09 at 19:50
5

Scripting languages reduce the effort required to build complex GUIs that otherwise require a lot of framework glue and repetition of code. Several GUI toolkits are available with Lua bindings, including wxWidgets and the IUP toolkit.

In both of those bindings, first class function values and full closures make event callbacks easy to code and easy to use.

A large application using Lua at its core (such as Adobe Photoshop Lightroom) has an outer C/C++ program that hosts the Lua interpreter and provides access to its core features by registering C functions with that interpreter. It typically implements compute-intensive core functions in C functions, but leaves the overall flow, operation, and even the GUI layout to Lua scripts.

I have found in my own projects that it is often the case that the stock standalone Lua interpreter (lua.exe or wlua.exe) is sufficient for the outer application when combined with IUP loaded at run time along with one or two custom DLL-based Lua modules coded in C that implement features that require that level of performance, or features that are implemented via other C-callable libraries.

The important points for my projects have included:

  • True tail calls allow for a easy expression of finite state machines.
  • Garbage collected memory management.
  • Anonymous functions, closures, first class function values.
  • Hash tables.
  • Rich enough string library.
  • Userdata extends the garbage collector to C side allocations.
  • Metatables allow a rich variety of object oriented and functional techniques.
  • Small but sufficiently powerful C API.
  • Good documentation, with open source as a backup.
  • Good user to user support through the mailing list and wiki.
  • Powerful modules such as a PEG parser available from the authors and from the community.

One of my favorite examples to cite is a test jig I built for an embedded system that required about 1000 lines of Lua and 1000 lines of C, ran under lua.exe, and used IUP to present a full Windows GUI. The first version was running in about a day. In C++ with MFC, it would have been at least a week's work, and many thousands of lines of code.

Community
  • 1
  • 1
RBerteig
  • 41,948
  • 7
  • 88
  • 128
  • Tail calls can always be turned into iteration, RAII means you don't have garbage, C++11 lambdas, `std::unordered_map`, `std::string` and `stlport::rope`, no garbage, `boost::mixins`, and `boost::spirit`. Nothing you've said shows Lua is better than C++, and I guarantee you I could have written your program using QT in less time than you took to write it in Lua. – Alice Dec 18 '15 at 00:16
  • LPEG rocks~~ And Torch is also good! – Decula Nov 22 '19 at 18:39
3

Try to implement a Lua table in C/C++, you'll see the strength of Lua right there.

In Lua:

a["index"] = "value"

In C, start by reading about linked list...

C++ STL may help, but it is going to be a lot more verbose than Lua.

Also, Lua makes great glue. It is so easy (IMHO) to interface to C.

0x6adb015
  • 7,473
  • 4
  • 24
  • 38
  • Thanks. Have you used Qt's QList? Just wondering if that offers something comparable. – bugmenot77 Jun 02 '09 at 18:06
  • No. I'm sure there is some lib that offers similar stuff, but as simple as Lua? – 0x6adb015 Jun 02 '09 at 18:30
  • 4
    What about std::map in C++? – bdonlan Jun 02 '09 at 18:34
  • 1
    A std::map will be orders of magnitude slower for large maps - you'd need a hash map. Google offers several (http://code.google.com/p/google-sparsehash/), or you can use tr1::unordered_map. Or @bugmenot77, with Qt you'd use QHash. – gnud Jun 02 '09 at 18:50
  • 1
    However, most Lua maps I've seen are small (less than 100 entries). Then again, Lua is dynamically-typed so you'd need something like a std::map. – Max Lybbert Jun 02 '09 at 19:22
  • 5
    Its not only dynamically typed, but values of any type (except nil) can be used to index the table. There are times when its handy to be able to use a function as a key, for instance. – RBerteig Jun 05 '09 at 00:27
  • 1
    This is utterly ridiculous; C++ has std::map and std::unordered_map, which are just as fast if not faster than Lua's tables, and offer almost identical syntax. If you need something even more powerful, try boost's containers. – Alice Aug 10 '15 at 06:20
  • @Alice, While you're correct in this specific case, the difference in syntax starts to stand out when things get more complicated, e.g nested hash tables and hash tables pointing to closures, etc. After a certain point of complexity, Lua's syntax is orders of magnitude clearer. – Hello World Dec 15 '15 at 09:57
  • @HelloWorld That is utterly facile; a nested `std::unordered_map` functions identically in many cases as Lua; the only difficult part is the type, which can easily be fixed with, uh, a typedef. As for closures and such, that is an equally invalid argument; heard of `std::function`? – Alice Dec 17 '15 at 23:56
  • @Alice, You seem to be implying that C++ is completely superior to Lua, and possibly to all other dynamic languages. If so, why do people use those languages? The above were just some specific examples. While everything can be done with C++, the hot-swappable and dynamic nature of dynamic languages often makes the code clearer. `std::function` is not as flexible as Lua's first class functions. In lua, you can modify an object's methods and fields at runtime, while this can be emulated with `std::unordered_map`, it certainly won't make cleaner code. As to the ad hominem, I'll ignore that. – Hello World Dec 18 '15 at 08:33
  • @HelloWorld Don't put words in my mouth; they smack of implicatures. I've never implied C++ is superior to Lua, I've implied _your arguments are without merit_. Nested hashtables and hashtables of closures are trivial in C++, via `std::unordered_map`, and the syntax is not ungainly at all (except for the type declaration, which can be solved with a typedef). – Alice Dec 19 '15 at 15:53
  • - Nesting: Lua's hashtable syntax is cleaner for modifying objects at runtime (attaching/detaching methods and fields), this is often combined with nesting, which is what I originally meant. It is certainly possible with std::unordered maps but it wouldn't be as clean. This is not merit-less. - std::unordered_map can only point to a single type (though boost::variant works around that limit). Lua's hashtables don't have that limit. - Lua can manufacture and return new functions at runtime. Can std::function do that? (Honest question). Apologize for the strawman. – Hello World Dec 20 '15 at 08:18
  • @HelloWorld Again, that is meritless; in each case, it is `map[item]` or `map[item][item2]`; how is the Lua case more clean? Homogeneous maps can always represent heterogeneous maps, and het-maps are valid in C++14; `boost::any`, `boost::variant`, or even C's `union` all exist to get what you want, and with no loss of syntax or performance. `std::function` and `std::bind` can be used to assemble new functions at runtime, or even to adjust the interface of functions, bind methods, and serve as full fledged delegates. As well, any syntax warts you dislike you can smooth over with macros. – Alice Dec 20 '15 at 19:20
  • @Alice, I'm probably failing to describe Lua's syntax merits with words. I'll show an example: `obj = {}; obj.sub.x = {}; obj.x = 4; obj.sub.a = "string"; obj.sub.b = 23.4`. Try doing that in C++, in such a way that the nested structure is **completely modifiable** in arbitrary ways ( more levels, less levels, etc) at runtime, and where methods can be detached or attached to the object at will (even functions that have different return types and parameters). Note that the hashtables are carrying different types as values too. That's hard with c++, and an answer probably won't fit in a comment. – Hello World Dec 20 '15 at 21:16
  • Trivial; `std::unordered_map obj`. Or if you prefer, `std::unordered_map`. There, now the C++ hashtable can easily accommodate any type and any value. Oh, and unlike Lua? With simple applications of type erasure or unions, we can easily restrict from a straight `boost::any` to the specific types we want: `std::unordered_map>`. The nested structure is completely modifiable in arbitrary ways, including ways Lua cannot modify them (adding types and restricting types). Where is the difficulty? – Alice Dec 26 '15 at 00:38
  • @HelloWorld As indicated above, it is trivial, not hard. The type is the only part even vaguely difficult to get down; everything after that is `type[] = thing`, and the type can be trivially solved with a typedef. You seem to confuse ease of use with expressiveness as well; C++'s way of doing this is both much more expressive (it's easy to constrain, modify, and specify what sort of types you want) and just as easy to use (the syntax is nearly identical). `std::function` and `boost::any` are as easy to use as Lua, and more powerful. Stop comparing Lua to C++03 if you want an honest comparison – Alice Dec 26 '15 at 02:38
  • @Alice I tried the `std::unordered_map obj` technique. My main problem with it is that the casting must be explicit, making the syntax ugly and runtime errors plenty. Furthermore, lack of GC makes hash table management much harder than Lua. It works, but the aformentioned drawbacks makes it not useful in practice. Any way to fix those? Esp. the casting part. – Hello World May 19 '16 at 11:30
  • @HelloWorld If you are willing to step outside of the bare standard for a moment, `boost::any` removes the casting, and using a `boost::pool` or `std::shared_ptr` removes the GC problems, assuming you are using RAII techniques. – Alice May 22 '16 at 05:59
  • 2
    I guess this reinforces my point, and the point OP tried making; making such a versatile structure in C++ is *possible*, but it is not "trivial", and it definitely requires good STL knowledge (Look at all the STL/Boost structures you've brought up so far). Lua's simplicity is not "meritless" here. – Hello World May 23 '16 at 08:26
  • Additionally, considering the fact that Lua is often used by novice/casual plugin devs to extend your software, even a minimal STL knowledge requirement becomes a true deal breaker. (I am hypothetically assuming for a moment C++ is equally convenient as a plugin language on a technical level, e.g. no complilation needed, etc.). – Hello World May 23 '16 at 08:34
3

I don't know if I make it 'click' for you but I'll try.

One of the advantages of embedding Lua is, that you can not only use it as a configfile but actually offer your C/C++-interfaces to lua and 'script' their usage via the Lua-scripting language.

If you want to change the behaviour/logic of your application, you just have to change the code in the Lua-script without the need to recompile the whole application.

Prominent uses are game logics like AI oder state machines, where a fast roundtrip-time from change to play is essential for developing the game.

Of course, the main logic has then to be present within the Lua-script, not within the C/C++-code, to be effectively used.

Kosi2801
  • 22,222
  • 13
  • 38
  • 45
  • Thanks. I probably exaggerated a bit and do use Lua to implemnet AI in game logic. Doing this, I did come across issues as to whether stuff should be implemented in C, Lua or some mix. – bugmenot77 Jun 02 '09 at 18:00
2

LUA has closures, and closures rock. For example:

function newCounter ()
  local i = 0
  return function ()   -- anonymous function
           i = i + 1
           return i
         end
end

c1 = newCounter()
print(c1())  --> 1
print(c1())  --> 2

You can create a function and pass it around. Sometimes it is more handy than creating separate class and instantiate it.

Jakub M.
  • 32,471
  • 48
  • 110
  • 179
2

Programming just in C can be a very tedious and redundant task, this certainly applies when compared to more abstract, high level languages.

In this sense, you can get started and finish things much more quickly than doing everything directly in C, this is because many things that need to be set up, done and cleaned up explicitly and manually in C, are often implicitly and automatically handled by a scripting language such as Lua (e.g. imagine memory management).

Similarly, many more abstract data structures and algorithms are often directly provided by such high level languages, so you don't have to re-invent the wheel and re-implement it, if all you need is a standard container (think linked list, tree, map etc).

So, you can get a fairly good ROI when using a fairly abstract scripting language such as Lua or even Python, especially if the corresponding language comes with a good library of core functionality.

So, scripting is actually ideal in order to prototype ideas and projects, because that's when you need to be able to concentrate on your effort, rather than all the mechanical redundancies that are likely to be identical for most projects.

Once you got a basic prototype done, you can always see how to improve and optimize it further, possibly be re-implementing essential key functionality in C space, in order to improve runtime performance.

none
  • 5,701
  • 28
  • 32
1

I use a game engine called Love2D which uses Lua for writing games. All the system calls and heavy-lifting is done in a C program which reads a Lua script.

Writing a game in C or C++, you find yourself trying to work with the subtleties of the system rather than just implementing your ideas.

Lua allows for "clean" dirty-style coding.

Here's an example of a game object written in pure lua:

local GameObj = {}              -- {} is an empty table
GameObj.position = {x=0,y=0}
GameObj.components = {}

function GameObject:update()
    for i,v in ipairs(self.components) do    -- For each component...
        v:update(self)                       -- call the update method
    end
end

To instantiate:

myObj = setmetatable({},{__index=GameObj})
-- tables can have a meta table which define certain behaviours
-- __index defines a table that is referred to when the table
-- itself doesn't have the requested index

Let's define a component, how about keyboard control? Assuming we have an object that does input for us (that would be supplied C-side)

KeyBoardControl = {}
function KeyBoardControl:update(caller)
    -- assuming "Input", an object that has a isKeyDown function that returns
    -- a boolean
    if Input.isKeyDown("left") then
        caller.position.x = caller.position.x-1
    end
    if Input.isKeyDown("right") then
        caller.position.x = caller.position.x+1
    end
    if Input.isKeyDown("up") then
        caller.position.y = caller.position.y-1
    end
    if Input.isKeyDown("down") then
        caller.position.y = caller.position.y+1
    end
end
--Instantiate a new KeyboardControl and add it to our components
table.insert(myObj.components,setmetatable({},{__index=KeyboardControl})

Now when we call myObj:update() it will check inputs and move it

Let's say we'll be using plenty of this kind of GameObj with a KeyboardControl, we can instantiate a prototype KeyObj and use THAT like an inherited object:

KeyObj = setmetatable( {}, {__index = GameObj} )
table.insert(KeyObj.components,setmetatable( {}, {__index = KeyboardControl} )    

myKeyObjs = {}

for i=1,10 do
    myKeyObjs[i] = setmetatable( {}, {__index = KeyObj} )
end

Now we have a table of KeyObj that we can play with. Here we can see how Lua provides us with a powerful, easy to extend, flexible object system which allows us to structure our program in accordance with the problem we're trying to solve, rather than having to bend the problem to fit into our language.

Also, Lua has some nice other features like functions as first-class types, allowing for lambda programming, anonymous functions, and other stuff that usually has comp-sci teachers smiling creepily.

1

For an example of where Lua fits better then c++ look at distributing scripts. Mush Client offers Lua as a scripting language. As shown by the link above you can do a lot with Lua to extend the program. Unlike C++ though Lua doesn't have to be compiled and can be restricted. For example you can sandbox Lua so it can't access the file system. This means that if you get a script from someone else it is incapable of destroying your data since it can't write to the disk.

Jared
  • 39,513
  • 29
  • 110
  • 145
  • Thanks. I can totally see the case where Lua is embedded or used to extend, plugin, script etc. But I also hear of people switching from C to Lua and only writing C 'modules' when speed is a factor. I suspect maybe I'm not fluent enough in Lua and if I was I could write faster/easier in Lua than C. – bugmenot77 Jun 02 '09 at 18:16
  • C++/C can both be sandboxed and restricted as well. Look at NaCl. – Alice Aug 10 '15 at 06:22
1

The main advantages of Lua as a programming language (apart from the embeddability) are

  • Powerful, efficient hash table as the main data structure
  • String-processing library with an excellent balance of complexity and expressive power
  • First-class functions and generic for loop
  • Automatic memory management!!

It's hard to find a short example that illustrates all these. I have 191 Lua scripts in my ~/bin directory; here's one that takes the output of pstotext and joins up lines that end in a hyphen:

local function  printf(...) return io.stdout:write(string.format(...)) end
local function eprintf(...) return io.stderr:write(string.format(...)) end

local strfind, strlen = string.find, string.len

function joined_lines(f)
  return coroutine.wrap(function()
                          local s = ''
                          for l in f:lines() do
                            s = s .. l
                            local n = strlen(s)
                            if strfind(s, '[%-\173]$', n-1) then
                              s = string.sub(s, 1, n-1)
                            else
                              coroutine.yield(s)
                              s = ''
                            end
                          end
                        end)
end

-- printf('hyphen is %q; index is %d\n', '­', string.byte('­'))

for _, f in ipairs(arg) do
  for l in joined_lines(io.popen('pstotext ' .. f, 'r')) do
    printf('%s\n', l)
  end
end

This example shows several features to advantage but does nothing interesting with tables.

Here's a short snippet from a Key Word In Context indexing program, which fetches context from a table and formats the key word in context. This example makes more extensive use of nested functions and shows some more table and string stuff:

local function showpos(word, pos, lw, start)
  -- word is the key word in which the search string occurs
  -- pos is its position in the document
  -- lw is the width of the context around the word
  -- start is the position of the search string within the word
  local shift = (start or 1) - 1  -- number of cols to shift word to align keys
  lw = lw - shift -- 'left width'
  local rw = cols - 20 - 3 - lw - string.len(words[pos])  -- right width
  local data = assert(map:lookup(pos)[1], "no map info for position")
     -- data == source of this word
  local function range(lo, hi)
    -- return words in the range lo..hi, but only in the current section
    if lo < data.lo then lo = data.lo end
    if hi > data.hi then hi = data.hi end
    local t = { }
    for i = lo, hi-1 do table.insert(t, words[i]) end
    return table.concat(t, ' ')
  end
  -- grab words on left and right, 
  -- then format and print as many as we have room for
  local left  = range(pos-width, pos)
  local right = range(pos+1, pos+1+width)
  local fmt = string.format('[%%-18.18s] %%%d.%ds %%s %%-%d.%ds\n', 
                            lw, lw, rw, rw)
  printf(fmt, data.title, string.sub(left, -lw), word, right)
end
Norman Ramsey
  • 198,648
  • 61
  • 360
  • 533
  • 2
    std::unordered_map, std::string, C++11 for loops and lambdas, and RAII and std::shared_ptr are all just as fast and easy to use as lua's alternatives. – Alice Aug 10 '15 at 06:21