Hello. I know of two modules which are capable of doing this. One is C::DynaLoad::Struct and the other is C::Include. You could probably do it using C::Inline as well. C::Include is perl based, but requires Storable.pm for caching (I find this kinda odd). It however, is your best bet. If you wish to do this without installing modules ( a silly requirement), you can probably rip off C::Include real easy (I would at least strip the Storable caching stuff). All of these have a lot of documentation. Knowledge of c is required. I tried out C::Include, and all the samples included work. Good luck (btw - I am looking into the future, and I see a tutorial on one of these, or at least a meditation on how you dealt with it - and code, always code ;D)
update: #!/usr/bin/perl -w
use C::Include;
use strict;
use vars qw/$inc $send $buffer/;
$inc = new C::Include( \*DATA );
# Make struct instance
$send = $inc->make_struct('send');
use Data::Dumper;
print Dumper $send;
# Fill struct fields
$$send{prevpass} = '0123456789';
$$send{answer} = 1;
# Pack struct to buffer
$buffer = $send->pack();
# Struct size
printf "Size of struct send: %d bytes\n", $send->size;
printf "Size of unsigned long: %d bytes\n", $inc->sizeof('unsigned lon
+g');
# Print buffer to STDOUT
&hview( \$buffer );
#require 'hview.pl';
## crazyinsomniac's note ~ hview.pl is distributed with the module, pr
+ops to the author
sub hview(\$){
my $str = shift;
my @lines = unpack 'a16'x(length($$str)/16+(length($$str)%16?1:0))
+, $$str;
for( 0..$#lines ){
printf "%08X: %-17sł %-17s %-16s\n", $_*16,
(map{
sprintf '%-2s ' x 8, map{
sprintf'%02X',$_
}unpack 'C*', $_
}unpack 'a8a8', $lines[$_]),
$lines[$_];
}
}
1;
__END__
#define PORT 2345
#define CHALLENGE 0
#define CORRECT 1
#define WRONG 2
struct recv {
int flag;
int b[4];
char nextpass[10];
};
typedef struct recv t_recv;
struct send {
int answer;
char prevpass[10];
};
typedef struct send t_send;
I tried the above, and got$>perl test4.pl
$VAR1 = bless( {
'' => {
'length' => 14,
'mask' => 'iZ10',
'tag' => 'send',
'buffers' => [
\undef,
\undef
]
},
'prevpass' => ${$VAR1->{''}{'buffers'}[1]},
'answer' => ${$VAR1->{''}{'buffers'}[0]}
}, 'C::Include::Struct' );
Size of struct send: 14 bytes
Size of unsigned long: 4 bytes
00000000: 01 00 00 00 30 31 32 33 ¦ 34 35 36 37 38 00 012
+345678
$>
That seems pretty cool.
** Note: I am using C::Include $VERSION = 1.40;
I tried $VERSION = 1.20;, but It didn't like your macro (WARNING: Unashed code ...) so I upgraded ... and all this after plenty of rum and coke (and some Jose Quervo tequila to boot)
| [reply] [d/l] [select] |
Another solution is to use pack and unpack to take the C structure and convert it back-and-forth into an appropriate Perl structure. A small example of this can be found on the current sample page for my book.
There may also be an example in the proposed perlpacktut that's been floating around the perl 5 porters mailing list.
| [reply] |
This is a perfect application for pack and unpack. It looks to me like your two transformation statements should be something like so:
my ($flag, $b1, $b2, $b3, $b4, $nextpass) =
unpack ( "iiiiiZ*", $received_binary_struct );
my $binary_struct_to_send =
pack ( "iZ*", $answer, $prevpass );
I wrote a little test-case using Inline::C to make sure that it looks like that to gcc, too <laugh>...
#!/usr/bin/perl -w
use Inline C;
use strict; $|++;
my $recv = test_recv_struct();
print "recv: " . join ( ",", unpack ( "iiiiiZ*", $recv ) ) . "\n";
my $send = pack ( "iZ*", 1, "string two" );
test_send_struct ( $send );
__END__
__C__
struct recv {
int flag;
int b[4];
char nextpass[10];
};
typedef struct recv t_recv;
struct send {
int answer;
char prevpass[10];
};
typedef struct send t_send;
//-----
SV* test_recv_struct() {
t_recv* foo = malloc ( sizeof(t_recv) );
foo->flag = 1;
foo->b[0] = 2;
foo->b[1] = 3;
foo->b[2] = 4;
foo->b[3] = 5;
sprintf( foo->nextpass, "a string" );
return newSVpv ( (char*)foo, sizeof(t_recv) );
}
void test_send_struct ( char* perl_packed ) {
t_send* foo = perl_packed;
printf ( "send: %d,%s\n", foo->answer, foo->prevpass );
}
Inline is really nifty!
Kwin | [reply] [d/l] [select] |
| [reply] |
I'm not sure Inline::Struct does do the right thing, here. Inline::Struct provides a simple way to manipulate a a C struct as a Perl object (which is very often exactly what one needs to do in the context of writing a bunch of Inline::C code). But AFAICT there are no documented methods for reading in a C struct from a blob of bytes or for dumping one out.
That's exactly what pack and unpack are for.
Having said that, it's probably worth writing to the Inline mailing list and suggesting such an addition to the API.
Kwin
| [reply] |