I have been programming C for a while. Now I need to write a C program that perl can call.
It should have the same syntax as the following dummy perl function: Take two inputs, both are strings (may contain binary characters, even "\x00"), output a new string.
Of course, algorithm of the function will be more complex, that's why I need to do it in C.
sub dummy {
my ($a, $b) = #_;
return $a . $b;
}
I have briefly looked at SWIG for the implementation, but taking input/ouput other than an integer is not easy, hope someone can give a concrete example.
Thanks in advance.
UPDATE: Got a great example from Rob (author of the Inline::C module in cpan), thanks!
##############################
use warnings;
use strict;
use Devel::Peek;
use Inline C => Config =>
BUILD_NOISY => 1,
;
use Inline C => <<'EOC';
SV * foo(SV * in) {
SV * ret;
STRLEN len;
char *tmp = SvPV(in, len);
ret = newSVpv(tmp, len);
sv_catpvn(ret, tmp, len);
return ret;
}
EOC
my $in = 'hello' . "\x00" . 'world';
my $ret = foo($in);
Dump($in);
print "\n";
Dump ($ret);
##############################
Perl has a glue language called XS for this kind of thing. It knows about mappings between Perl data types and C types. For example, a C function
char *dummy(char *a, int len_a, char *b, int len_b);
could be wrapped by the XS code
MODULE = Foo PACKAGE = Foo
char *
dummy(char *a, int length(a), char *b, int length(b));
The Foo.xs file will be compiled when the module is installed, all relevant build tool chains have support for XS.
Argument conversion code will be generated automatically, so that the function can be called in Perl as Foo::dummy("foo", "bar"), once the XS code has been loaded from Perl:
package Foo;
use parent 'DynaLoader';
Foo->bootstrap;
There is an XS tutorial in the perl documentation, and reference documentation in perlxs.
XS is a good choice for modules, but is awkward for one-off scripts. The Inline::C module allows you to embed the glue C code directly into your Perl script and will take care of automatic compilation whenever the C code changes. However, less code can be generated automatically with this approach.
I am a newbie in perl, I have defined a function
sub ex_arg($_)
{
print "$_[0]\n";
print "$_[1]\n";
print "$_[2]\n";
}
1;
in another page i am calling that page.
require "ex_arg.pl";
ex_arg(1,2,3);
ex_arg(1,,3); #in the second function i want the second parameter to be null
1;
is it possible to do this. am not getting the second parameter instead the third parameter is coming as the second parameter.
Am i doing something wrong. please correct me.
Thanks in advance.
The problem is that your argument list does not retain an empty value for the second argument before it is passed to the subroutine. The list 1,,3 will expand into the same list as 1,3.
You'll need to pass some value that actually exists, such as undef or the empty string.
ex_arg(1, undef, 3);
Then perhaps something like
sub ex_arg {
my ($arg1, $arg2, $arg3) = #_;
if (! defined($arg2)) { # checking if arg2 is empty
# do something
}
}
On a related note, you should not use prototypes in your subroutine declaration unless you know what it does. They are used to create specific behaviour in subroutines, and it has nothing to do with how other languages work, with regard to variables in the subroutine.
sub foo ( ... ) {
#^^^^^^^----- prototypes
sub foo { # this is the standard way to declare
Try this,
ex_arg(1, undef, 3);
ex_arg(1, , 3) for perl is same thing as ex_arg(1, 3)
perl -MO=Deparse -e 'ex_arg(1, , 3);'
ex_arg(1, 3); # <<<<<< deparsed
Side notes; if you don't want to use prototypes then sub ex_arg {..} is what you want.
Prefer my ($x,$y) = #_; over $_[0] and $_[1].
I have a function in C++ such as:
void* getField(interface* p)
{
int* temp = new int( p->intValue);
cout<< "pointer value in c++" << temp << "value of temp = " << temp << endl;
return temp;
}
I am using SWIG to generate wrappers for the above class. Now I am trying to get the returned pointer value in Perl. How do i do it??
I have written the following perl script to call the function:
perl
use module;
$a = module::create_new_interface();
$b = module::getField();
print $b,"\n";
print $$b, "\n";
I ahve correctly defined the create_interface function since on calling the function getField() the correct intValue of the interface gets printed.
The output of the program is as follows:
_p_void=SCALAR(0x5271b0)
5970832
pointer value in c++ 0x5b1b90 value of temp 22
Why are the two values - the pointer in C++ and the reference in perl different? How do i get the intValue from the refernce? (i.e the value 22 )
Because you printed one in decimal and one in hexadecimal:
printf "pointer is 0x%x\n", $$b; # prints "pointer is 0x5b1b90"
Perl doesn't normally use pointers. The pack and unpack functions can deal with pointers (see Packing and Unpacking C Structures), but the normal Perl idiom would be to have the function return an integer instead of a pointer to an integer.
strftime(), as per cpan.org:
print strftime($template, #lt);
I just can't figure the right Perl code recipe for this one. It keeps reporting an error where I call strftime():
...
use Date::Format;
...
sub parse_date {
if ($_[0]) {
$_[0] =~ /(\d{4})/;
my $y = $1;
$_[0] =~ s/\d{4}//;
$_[0] =~ /(\d\d)\D(\d\d)/;
return [$2,$1,$y];
}
return [7,7,2010];
}
foreach my $groupnode ($groupnodes->get_nodelist) {
my $groupname = $xp->find('name/text()', $groupnode);
my $entrynodes = $xp->find('entry', $groupnode);
for my $entrynode ($entrynodes->get_nodelist) {
...
my $date_added = parse_date($xp->find('date_added/text()', $entrynode));
...
$groups{$groupname}{$entryname} = {...,'date_added'=>$date_added,...};
...
}
}
...
my $imday = $maxmonth <= 12 ? 0 : 1;
...
while (my ($groupname, $entries) = each %groups) {
...
while (my ($entryname, $details) = each %$entries) {
...
my $d = #{$details->{'date_added'}};
$writer->dataElement("creation", strftime($date_template, (0,0,12,#$d[0^$imday],#$d[1^$imday]-1,#$d[2],0,0,0)));
}
...
}
...
If I use () to pass the required array by strftime(), I get:
Type of arg 2 to Date::Format::strftime must be array (not list) at ./blah.pl line 87, near "))"
If I use [] to pass the required array, I get:
Type of arg 2 to Date::Format::strftime must be array (not anonymous list ([])) at ./blah.pl line 87, near "])"
How can I pass an array on the fly to a sub in Perl? This can easily be done with PHP, Python, JS, etc. But I just can't figure it with Perl.
EDIT: I reduced the code to these few lines, and I still got the exact same problem:
#!/usr/bin/perl
use warnings;
use strict;
use Date::Format;
my #d = [7,13,2010];
my $imday = 1;
print strftime( q"%Y-%m-%dT12:00:00", (0,0,12,$d[0^$imday],$d[1^$imday]-1,$d[2],0,0,0));
Where an array is required and you have an ad hoc list, you need to actually create an array. It doesn't need to be a separate variable, you can do just:
strftime(
$date_template,
#{ [0,0,12,$d[0^$imday],$d[1^$imday],$d[2],0,0,0] }
);
I have no clue why Date::Format would subject you to this hideousness and not just expect multiple scalar parameters; seems senseless (and contrary to how other modules implement strftime). Graham Barr usually designs better interfaces than this. Maybe it dates from when prototypes still seemed like a cool idea for general purposes.
To use a list as an anonymous array for, say, string interpolation, you could write
print "#{[1, 2, 3]}\n";
to get
1 2 3
The same technique provides a workaround to Date::Format::strftime's funky prototype:
print strftime(q"%Y-%m-%dT12:00:00",
#{[0,0,12,$d[0^$imday],$d[1^$imday]-1,$d[2],0,0,0]});
Output:
1900-24709920-00T12:00:00
Normally, it is easy to pass arrays "on-the-fly" to Perl subroutines. But Date::Format::strftime is a special case with a special prototype ($\#;$) that doesn't allow "list" arguments or "list assignment" arguments:
strftime($format, (0,0,12,13,7-1,2010-1900)); # not ok
strftime($format, #a=(0,0,12,13,7-1,2010-1900)); # not ok
The workaround is that you must call strftime with an array variable.
my #time = (0,0,12,13,7-1,2010-1900); # note: #array = ( ... ), not [ ... ]
strftime($format, #time);
I looked again and I see the real problem in this code:
my $d = #{$details->{'date_added'}};
$writer->dataElement("creation", strftime($date_template, (0,0,12,#$d[0^$imday],#$d[1^$imday]-1,#$d[2],0,0,0)));
Specifically #{$details->{'date_added'}} is a dereference. But you're assigning it to a scalar variable and you don't need to dereference in the line below it:
my #d = #{$details->{'date_added'}};
$writer->dataElement("creation", strftime($date_template, (0,0,12,$d[0^$imday],$d[1^$imday]-1,$d[2],0,0,0)));
I've created a regular array for your reference #d and just accessed it as a regular array ( $d[ ... ] instead of #$d[ ... ] )
I have a struct wich contains:
struct mystruct{
int id[10];
char text[40];
unsigned short int len;
};
And I'm trying to unpack it in a single line, something like this:
my(#ids,$text,$length) = unpack("N10C40n",$buff) ;
But everything is going to the first array(#ids), i've tried templates as "N10 C40 n" and "(N10)(C40)(n)"
So, either this can't be done or I'm not using the proper template string.
Note: I'm using big endian data.
Any hints?
In list assignment the first array or hash will eat everything (how would it know where to stop?). Try this instead:
my #unpacked = unpack "N10Z40n", $buff;
my #ids = #unpacked[0 .. 9];
my ($text, $length) = #unpacked[10, 11];
you could also say
my #ids;
(#ids[0 .. 9], my ($text, $length)) = unpack "N10Z40n", $buff;
If the order of the #ids does not matter:
my ($length, $text, #ids) = reverse unpack("N10C40n",$buff) ;