Re: Why do we test a build using g++?

[email protected] ("paul.marquess via perl5-porters") Tue, 30 Jun 2026 08:36:10 +0000
Newsgroups perl.perl5.porters
Message-ID <GV1PR08MB828292F61ADA9BD5F3DB806AACF72@GV1PR08MB8282.eurprd08.prod.outlook.com>
--_000_GV1PR08MB828292F61ADA9BD5F3DB806AACF72GV1PR08MB8282eurp_
Content-Type: text/plain; charset="Windows-1252"
Content-Transfer-Encoding: quoted-printable

My recollection from the early days was it allowed the embedded-Perl use ca=
se to build within a C++ program. Can't remember why just compiling it as C=
, then linking to the enclosing C++ program wasn't good enough. Suspect it =
was to facilitate a workflow that didn't have a C compiler.


Paul
________________________________
From: Paul "LeoNerd" Evans <[email protected]>
Sent: Monday, June 29, 2026 10:41 PM
To: Perl5 Porters <[email protected]>
Subject: Why do we test a build using g++?

TL;DR: We have some CI test builds that use -Dcc=3D'g++', to test if we can
  build perl using g++ as the "C" compiler. I wonder - why do we do this?
  Perl is written in C, not C++, which is a different language. Can we
  please not?


I ask because it's increasingly annoying and getting in the way. Before
we added C99 (and thus were only using C89 features), this wasn't so
bad, but we introduced C99 specifically so we could use some features
that were introduced only 25 years ago, instead of having to stick to
features that are over 35 years old.

One feature of C99 that I intend to use a lot is the ability to
initialise structures with named initialisers, and to be able to omit
fields to imply the compiler-assigned defaults of zero for integers or
NULL for pointers. This makes it nicely convenient to initialise
structures that, for example, have optional "flags" or other features
in them, or pointers that are rarely used, and so most of the time you
leave them as NULL. Not having to write lots of extra lines in the
source makes the code clearer.

The default C99-and-not-C++ compiler is perfectly happy for me to write
code such as:

  static const struct MagicFunctions magicfuncs_customop_xop =3D {
      .ver   =3D 2,
      .shape =3D MGv2s_BASE,
      .debug_name =3D "customop_xop",
      .free_mg =3D &customop_xop_free,
  };

This is relatively easy on the human eye, because it draws attention to
only those fields that we're putting something interesting in, without
a lot of extra clutter. It's almost as nice as having named parameters
with optional defaults in them. ;)

But g++ doesn't like this; it complains:

  op.c:16427:1: warning: missing initializer for member
      =91MagicFunctions::_v1_vtbl=92 [-Wmissing-field-initializers]
  16427 | };
        | ^
  op.c:16427:1: warning: missing initializer for member
      =91MagicFunctions::flags=92 [-Wmissing-field-initializers]
  op.c:16427:1: warning: missing initializer for member
      =91MagicFunctions::user_size=92 [-Wmissing-field-initializers]
  op.c:16427:1: warning: missing initializer for member
      =91MagicFunctions::clone_mg=92 [-Wmissing-field-initializers]

In order to appease it, I tried just adding a bunch of zero/NULL/{}
fields at the end of the struct; but then it complains:

  op.c:16432:1: error: designator order for field =91MagicFunctions::flags=
=92
      does not match declaration order in =91const MagicFunctions=92

It also still gets upset that initialisers that I have in fact written
aren't there, because it doesn't see them. Or something. I don't know,
I'm not a C++ programmer. I write in C.

It seems the only way to make it happy is to write a lot more noisy
structure, that's far worse on the human reader:

  static const struct MagicFunctions magicfuncs_customop_xop =3D {
      ._v1_vtbl =3D {},
      .ver   =3D 2,
      .shape =3D MGv2s_BASE,
      .flags =3D 0,
      .debug_name =3D "customop_xop",
      .user_size =3D 0,
      .free_mg =3D &customop_xop_free,
      .clone_mg =3D NULL,
  };

