Hi, attached are the test for new convenience sub, and also small code to play with, it is really simple.
So the proposed convenience sub becomes this way: =comment An interpreter method, export_to_tcl, takes a hash as arguments, which represents named parameters, with following allowed values: namespace => '...', - tcl namespace, where commands and variables are to be created, defaults to 'perl'. If '' is specified - then global namespace is used. A possible '::' at end is stripped. subs => { ... }, - anonymous hash of subs to be created in Tcl, in the form /tcl name/ => /code ref/ vars => { ... }, - anonymous hash of vars to be created in Tcl, in the form /tcl name/ => /code ref/ subs_from => '...', - a name of Perl namespace, from where all existing subroutines will be searched and Tcl command will be created for each of them. vars_from => '...', - a name of Perl namespace, from where all existing variables will be searched, and each such variable will be tied to Tcl. An example: use strict; use Tcl; my $int = new Tcl; $tcl::foo = 'qwerty'; $int->export_to_tcl(subs_from=>'tcl',vars_from=>'tcl'); $int->Eval(<<'EOS'); package require Tk button .b1 -text {a fluffy button} -command perl::fluffy_sub button .b2 -text {a foo button} -command perl::foo entry .e -textvariable perl::foo pack .b1 .b2 .e focus .b2 tkwait window . EOS sub tcl::fluffy_sub { print "Hi, I am a fluffy sub\n"; } sub tcl::foo { print "Hi, I am foo\n"; $tcl::foo++; } =cut sub export_to_tcl { my $int = shift; my %args = @_; # name of Tcl package to hold tcl commands bound to perl subroutines my $tcl_namespace = (exists $args{namespace} ? $args{namespace} : 'perl::'); $tcl_namespace=~s/(?:::)?$/::/; # a batch of perl subroutines which tcl counterparts should be created my $subs = $args{subs} || {}; # a batch of perl variables which tcl counterparts should be created my $vars = $args{vars} || {}; # TBD: # only => \@list_of_names # argument to be able to limit the names to export to Tcl. if (exists $args{subs_from}) { # name of Perl package, which subroutines would be bound to tcl commands my $subs_from = $args{subs_from}; $subs_from =~ s/::$//; for my $name (keys %{"$subs_from\::"}) { #print STDERR "$name;\n"; if (defined &{"$subs_from\::$name"}) { if (exists $subs->{$name}) { next; } #print STDERR "binding sub '$name'\n"; $int->CreateCommand("$tcl_namespace$name", \&{"$subs_from\::$name"}, undef, undef, 1); } } } if (exists $args{vars_from}) { # name of Perl package, which subroutines would be bound to tcl commands my $vars_from = $args{vars_from}; $vars_from =~ s/::$//; for my $name (keys %{"$vars_from\::"}) { #print STDERR "$name;\n"; if (defined ${"$vars_from\::$name"}) { if (exists $vars->{$name}) { next; } #print STDERR "binding var '$name' in '$tcl_namespace'\n"; local $_ = ${"$vars_from\::$name"}; tie ${"$vars_from\::$name"}, 'Tcl::Var', $int, "$tcl_namespace$name"; ${"$vars_from\::$name"} = $_; } if (0) { # array, hash - no need to do anything. # (or should we?) } } } for my $subname (keys %$subs) { #print STDERR "binding2 sub '$subname'\n"; $int->CreateCommand("$tcl_namespace$subname",$subs->{$subname}, undef, undef, 1); } for my $varname (keys %$vars) { #print STDERR "binding2 var '$varname'\n"; unless (ref($vars->{$varname})) { require 'Carp.pm'; Carp::croak("should pass var ref as variable bind parameter"); } local $_ = ${$vars->{$varname}}; tie ${$vars->{$varname}}, 'Tcl::Var', $int, "$tcl_namespace$varname"; ${$vars->{$varname}} = $_; } } # extra convenience sub, binds to tcl all subs and vars from perl tcl:: namespace sub export_tcl_namespace { my $int = shift; $int->export_to_tcl(subs_from=>'tcl', vars_from=>'tcl'); } I think this is good for inclusion. I will change =comment/=cut to proper POD explanations afterwards. Regards, Vadim. > -----Original Message----- > From: Gisle Aas [mailto:gi...@activestate.com] > Sent: Thursday, January 20, 2011 12:07 AM > To: Konovalov, Vadim (Vadim)** CTR ** > Cc: Jeff Hobbs; tcltk@perl.org > Subject: Re: bind some said tcl to perl - all at once > > On Jan 19, 2011, at 17:47 , Konovalov, Vadim (Vadim)** CTR ** wrote: > > > First, I did not liked the name bind_... either. > > create_commands obviously better. > > (or maybe create_tcl_commands?) > > > > However I think the idea could be extended to variables as > well, how do > > you think, is it possible to leave this name but add > functionality to > > variables also? > > Perhaps? > > $interp->export_to_tcl( > namespace => "perl", > subs => { ... }, > vars => { ... }, > subs_from => $ns, > vars_from => $ns, > ); > > I think you should say it explicitly if you want to export > vars from a namespace as well. You might for instance have > some vars in that namespace that is shared between the perl > subs without the intention to access it from Tcl. > > > And having default namespace to be 'perl' also seems > reasonable to me. > > Fine with me as long as it's possible to pass "" or "::" to > signal the root namespace. > > > Please see below the code I suggest, it is not finished, > but all ideas are expressed. > > > > Opinions welcome. > > > > Best regards, > > Vadim. > > > > > > =comment > > An interpreter method, bind_perl_to_tcl_commands, takes two optional > > arguments - tcl package name (defaults to 'tcl') and perl > package name > > (defaults to 'tcl') > > > > Given a number of Perl sub's in said package, which is passed as the > > second parameter, binds all of them into tcl, in the said > package, which > > is passed as the first parameter to the > bind_perl_to_tcl_commands method. > > > > An example: > > > > use strict; > > use Tcl; > > > > my $int = new Tcl; > > > > $tcl::foo = 'qwerty'; > > $int->create_tcl_commands(subs_from=>'tcl'); > > > > $int->Eval(<<'EOS'); > > > > package require Tk > > > > button .b1 -text {a fluffy button} -command perl::fluffy_sub > > button .b2 -text {a foo button} -command perl::foo > > entry .e -textvariable perl::foo > > pack .b1 .b2 .e > > focus .b2 > > > > tkwait window . > > EOS > > > > sub tcl::fluffy_sub { > > print "Hi, I am a fluffy sub\n"; > > } > > sub tcl::foo { > > print "Hi, I am foo\n"; > > $tcl::foo++; > > } > > =cut > > > > sub create_tcl_commands { > > my $int = shift; > > my %args = @_; > > > > # name of Tcl package to hold tcl commands bound to perl > subroutines > > my $tcl_namespace = $args{namespace} || 'perl'; > > > > # a batch of perl subroutines which tcl counterparts > should be created > > my $subs = $args{subs} || {}; > > > > # name of Perl package, which subroutines would be bound > to tcl commands > > my $subs_from = $args{subs_from}; > > > > if ($subs_from) { > > for my $name (keys %{"$subs_from\::"}) { > > print STDERR "$name;\n"; > > if (defined &{"$subs_from\::$name"}) { > > if (exists $sub->{$name}) { > > next; > > } > > # print STDERR "binding sub '$name'\n"; > > > $int->CreateCommand("$tcl_namespace\::$name",\&{"$subs_from\:: > $name"}); > > Call CreateCommand($tclname, \&sub, undef, undef, 1) to get > avoid getting passed (undef, $int, $name) as the first 3 > arguments to the callback. > > > } > > if (defined ${"$subs_from\::$name"}) { > > # print STDERR "binding var '$name'\n"; > > local $_ = ${"$subs_from\::$name"}; > > tie ${"$subs_from\::$name"}, 'Tcl::Var', $int, > "$tcl_namespace\::$name"; > > ${"$subs_from\::$name"} = $_; > > } > > { > > # array, hash - no need to. > > } > > } > > } > > > > for my $subname (keys %$subs) { > > > $int->CreateCommand("$tcl_namespace\::$subname",$subs{$subname}); > > } > > } > > --Gisle > > > > > > > > >> -----Original Message----- > >> From: Gisle Aas [mailto:gi...@activestate.com] > >> Sent: Tuesday, January 18, 2011 10:08 PM > >> To: Konovalov, Vadim (Vadim)** CTR ** > >> Cc: Jeff Hobbs; tcltk@perl.org > >> Subject: Re: bind some said tcl to perl - all at once > >> > >> I don't like to use the name 'bind_...' for this. To me it > >> sounds like this would bind the namespace together so that > >> subs created after the call also become visible to Tcl and > >> perhaps even that commands created on the Tcl side become > >> visible to Perl. > >> > >> My suggestion would be to make a convenience method like this one: > >> > >> $interp->create_commands( > >> namespace => "perl", > >> subs => { > >> foo => sub { .... }, > >> bar => sub { .... }, > >> } > >> ); > >> > >> where the "namespace" argument is optional. It's the Tcl > >> namespace where the commands will be created. If not provided > >> the names will be registered in the root namespace. This > >> would also always call the underlying $interp->CreateCommand > >> with FLAGS=1 in order to suppress the confusing initial > >> legacy arguments. The keys of the subs hash could also use > >> "::ns::foo" style names in which case the namespace argument > >> is ignored for that particular key. > >> > >> Your use case would then be covered by: > >> > >> $interp->create_commands( > >> namespace => "perl", > >> subs => \%{"tcl\::"}, > >> ); > >> > >> which is a bit ugly to write so you might be able to provide > >> some sugar like: > >> > >> $interp->create_commands( > >> namespace => "perl", > >> subs_from => "tcl", > >> ); > >> > >> if 'subs' and 'subs_from' are provided together they both > >> contribute; with 'subs' taking preference in case there are > >> conflicting names. Alternatively croak on conflicting names. > >> > >> I would also suggest a 'only => \@list_of_names' argument to > >> be able to limit the names to export to Tcl. > >> > >> Regards, > >> Gisle > >> > >> > >> > >> On Jan 18, 2011, at 17:20 , Konovalov, Vadim (Vadim)** CTR > ** wrote: > >> > >>>> From: Jeff Hobbs [mailto:je...@activestate.com] > >>>> On 15/01/2011 1:35 PM, Konovalov, Vadim (Vadim)** CTR ** wrote: > >>>>> I wonder, is it reasonable for this approach to be > >>>> "standartized" and included to, say, Tcl.pm module? > >>>>> > >>>>> For example - all Tcl names from some predefined Tcl > >>>> namespace could be bound to perl subroutines all at once. > >>>> > >>>> I think it would be good to provide this as a standard > convenience > >>>> function, that would tie either a Perl or Tcl namespace to > >> the other > >>>> language. I don't think we'd want to pre-define the > >> namespace (note > >>>> that Tcl.pm already uses ::perl:: on the Tcl side), but > >> let the user > >>>> pick it with a single command invocation. > >>> > >>> Indeed, Tcl.pm uses ::perl:: in Tcl::call. > >>> > >>> All refs in this namespace have names like > >> "SCALAR(0xXXXXXX)" or "CODE(0x######)", and it is very > >> unlikely to have a collision. IOW, the collision could happen > >> if a user have an intention to create a collision, but he/she > >> could just create such a collision anyway, by creating > >> conflicting names using another existing mechanisms. > >>> > >>> Ok, below is my suggestion to include to Tcl.pm, > >>> > >>> > >>> =comment > >>> An interpreter method, bind_perl_to_tcl_commands, takes > two optional > >>> arguments - tcl package name (defaults to 'tcl' and perl > >> package name > >>> (defaults to 'tcl') > >>> > >>> Given a number of Perl sub's in said package, which is > passed as the > >>> second parameter, binds all of them into tcl, in the said > >> package, which > >>> is passed as the first parameter to the > >> bind_perl_to_tcl_commands method. > >>> > >>> An example: > >>> > >>> use Tcl; > >>> > >>> my $int = new Tcl; > >>> > >>> $int->bind_perl_to_tcl_commands; > >>> > >>> $int->Eval(<<'EOS'); > >>> > >>> package require Tk > >>> > >>> button .b1 -text {a fluffy button} -command perl::fluffy_sub > >>> button .b2 -text {a foo button} -command perl::foo > >>> pack .b1 .b2 > >>> > >>> tkwait window . > >>> EOS > >>> > >>> sub tcl::fluffy_sub { > >>> print "Hi, I am a fluffy sub\n"; > >>> } > >>> sub tcl::foo { > >>> print "Hi, I am foo\n"; > >>> } > >>> > >>> =cut > >>> > >>> sub bind_perl_to_tcl_commands { > >>> my $int = shift; > >>> > >>> # name of Tcl package to hold tcl commands bound to perl > >> subroutines > >>> my $tcl_namespace = shift || 'perl'; > >>> > >>> # name of Perl package, which subroutines would be bound > >> to tcl commands > >>> my $perl_namespace = shift || 'tcl'; > >>> > >>> die "Shouldn't bind to main package" > >>> if $perl_namespace eq "" || $perl_namespace eq "main"; > >>> > >>> for my $subname (keys %{"$perl_namespace\::"}) { > >>> # have no need to check if this is a sub name or a var > >> name, as long > >>> # as we're binding to CODE, \&{"..."} > >>> > >> $int->CreateCommand("$tcl_namespace\::$subname",\&{"$perl_name > >> space\::$subname"}); > >>> } > >>> } > >>> > >>> > >>> Similar binding of variables could also be added. > >>> > >>> Best regards, > >>> Vadim. > >> > >> > >
export_to_tcl.t
Description: export_to_tcl.t
p1.pl
Description: p1.pl