Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

Using C-style .h as constants

by gri6507 (Deacon)
on Oct 26, 2011 at 16:30 UTC ( [id://933931]=perlquestion: print w/replies, xml ) Need Help??

gri6507 has asked for the wisdom of the Perl Monks concerning the following question:

Hello,

I have a C header file with a number of enumerations defined (i.e. typdef enum {a=1, b, c}). I would like to reuse the same enumerations in my Perl script. I don't want to copy and paste the enums and define them as constants one at a time. Is there a better way? Perhaps a more automated way?

Thanks as always!

P.S. In addition to the enumerations, the header file has a number of other C-style header file things (i.e. preprocessor directives, function prototypes, etc). Hopefully, which ever way is recommended for pulling out the enums would safely ignore these.

Replies are listed 'Best First'.
Re: Using C-style .h as constants
by davido (Cardinal) on Oct 26, 2011 at 19:15 UTC

    Perl's declared constants (using "use constant") are implemented as subroutines. Although it is entirely possible to do what you're asking (and I'll show you how in a minute) generating Perl constants (subroutines) means that you run the risk of your enums clashing with subs already in the program as you pull every enum in from some header file. It might be less offensive to pull all of the enum members into a single hash, and make the hash itself immutable. I'll show you the hash technique first.

    There is a really useful module on CPAN called Convert::Binary::C. Its name doesn't seem to really convey the breadth of its utility. Combine that with Hash::Util's lock_hash() function, and you can have a constant hash built out of your enumerations.

    Here's an example of how that could work:

    A sample header file:

    /* mytest.h */ enum { a = 10, b, c, d, efg, hijk }; /* unnamed */ enum Temperature { temp_low, temp_medium, temp_high }; enum Othertest { frank, john, kurt, jason };

    A Perl program to parse that file

    #!/usr/bin/env perl use strict; use warnings; use v5.12; use Convert::Binary::C; use Hash::Util qw/lock_hash/; my %C_ENUMS; { my $c = Convert::Binary::C->new->parse_file( 'mytest.h' ); %C_ENUMS = map %{ $_->{enumerators} || {} }, $c->enum; lock_hash( %C_ENUMS ); } say "'efg' is $C_ENUMS{efg}"; say "'kurt' is $C_ENUMS{kurt}"; eval{ $C_ENUMS{kurt} = 15; }; print $@ if $@; say "'kurt' is $C_ENUMS{kurt}";

    The output:

    'efg' is 14 'kurt' is 2 Modification of a read-only value attempted at mytest.pl line 22. 'kurt' is 2

    This is a pretty simple example where all enum fields are just dumped into a single hash. If you need to keep track of the enum names, for example, Temperature and Othertest the Convert::Binary::C module has methods for that too, and you could build a hash of multiple dimensions that way.

    Also, by going with the hash approach your "constants" can still interpolate easily into quoted strings.

    Update:

    As Perl use constants:

    I mentioned before I would show how to turn your enums into Perl constants. If you really want to risk subroutine name collisions you can do exactly what you're asking for by using the same Convert::Binary::C module like this:

    use strict; use warnings; use v5.12; BEGIN{ use Convert::Binary::C; my $c = Convert::Binary::C->new->parse_file( 'mytest.h' ); my %c_enums = map %{ $_->{enumerators} || {} }, $c->enum; eval "use constant $_ => $c_enums{$_};" for keys %c_enums; } say "efg => ", efg; say "kurt => ", kurt;

    The output will be:

    efg => 14 kurt => 2

    Fun stuff!


    Dave

      Perfect! Thank you. That's exactly what I was looking for.
Re: Using C-style .h as constants
by Marshall (Canon) on Oct 26, 2011 at 19:25 UTC
    Interesting idea. I have certainly written Perl code that generates .h files that a C/C++ program uses. I see no reason why it couldn't go the other direction?

    So one way would be to modify the "make file" for your C program so that if the .h file has changed, it creates a new Perl .pm module file (that your main Perl program "uses"). You do this by writing a Perl program that reads the .h file and writes Perl code!

    I personally would be thinking in the direction of a module like this rather than just another .pl file that is executed at the start of your Perl's main .pl file. You can make it very clear what is "derived from the .h file" and that would be the purpose of this Perl module - defining constants that the C program knows about.

    Hope this makes sense to you.

    Write a Perl program that is one of the "make targets" in the make file. That Perl program writes a Cinterface.pm module file. That file is "used" by the PerlA.pl file when it runs. If you redefine #define MAX_FILES = 20 in the .h file, then building the C program causes the Perl program to also understand that: use constant MAX_FILES => 20; and that change goes into a new version of the .pm file.

    Of course instead of just running your PerlA.pl program, you need to run the "make" file of the C program first. The Perl program depends upon the .h constants of the C program. The syntax of this enum stuff can all be taken care of. Perhaps the "OH!" moment for you is that you need to write a Perl program that writes Perl code! And that Perl program is a target in the "make" file. If the .h file changes, then the Perl Cinterface.pm module (program generated) changes and therefore the .pl program that runs has been modified.

Re: Using C-style .h as constants
by johngg (Canon) on Oct 26, 2011 at 18:29 UTC

    You might want to look at the h2ph script that ships with Perl.

    Cheers,

    JohnGG

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://933931]
Approved by davido
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others having an uproarious good time at the Monastery: (6)
As of 2024-04-16 08:54 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found