I find this quite a bit worse to look at, because of all those empty
junk lines being added. It makes it harder to see what's "interesting"
about this struct, as even the zeroes and the NULLs are in there. Plus
that leading ._v1_vtbl is even worse. It's present in the structure
definition itself in order to make a compatible hole with (version 1)
MAGIC structure, but shouldn't really be thought about or visible as
part of the v2 API. So having to add it in *every* struct
initialisation is very annoying indeed.

Aside from being more cluttered and less clear to look at to the human
reader, I also find this worse for longterm maintainability. A key
selling point of C99's ability to imply zeroes/etc.. for fields you
don't name, is that if you later extend the struct definition by adding
more fields to it, you don't have to go around all your initialisers
everywhere to add more ".foo =3D 0" lines. You can just ignore them,
knowing they will default to zeroes if you don't need them. It makes it
easier and nicer to extend those data types later without breaking
existing source code. Plus, an actual C99 compiler doesn't care if the
named initialisers don't appear in the same order as the fields in the
struct. The whole point of naming them is that now order doesn't
matter. You can reorder the elements in the struct without upsetting
users of that struct, even in initialisers. Yes you'll still have to
recompile it - but that's fine for all the code within the source tree.
You just don't have to manually edit all your .c files.


I think therefore I have made a reasonable case for wanting to be able
to use this C99 named initialiser syntax. So now I want to understand
why we care if we can compile perl source using g++.

I understand that historically we have used this as a test to see that
perl's *headers* are suitable for use by both a C compiler and a C++
compiler, for those folks who want to write XS extensions in C++. That
made sense when we were only using C89, and the C++ compilers were
basically able to handle a superset of that anyway. But C99 and C++
diverge a little, so that is no longer really true.

I'm not aware of any direct reason why the source of the perl binary
itself needs to be compilable by a C++ compiler; especially not since
it gets in the way of us writing the code we want to write - in C99.

I'd like to propose that (if there isn't another valid reason that is
stronger than our desire to use C99 features) we find another way to
test if the perl headers are compatible with C++. For example, could we
ship a tiny "helloworld.cpp" program that also pulls in <perl.h> and
<XSUB.h> or somesuch? Or maybe we write a tiny XS extension in C++ and
compiled with g++, to use for such testing purposes?

Or failing all of those; would we be able to test with g++ in some sort
of mode where we can ask it to accept things that C99 considers valid.
I don't know if this one is possible - see above under "I'm not a C++
programmer", but I can't imagine we're the first and only people to
ever run into this issue. Perhaps there's some way we can get around it?

--
Paul "LeoNerd" Evans

[email protected]
http://www.leonerd.org.uk/  |  https://metacpan.org/author/PEVANS

--_000_GV1PR08MB828292F61ADA9BD5F3DB806AACF72GV1PR08MB8282eurp_
Content-Type: text/html; charset="Windows-1252"
Content-Transfer-Encoding: quoted-printable

<html>
<head>
<meta http-equiv=3D"Content-Type" content=3D"text/html; charset=3DWindows-1=
252">
<style type=3D"text/css" style=3D"display:none;"> P {margin-top:0;margin-bo=
ttom:0;} </style>
</head>
<body dir=3D"ltr">
<div class=3D"elementToProof" style=3D"font-family: Aptos, Aptos_EmbeddedFo=
nt, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; c=
olor: rgb(0, 0, 0);">
My recollection from the early days was it allowed the embedded-Perl use ca=
se to build within a C++ program. Can't remember why just compiling it as C=
, then linking to the enclosing C++ program wasn't good enough. Suspect it =
was to facilitate a workflow that
 didn't have a C compiler.</div>
