develooper Front page | perl.perl5.porters | Postings from January 2003

FW: eval_sv and exception handling

From:
rwilding
Date:
January 30, 2003 01:47
Subject:
FW: eval_sv and exception handling
Message ID:
DD4AFB45E2CCD211B6EE0008C7333BCF035433A0@ntxmel01.micron.com


Hi Ken,

Thanks for your reply, i'll give your suggestion a try!

I've actually sorted out my problem, i dont how but i managed to omit a flag
for eval_sv and then over complicate the situation - i guess i'd been
staring at the problem too long!

Thanks for your help though and appologies to you all for filling up this
already busy list.

Rich

-----Original Message-----
From: Fox, Ken (K.C.)
To: 'rwilding'
Sent: 29/01/2003 11:45
Subject: RE: eval_sv and exception handling

I think you're making life hard on yourself by
not using Perl for most of the job. When I need
to use C++ with Perl, I try to do as much as I
can in Perl and then make a thin "glue" layer
in C++.

Here's what I'd do.

(1) Write Perl code to evaluate a user's expression.
    You may need to add parameters or change the
    package (scope) the expression is evaluated in,
    but it will probably look something like this:

    use vars qw($r);
    sub eval_user_expression {
       my ($code) = @_;
       my $sub = eval 'sub { eval { $r = '. $code .' } }';
       if ($sub && defined($sub->())) {
          return 1
       }
       return 0
    }

(2) Load your Perl code when you create the interpreter.
    I usually use a '-e' flag in perl_parse(), but you
    can load a separate file too. (The advantage of '-e'
    is everything is contained in the C++ executable. Of
    course this could be a disadvantage too...)

(3) Use perl_get_cv() to fetch the CV corresponding to
    the "eval_user_expression" sub.

(4) Use perl_call_sv() to call eval_user_expression.
    It will look something like this:

        dSP;

        ENTER;
        SAVETMPS;

        // expr is the user's expression to evaluate

        PUSHMARK(sp);
        XPUSHs(sv_2mortal(newSVpv(const_cast(char *, expr),
strlen(expr))));
        PUTBACK;

        int count = perl_call_sv(eval_user_expression, G_SCALAR);

        SPAGAIN;

        if (count == 1) result = POPs;

        if (SvTRUE(result))
        {
            // fetch the expression result from $r
            // if you're going to keep it around for a
            // while, you might want to copy it because
            // the next user expression to come through
            // will over-write $r.
        }
        else
        {
            STRLEN ignore;
            fprintf(stderr, "expression '%s' failed: %s\n",
                    expr, SvPV(GvSV(PL_errgv), ignore));
        }

        PUTBACK;

        FREETMPS;
        LEAVE;

Hope that helps. I cut and pasted the code from a large
application I wrote so there may be compile errors. It's
also been a while (version 5.5) since I seriously looked
at this code so there may be some differences due to
threading and internals changes. It works on 5.8, but
I haven't checked to see if any of the API I'm using has
been deprecated.

Good luck.

- Ken

-----Original Message-----
From: rwilding [mailto:rwilding@micron.com]
Sent: Wednesday, January 29, 2003 12:31 PM
To: 'perl5-porters@perl.org'
Subject: eval_sv and exception handling


Hi All,

I'm embedding Perl as the scripting language for a c++ application.
Users
will be able to enter perl expressions to be evaluated and the results
used
elsewhere in the program (by no means spectacular but i mention this so
you
know that I have no control of what expression are being evaluated).

Earlier this week i struggled with trapping and reporting any
syntax/compiler time errors in the entered expression.  Since I dont
want a
user's expression (or the results of the expression) to interfere with
the
perl namespace I encapsulate them in an anonymous subroutine, i.e.

		SV* cv=eval_pv("sub{return  [user's expression]}");

eval_pv hides any compilation errors (such as the use mistyping) since
it is
just preparing the anonymous sub and it an error occurs it simply
returns an
invalid reference, it does this without setting ERRSV.  The only way to
detect the error is to manually inspect the cv->sv_any member for NULL.
I
decided to write an alternative to eval_pv which allows me to detect the
presence of the error (and pass Perl's error message to the end user).
This
is a rewrite of eval_pv from the Perl source which required changing the
flags for the internal call to eval_sv and some $@ cleanup . so far so
good.

Now, I have a new problem:  If the expression compiles and MY_eval_pv
returns a valid CV and that CV kicks out an exception there is no way to
trap that exception! In plain perl i could do this (gf is an imaginary
user
typo):

		$test=eval "sub{$p=5;$out=gf$p;return $out}";
		eval{
			&$test
		}	
		if($@)
		{
			print "Error", $@
		}

But there is no way (that i can see) to do an eval round an anonymous
subroutine called with call_sv.  I've looked through the Perl source and
can't find any clues of how to achieve this.  One idea I had was to
somehow
tie the CV with a new RV which was named (say $temp) and then do
eval_pv("&$temp"); but that just seems messy (and I dont know if the
results
from the routine will be in the right place on the stack after the eval
-
though i expect they would).

Any ideas??, if there isn't a way to do this at the moment I think it
would
be an important addition to the Perl core - at least for embedded perl
apps.

Thanks in advance,

Rich



nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About