Luftkissenboot has asked for the wisdom of the Perl Monks concerning the following question:

Happy Newton's Birthday, monks!

I have decided to give myself the winter solstice present of picking up Lua, with an intent to use it as a quick embedded scripting language (somewhat silly from Perl, I know, but I plan to also use the same Lua code elsewhere). In doing so, I have run into a situation with namespaces that I think may actually require string eval for the first time in >8 years of Perl, which causes my codesmell sensors to go haywire, and thus, I am seeking your sage advice.

Essentially, I want to write a wrapper module around Inline::Lua which will facilitate config information (paths, file loading, etc) and provide a simple OO interface of ->load( 'module' ) and ->run( 'module', 'sub', @args ) However, for future-proofing, it is desirable to load each Lua module's symbols into their own namespace (package), indexed by Lua module name, so that collisions between symbols don't take place when loading multiple Lua modules.

  1. While this seems silly, almost like the varvar/symbolic reference issue, the ideal way would be to have dynamic package Foo::$bar; available, I was unable to do so, and ended up with an eval EXPR in the ->load() method, which is essentially forced upon me, due to the way that Inline crams subs into the current package (not that that is inherently a bad thing, it's really ideal for Inline, just not for this application). So, first, is there a way to avoid the string eval in the ->load() method below?
  2. Also, in trying countless ways to directly reference the loaded sub symbolically via something along the lines of:
    local *func = $main::{"Lua::${modname}::${funcname}"}
    I have been unable to get a handle to it from the ->run() method. Thus, is there also a way to avoid this, and get rid of the second string eval?

    Question 2 has kindly been sorted by kennethk.

Replacing one or both with a block eval would be ideal. While the code below does "work" (I'll be adding further error checking, of course), it still causes me no small consternation in thinking that there is a better way to do it.

Thus, I throw myself upon the mercy of the monks during this fine Kwanzaa season. Thank you ever so much.

(NB: Inline::Lua may be difficult to install properly; but it should not be necessary to install to simply examine the code.)

----------- Lua.pm ---------------

package Lua; use strict; use warnings; use Carp qw( croak ); #use Inline; my $luadir = '.'; sub new { my( $class ) = @_; my $self = {}; return( bless( $self, $class ) ); } sub load { my( $self, $module ) = @_; my $src_file = $module . '.lua' unless( $module =~ m/.*\.lua$/ ); my $path = path_join( $luadir, $src_file ); open( my $fd, '<', $path ) or warn "$src_file is inaccessible" and return; my $luacode = do { local $/; <$fd> }; close( $fd ); ####################################################### my $code = sprintf( 'package Lua::%s;' . 'use Inline;' . 'Inline->bind( Lua => $luacode );' . 'return( grep { ! /^BEGIN$/ } keys %%Lua::%s:: + )', $module, $module ); my @symbols = eval $code; package Lua; chomp $@ if( $@ ); warn "Error compiling $path: '$@'" and return if( $@ ); ####################################################### return( @symbols ); } sub run { my( $self, $module, $funcname, @args ) = @_; ###################################################### my $sub = sprintf( 'Lua::%s::%s( @args );', $module, $funcname ); my @retval = eval $sub; chomp $@ if( $@ ); warn "Error executing $module::$funcname: $@" and return if( $@ ); ###################################################### return( @retval ); } sub path_join { my $pathchar = ( $^O eq 'MSWin32' ) ? "\\" : '/'; return( join( $pathchar, @_ ) ); } 1; __END__

-------- lua.pl --------

#!/usr/bin/perl use strict; use warnings; use lib '.'; use Lua; my $lua = Lua->new(); my @symbols = $lua->load( 'foo' ); $lua->run( 'foo', 'Greet', 'grandma', 'pie' ); __END__

----------- foo.lua -------------

function Greet( name, treat ) print( "Hello, " .. name .. ". Would you like some " .. treat .. +"?\n" ) end
  • Comment on Inline::* and string eval -- Have I found a place where it might actually be necessary?
  • Select or Download Code

Replies are listed 'Best First'.
Re: Inline::* and string eval -- Have I found a place where it might actually be necessary?
by kennethk (Abbot) on Dec 25, 2008 at 18:21 UTC

    You could use symbolic code references in place of your eval sting (in blocks where you turn off strict refs). Unfortunately, that doesn't really solve your issue and still results in more code. It does bookend your statements and should be clearer for maintenance. You could also just create a code ref for your run routine and then pass it arguments later to decouple, a la

    my $code; my $sub_ref = 'sample'; #my $sub_ref = "Lua::$module::$funcname"; { no strict 'refs'; $code = \&{"sample"}; } my @args = (1,2,3); print $code->(@args); sub sample { return shift @_; }

      Aha, brilliant! That was the syntax for reaching into the package symbol table that I couldn't quite wrap my mind around.

      I ended up having to use: my $sub = \&{"Lua::${module}::${funcname}"}; though, of course, to separate the :: from the variable names. So, that's one string eval gone!

      Thank you.

        It can also be done via %::
        \&{ $::{'Lua::'}{$module.'::'}{$funcname} };

        but no strict 'refs'; is a lot easier.

        By the way, Module::Pluggable is now part of core. I'd use that over playing with the symtab if applicable.

Re: Inline::* and string eval -- has become Inline::Wrapper
by Luftkissenboot (Novice) on Dec 28, 2008 at 02:22 UTC

    With the advice provided to the above questions, I have whipped this small idea up into a proper CPAN module, and uploaded it as:

    Inline::Wrapper

    Thanks, kennethk and ikegami for clarifying the package symbol table dereference syntax.