Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions EXTERN.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,8 @@
*
*/

/*
* EXT: designates a global var which is defined in perl.h
*
* dEXT: designates a global var which is defined in another
* file, so we can't count on finding it in perl.h
* (this practice should be avoided).
*/
/* These are documented in INTERN.h */

#undef EXT
#undef dEXT
#undef EXTCONST
Expand Down
65 changes: 61 additions & 4 deletions INTERN.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,37 @@
*/

/*
* EXT designates a global var which is defined in perl.h
* dEXT designates a global var which is defined in another
* file, so we can't count on finding it in perl.h
* (this practice should be avoided).

=for apidoc CmU||EXT
=for apidoc_item EXTCONST
=for apidoc_item dEXT
=for apidoc_item dEXTCONST

These each designate a global variable. The C<CONST> forms indicate that it is
constant.

Use them like this:

Include either EXTERN.h or INTERN.h, but not both
...
#include "perl.h"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you double check this is correct?
Just looking around I can't immediately see how including perl.h would cause INTERN.h/EXTERN.h to be included..

And looking at some files, av.c for example contains:

#include "EXTERN.h"
#define PERL_IN_AV_C
#include "perl.h"

So it's loading both EXTERN.h and perl.h but if that is the "right" way to do it then I don't see how including onliny perl.h in the example works?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about now?

...
EXT char PL_WARN_ALL INIT(0);
EXTCONST U8 PL_revision INIT(PERL_REVISION);

This will handle everything for you regarding whether they are to actually be
defined and initialized or just declared C<extern>.

If the initialization is complex, you may have to use C<L</DOINIT>>.

If some constants you wish to reference will not become defined by #including
F<perl.h>, instead use C<dEXT> and C<dEXTCONST> for them and include whatever
files you need to to get them. This is currently very rare, and should be
avoided.

=cut
*/

#undef EXT
#undef dEXT
#undef EXTCONST
Expand Down Expand Up @@ -45,7 +71,38 @@
# endif
# endif

/*
=for apidoc Cm||INIT|const_expr

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commit Document things in EXTERN.h, INTERN.h

Wondering if this can/should contain an explanation of when one should use this.
(But maybe there is a better place for it?)


Macro to initialize something, used like so:

EXTCONST char PL_memory_wrap[] INIT("panic: memory wrap");

It expands to nothing in F<EXTERN.h>.

=cut
*/
#undef INIT
#define INIT(...) = __VA_ARGS__

/*
=for apidoc C#||DOINIT

This is defined in F<INTERN.h>, undefined in F<EXTERN.h>

Most of the time you can use C<L</INIT>> to initialize your data structures.
But not always. In such cases, you can do the following:

#ifdef DOINIT
... do the declaration and definition ...
#else
declaration only
#endif

A typical reason for needing this is when the definition includes #ifdef's.
You can't put that portably in a call to C<INIT>, as a macro generally can't
itself contain preprocessor directives.

=cut
*/
#define DOINIT
67 changes: 59 additions & 8 deletions autodoc.pl
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@
my $filters_scn = 'Source Filters';
my $floating_scn = 'Floating point';
my $genconfig_scn = 'General Configuration';
my $global_definitions_scn = 'Declaration and Initialization of Globals';
my $globals_scn = 'Global Variables';
my $GV_scn = 'GV Handling and Stashes';
my $hook_scn = 'Hook manipulation';
Expand Down Expand Up @@ -373,6 +374,49 @@
=back
EOT
},
$global_definitions_scn => {
header => <<~'EOT',
Global variables are defined and initialized in one place, but
referred to from multiple files. They need to be defined and any
initialization done in that one place, but extern declarations
made for them in each file that may refer to them. Note that
there is no harm in declaring a global and not using it.

Perl has a mechanism that allows both purposes to be served while
minimizing code duplication. F<EXTERN.h> and F<INTERN.h> define
the same relatively few macros, but their definitions are
different. In F<EXTERN.h>, the macros expand to
declarations of the globals as external to the file. In
F<INTERN.h> they actually cause the space to be allocated and
possibly initialized.

Most files will follow this paradigm:

#include "EXTERN.h"
...
#include "perl.h">

This causes every global symbol that is referred to in F<perl.h>
and every file it includes (which is nearly every top level Perl
header file) to be declared as external.

The very few files that define globals will instead do

#include "INTERN.h"
...
#include "perl.h">
include the file

It doesn't work for a file to both define some globals and refer
to others as externs. That is, you can only include one of
F<INTERN.h> and F<EXTERN.h>.

This section documents the macros that are defined in these two
header files. F<perl.h> has many uses of them that can serve as
paradigms for you.
EOT
may_be_empty_in_perlapi => 1,
},
$globals_scn => {},
$GV_scn => {},
$hook_scn => {},
Expand Down Expand Up @@ -475,6 +519,7 @@
'gv.c' => $GV_scn,
'gv.h' => $GV_scn,
'hv.h' => $HV_scn,
'INTERN.h' => $global_definitions_scn,
'locale.c' => $locale_scn,
'malloc.c' => $memory_scn,
'numeric.c' => $numeric_scn,
Expand Down Expand Up @@ -572,6 +617,7 @@ sub check_and_add_proto_defn {

$flags .= "m" if $flags =~ /M/;
$flags .= "U" if $flags =~ /@/; # No usage output for @arrays
$flags .= "n" if $flags =~ /#/; # No threads, arguments for #ifdef

my @munged_args= $args_ref->@*;
s/\b(?:NN|NULLOK)\b\s+//g for @munged_args;
Expand Down Expand Up @@ -1858,9 +1904,9 @@ ($fh, $section_name, $element_name, $docref)

my $has_args = $flags !~ /n/;
if (! $has_args) {
warn "$name: n flag without m"
warn "$name: n flag without [m#] "
. where_from_string($item->{file}, $item->{line_num})
unless $flags =~ /m/;
unless $flags =~ /[m#]/;

if ($item->{args} && $item->{args}->@*) {
warn "$name: n flag but apparently has args"
Expand Down Expand Up @@ -2296,10 +2342,14 @@ ($destpod)
for my $section_name (sort dictionary_order keys %valid_sections) {
my $section_info = $dochash->{$section_name};

# We allow empty sections in perlintern.
if (! $section_info && $podname eq $api) {
warn "Empty section '$section_name' for $podname; skipped";
next;
if (! $section_info) {
# We always allow empty sections in perlintern.
if ( $podname eq $api
&& ! $valid_sections{$section_name}{may_be_empty_in_perlapi})
{
warn "Empty section '$section_name' for $podname; skipped";
next;
}
}

print $fh "\n=head1 $section_name\n";
Expand All @@ -2310,7 +2360,8 @@ ($destpod)
delete $section_info->{X_tags};
}

if ($podname eq $api) {
my $has_entries = $section_info && keys $section_info->%*;
if ($has_entries) {
print $fh "\n", $valid_sections{$section_name}{header}, "\n"
if defined $valid_sections{$section_name}{header};

Expand All @@ -2322,7 +2373,7 @@ ($destpod)
}
}

if (! $section_info || ! keys $section_info->%*) {
if (! $has_entries) {
my $pod_type = ($podname eq $api) ? "public" : "internal";
print $fh "\nThere are currently no $pod_type API items in ",
$section_name, "\n";
Expand Down
Loading