[XS] .h constants -> Perl subs (like POSIX' "errno_h")?

I

Ivan Shmakov

Is there an easy way to make a bunch of C constants (as in:
#define) available as Perl subroutines (just like, e. g., POSIX
does for the errno.h constants)?

I've looked at POSIX.xs, POSIX.pm [1, 2], but don't seem to get
it. (Somehow, there do not seem to be any references to, say,
ENOENT, other than that in the export list.)

Surely, I can follow the example at SQLite.xs [3]:

static int
OK()
CODE:
RETVAL = SQLITE_OK;
OUTPUT:
RETVAL

But that seems overly repetitive and error-prone. (Given that I
need to handle some 156 such constants, anyway.)

TIA.

[1] http://cpansearch.perl.org/src/RJBS/perl-5.18.0/ext/POSIX/POSIX.xs
[2] http://cpansearch.perl.org/src/RJBS/perl-5.18.0/ext/POSIX/lib/POSIX.pm
[3] http://cpansearch.perl.org/src/ISHIGAKI/DBD-SQLite-1.39/SQLite.xs
 
P

Peter Makholm

Ivan Shmakov said:
Is there an easy way to make a bunch of C constants (as in:
#define) available as Perl subroutines (just like, e. g., POSIX
does for the errno.h constants)?

In the past I have used the h2ph tools for this. It requires some post
processing to make it look like a modern perl module with sane exports.

//Makholm
 
R

Rainer Weikusat

Ivan Shmakov said:
Is there an easy way to make a bunch of C constants (as in:
#define) available as Perl subroutines (just like, e. g., POSIX
does for the errno.h constants)?

In case of a new module, this is part of what h2xs does. I'm also
using C::Scan::Constants when more 'dynamic'(ically changing) headers
are involved.
 
I

Ivan Shmakov

In case of a new module, this is part of what h2xs does.

It seems to do more than that: it prepares a complete template
for a new module. Which is certainly nice and worth looking at,
but I already have started working on that module, thus it isn't
all that helpful.

Besides, the data gathered by h2xs(1) ends up in the respective
WriteConstants () invocation in Makefile.PL. Which then does
the thing, as I was able to figure out.
I'm also using C::Scan::Constants when more 'dynamic'(ically
changing) headers are involved.

ACK, thanks for the pointer.

(To note is that the C::Scan::Constants documentation also
points to WriteConstants ().)
 
R

Rainer Weikusat

Ivan Shmakov said:
[...]
I'm also using C::Scan::Constants when more 'dynamic'(ically
changing) headers are involved.

ACK, thanks for the pointer.

(To note is that the C::Scan::Constants documentation also
points to WriteConstants ().)

