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:[email protected]]
> > Sent: Thursday, January 20, 2011 12:07 AM
> > To: Konovalov, Vadim (Vadim)** CTR **
> > Cc: Jeff Hobbs; [email protected]
> > 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:[email protected]]
> > >> Sent: Tuesday, January 18, 2011 10:08 PM
> > >> To: Konovalov, Vadim (Vadim)** CTR **
> > >> Cc: Jeff Hobbs; [email protected]
> > >> 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:[email protected]]
> > >>>> 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.
> > >>
> > >>
> >
> >