Hi all, FWIW I've updated the Tcl.pm repository with proposed additions, so feel free to comment on it.
I find a way when some GUI is done with pure-tcl and then using either of tcl/tk perl modules is extremely powerful and convenient. (clean GUI, logic separation, etc) so, IMO using this $int->export_tcl_namespace(); is a good way to go. Regards, Vadim. On Sun, 2011-01-23 at 13:09 +0100, Konovalov, Vadim (Vadim)** CTR ** wrote: > 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. > > >> > > >> > > > >