The more interesting part is that it provides a reasonably working
(the version I'm using required manual fixups because it can't cope
with #defines which don't include an expansion, eg, guard macros)
'easy to use' way to extract 'constant information' from a bunch of C
header files. Eg, I have a Perl (extension) module capable of parsing
a (set of) racoon config file(s) based on a shared library containing
the actual racoon parser code (based on a thorough reorganization of
the racoon source code) and I'm using a script looking like this:

---------------
use C::Scan::Constants;

my @hdrs = qw(/usr/local/include/racoon/ipsecdoi_id.h
/usr/local/include/racoon/ipsec_doi.h
/usr/local/include/racoon/handler.h
/usr/local/include/racoon/isakmp.h
/usr/local/include/racoon/oakley.h
/usr/local/include/racoon/remoteconf.h);
my @cs = extract_constants_from(@hdrs);
write_constants_module('MECS::Racoonconf', @cs);
 
C

Charles DeRykus

Is there an easy way to make a bunch of C constants (as in:
#define) available as Perl subroutines (just like, e. g., POSIX
does for the errno.h constants)?

I've looked at POSIX.xs, POSIX.pm [1, 2], but don't seem to get
it. (Somehow, there do not seem to be any references to, say,
ENOENT, other than that in the export list.)

Surely, I can follow the example at SQLite.xs [3]:

static int
OK()
CODE:
RETVAL = SQLITE_OK;
OUTPUT:
RETVAL

But that seems overly repetitive and error-prone. (Given that I
need to handle some 156 such constants, anyway.)

TIA.

[1] http://cpansearch.perl.org/src/RJBS/perl-5.18.0/ext/POSIX/POSIX.xs
[2] http://cpansearch.perl.org/src/RJBS/perl-5.18.0/ext/POSIX/lib/POSIX.pm
[3] http://cpansearch.perl.org/src/ISHIGAKI/DBD-SQLite-1.39/SQLite.xs

Could you just transform 'em to perl constants, eg,

BEGIN {
my %const = map {chomp; (split)[1,2]; }
qx{ grep '^#define' /path/to/*.h };
my $code;
while ( my($k,$v) = each %const ) {
$code .= "$k => $v,";
}
eval "use constant {$code}";
die $@ if $@;
}
 
I

Ivan Shmakov

Charles DeRykus said:
Is there an easy way to make a bunch of C constants (as in: #define)
available as Perl subroutines (just like, e. g., POSIX does for the
errno.h constants)?
[...]

Could you just transform 'em to perl constants, eg,
BEGIN {
my %const = map {chomp; (split)[1,2]; }
qx{ grep '^#define' /path/to/*.h };

Surely I could. The question is: how do I keep my code in sync
with the library's it duplicates?

[...]
 
R

Rainer Weikusat

Charles DeRykus said:
Is there an easy way to make a bunch of C constants (as in:
#define) available as Perl subroutines (just like, e. g., POSIX
does for the errno.h constants)?
[...]

Could you just transform 'em to perl constants, eg,

BEGIN {
my %const = map {chomp; (split)[1,2]; }
qx{ grep '^#define' /path/to/*.h };
my $code;
while ( my($k,$v) = each %const ) {
$code .= "$k => $v,";
}
eval "use constant {$code}";
die $@ if $@;
}

That's broken. Some people indent nested preprocessor directives, eg

#ifdef BLAH
# define OOPS "yodel"
#else
# define OOPS "---"
#endif

Macro expansions may be multiline, eg

#define OTHERTINGER \
"dock"\
"wok"\
"clock"\
"cat"\
"bat"\
"flat"\
"spherical"\
"dot"

There might not by any expansions and macros can have arguments (this
is likely not an exhausive list). Also, numerical constants can be
defined via enum which makes them visible to 'other tools' (like
debuggers) and prevents 'unexpected substitutions' as in

#include <stdio.h>

#define oo ar

int main(void)
{
int oo = 5;
printf("%d\n", ar);
return 0;
}

You also don't need the eval, constant.pm also works with anonymous
hashes

perl -e 'use constant { A => 2, B => 3 }; print A + B, "\n";'
 
R

Rainer Weikusat

Rainer Weikusat said:
[...]
BEGIN {
my %const = map {chomp; (split)[1,2]; }
qx{ grep '^#define' /path/to/*.h };
my $code;
while ( my($k,$v) = each %const ) {
$code .= "$k => $v,";
}
eval "use constant {$code}";
die $@ if $@;
}
[...]

You also don't need the eval, constant.pm also works with anonymous
hashes

This statement doesn't exactly make any sense :). But the eval isn't
needed, one way to accomplish the same would be

----------------
BEGIN {
my %c = ( A => 1, B => 2 );

use constant;
constant->import(\%c);
}

print A,B,"\n";
 
R

Rainer Weikusat

Ben Morrow said:
Quoth Charles DeRykus said:
Is there an easy way to make a bunch of C constants (as in:
#define) available as Perl subroutines (just like, e. g., POSIX
does for the errno.h constants)?

Could you just transform 'em to perl constants, eg,

BEGIN {
my %const = map {chomp; (split)[1,2]; }
qx{ grep '^#define' /path/to/*.h };
my $code;
while ( my($k,$v) = each %const ) {
$code .= "$k => $v,";
}
eval "use constant {$code}";
die $@ if $@;
}

That's what h2ph does. There's a reason it was replaced with h2xs, and
that is that it's usually better to get the C compiler to interpret C,
since include files can contain all sorts of rubbish.

h2xs doesn't use external programs for analysing C headers, it
just contains a more complete 'C header analyzer' than the one in the
example above (as does h2ph, FWIW).
 
C

Charles DeRykus

Quoth Charles DeRykus said:
Is there an easy way to make a bunch of C constants (as in:
#define) available as Perl subroutines (just like, e. g., POSIX
does for the errno.h constants)?

Could you just transform 'em to perl constants, eg,

BEGIN {
my %const = map {chomp; (split)[1,2]; }
qx{ grep '^#define' /path/to/*.h };
my $code;
while ( my($k,$v) = each %const ) {
$code .= "$k => $v,";
}
eval "use constant {$code}";
die $@ if $@;
}

That's what h2ph does. There's a reason it was replaced with h2xs, and
that is that it's usually better to get the C compiler to interpret C,
since include files can contain all sorts of rubbish.

For instance, your code above will fail on both these common cases:

I knew (and have experienced) h2ph's flaws. Somehow, I was thinking
they were well-behaved, home-grown header files so "I came, I saw,
I re-invented" h2ph.
 
C

Charles DeRykus

Rainer Weikusat said:
[...]
BEGIN {
my %const = map {chomp; (split)[1,2]; }
qx{ grep '^#define' /path/to/*.h };
my $code;
while ( my($k,$v) = each %const ) {
$code .= "$k => $v,";
}
eval "use constant {$code}";
die $@ if $@;
}
[...]

You also don't need the eval, constant.pm also works with anonymous
hashes

This statement doesn't exactly make any sense :). But the eval isn't
needed, one way to accomplish the same would be

----------------
BEGIN {
my %c = ( A => 1, B => 2 );

use constant;
constant->import(\%c);
}

print A,B,"\n";

Thanks, I had forgotten that too...
 
I

Ivan Shmakov

Rainer Weikusat said:
[...]
There's a reason [h2ph] was replaced with h2xs, and that is that
it's usually better to get the C compiler to interpret C, since
include files can contain all sorts of rubbish.
h2xs doesn't use external programs for analysing C headers, it just
contains a more complete 'C header analyzer' than the one in the
example above (as does h2ph, FWIW).

I guess the point was that h2xs (or, rather, WriteConstants ())
uses the C compiler to get the /values/ of such constants.

And as for the names, -- indeed.
 
C

Charles DeRykus

...

You can even calculate the contents of the hashref dynamically, if you
like:

use constant {
map (chr(ord("@") + $_), $_), 1..4,
};

This expression is evaluated at compile time, so any subs called need to
be available at the point where the 'use' statement is compiled.

Hm, appears not:

$ perl -le 'use constant {map( (chr(ord("@") + $_), $_), 1..4 ) }'
syntax error at -e line 1, near "use constant {


Interestingly though:

$ perl -le 'use constant map( (chr(ord("@") + $_), $_), 1..4 );
print A()'
1B2C3D4

This works:

$ perl -le 'BEGIN{%h = map ( (chr(ord("@") + $_), $_), 1..4 ) };use
constant \%h;print A()'
1

This of course too:

$ perl -le 'use constant; constant->import( {map ( (chr(ord("@") +
$_), $_), 1..4)}); print A()'
1
 
P

Peter J. Holzer

Hm, appears not:

$ perl -le 'use constant {map( (chr(ord("@") + $_), $_), 1..4 ) }'
syntax error at -e line 1, near "use constant {

Perl thinks that the { starts a block here, not an anonymous hash. You
can force it by prepending a +:

% perl -le 'use constant +{map( (chr(ord("@") + $_), $_), 1..4 ) }; print A, B, C, D'
1234

hp
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,999
Messages
2,570,246
Members
46,840
Latest member
BrendanG78

Latest Threads

Top