<div class=3D"elementToProof" style=3D"font-family: Aptos, Aptos_EmbeddedFo=
nt, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; c=
olor: rgb(0, 0, 0);">
<br>
</div>
<div class=3D"elementToProof" style=3D"font-family: Aptos, Aptos_EmbeddedFo=
nt, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; c=
olor: rgb(0, 0, 0);">
<br>
</div>
<div class=3D"elementToProof" style=3D"font-family: Aptos, Aptos_EmbeddedFo=
nt, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; c=
olor: rgb(0, 0, 0);">
Paul</div>
<div id=3D"appendonsend"></div>
<hr style=3D"display:inline-block;width:98%" tabindex=3D"-1">
<div id=3D"divRplyFwdMsg" dir=3D"ltr"><font face=3D"Calibri, sans-serif" st=
yle=3D"font-size:11pt" color=3D"#000000"><b>From:</b> Paul &quot;LeoNerd&qu=
ot; Evans &lt;[email protected]&gt;<br>
<b>Sent:</b> Monday, June 29, 2026 10:41 PM<br>
<b>To:</b> Perl5 Porters &lt;[email protected]&gt;<br>
<b>Subject:</b> Why do we test a build using g++?</font>
<div>&nbsp;</div>
</div>
<div class=3D"BodyFragment"><font size=3D"2"><span style=3D"font-size:11pt;=
">
<div class=3D"PlainText">TL;DR: We have some CI test builds that use -Dcc=
=3D'g++', to test if we can<br>
&nbsp; build perl using g++ as the &quot;C&quot; compiler. I wonder - why d=
o we do this?<br>
&nbsp; Perl is written in C, not C++, which is a different language. Can we=
<br>
&nbsp; please not?<br>
<br>
<br>
I ask because it's increasingly annoying and getting in the way. Before<br>
we added C99 (and thus were only using C89 features), this wasn't so<br>
bad, but we introduced C99 specifically so we could use some features<br>
that were introduced only 25 years ago, instead of having to stick to<br>
features that are over 35 years old.<br>
<br>
One feature of C99 that I intend to use a lot is the ability to<br>
initialise structures with named initialisers, and to be able to omit<br>
fields to imply the compiler-assigned defaults of zero for integers or<br>
NULL for pointers. This makes it nicely convenient to initialise<br>
structures that, for example, have optional &quot;flags&quot; or other feat=
ures<br>
in them, or pointers that are rarely used, and so most of the time you<br>
leave them as NULL. Not having to write lots of extra lines in the<br>
source makes the code clearer.<br>
<br>
The default C99-and-not-C++ compiler is perfectly happy for me to write<br>
code such as:<br>
<br>
&nbsp; static const struct MagicFunctions magicfuncs_customop_xop =3D {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .ver&nbsp;&nbsp; =3D 2,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .shape =3D MGv2s_BASE,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .debug_name =3D &quot;customop_xop&quot;,<br=
>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .free_mg =3D &amp;customop_xop_free,<br>
&nbsp; };<br>
<br>
This is relatively easy on the human eye, because it draws attention to<br>
only those fields that we're putting something interesting in, without<br>
a lot of extra clutter. It's almost as nice as having named parameters<br>
with optional defaults in them. ;)<br>
<br>
But g++ doesn't like this; it complains:<br>
<br>
&nbsp; op.c:16427:1: warning: missing initializer for member<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; =91MagicFunctions::_v1_vtbl=92 [-Wmissing-fi=
eld-initializers]<br>
&nbsp; 16427 | };<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | ^<br>
&nbsp; op.c:16427:1: warning: missing initializer for member<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; =91MagicFunctions::flags=92 [-Wmissing-field=
-initializers]<br>
&nbsp; op.c:16427:1: warning: missing initializer for member<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; =91MagicFunctions::user_size=92 [-Wmissing-f=
ield-initializers]<br>
&nbsp; op.c:16427:1: warning: missing initializer for member<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; =91MagicFunctions::clone_mg=92 [-Wmissing-fi=
eld-initializers]<br>
<br>
In order to appease it, I tried just adding a bunch of zero/NULL/{}<br>
fields at the end of the struct; but then it complains:<br>
<br>
&nbsp; op.c:16432:1: error: designator order for field =91MagicFunctions::f=
lags=92<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; does not match declaration order in =91const=
 MagicFunctions=92<br>
<br>
It also still gets upset that initialisers that I have in fact written<br>
aren't there, because it doesn't see them. Or something. I don't know,<br>
I'm not a C++ programmer. I write in C.<br>
<br>
It seems the only way to make it happy is to write a lot more noisy<br>
structure, that's far worse on the human reader:<br>
<br>
&nbsp; static const struct MagicFunctions magicfuncs_customop_xop =3D {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ._v1_vtbl =3D {},<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .ver&nbsp;&nbsp; =3D 2,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .shape =3D MGv2s_BASE,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .flags =3D 0,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .debug_name =3D &quot;customop_xop&quot;,<br=
>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .user_size =3D 0,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .free_mg =3D &amp;customop_xop_free,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .clone_mg =3D NULL,<br>
&nbsp; };<br>
<br>
I find this quite a bit worse to look at, because of all those empty<br>
junk lines being added. It makes it harder to see what's &quot;interesting&=
quot;<br>
about this struct, as even the zeroes and the NULLs are in there. Plus<br>
that leading ._v1_vtbl is even worse. It's present in the structure<br>
definition itself in order to make a compatible hole with (version 1)<br>
MAGIC structure, but shouldn't really be thought about or visible as<br>
part of the v2 API. So having to add it in *every* struct<br>
initialisation is very annoying indeed.<br>
<br>
Aside from being more cluttered and less clear to look at to the human<br>
reader, I also find this worse for longterm maintainability. A key<br>
selling point of C99's ability to imply zeroes/etc.. for fields you<br>
don't name, is that if you later extend the struct definition by adding<br>
more fields to it, you don't have to go around all your initialisers<br>
everywhere to add more &quot;.foo =3D 0&quot; lines. You can just ignore th=
em,<br>
knowing they will default to zeroes if you don't need them. It makes it<br>
easier and nicer to extend those data types later without breaking<br>
existing source code. Plus, an actual C99 compiler doesn't care if the<br>
named initialisers don't appear in the same order as the fields in the<br>
struct. The whole point of naming them is that now order doesn't<br>
matter. You can reorder the elements in the struct without upsetting<br>
users of that struct, even in initialisers. Yes you'll still have to<br>
recompile it - but that's fine for all the code within the source tree.<br>
You just don't have to manually edit all your .c files.<br>
<br>
<br>
I think therefore I have made a reasonable case for wanting to be able<br>
to use this C99 named initialiser syntax. So now I want to understand<br>
why we care if we can compile perl source using g++.<br>
<br>
I understand that historically we have used this as a test to see that<br>
perl's *headers* are suitable for use by both a C compiler and a C++<br>
compiler, for those folks who want to write XS extensions in C++. That<br>
made sense when we were only using C89, and the C++ compilers were<br>
basically able to handle a superset of that anyway. But C99 and C++<br>
diverge a little, so that is no longer really true.<br>
<br>
I'm not aware of any direct reason why the source of the perl binary<br>
itself needs to be compilable by a C++ compiler; especially not since<br>
it gets in the way of us writing the code we want to write - in C99.<br>
<br>
I'd like to propose that (if there isn't another valid reason that is<br>
stronger than our desire to use C99 features) we find another way to<br>
test if the perl headers are compatible with C++. For example, could we<br>
ship a tiny &quot;helloworld.cpp&quot; program that also pulls in &lt;perl.=
h&gt; and<br>
&lt;XSUB.h&gt; or somesuch? Or maybe we write a tiny XS extension in C++ an=
d<br>
compiled with g++, to use for such testing purposes?<br>
<br>
Or failing all of those; would we be able to test with g++ in some sort<br>
of mode where we can ask it to accept things that C99 considers valid.<br>
I don't know if this one is possible - see above under &quot;I'm not a C++<=
br>
programmer&quot;, but I can't imagine we're the first and only people to<br=
>
ever run into this issue. Perhaps there's some way we can get around it?<br=
>
<br>
-- <br>
Paul &quot;LeoNerd&quot; Evans<br>
<br>
[email protected]<br>
<a href=3D"http://www.leonerd.org.uk/">http://www.leonerd.org.uk/</a>&nbsp;=
 |&nbsp; <a href=3D"https://metacpan.org/author/PEVANS">
https://metacpan.org/author/PEVANS</a><br>
</div>
</span></font></div>
</body>
</html>

--_000_GV1PR08MB828292F61ADA9BD5F3DB806AACF72GV1PR08MB8282eurp_--