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 "LeoNerd&qu= ot; Evans <[email protected]><br> <b>Sent:</b> Monday, June 29, 2026 10:41 PM<br> <b>To:</b> Perl5 Porters <[email protected]><br> <b>Subject:</b> Why do we test a build using g++?</font> <div> </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> build perl using g++ as the "C" compiler. I wonder - why d= o we do this?<br> Perl is written in C, not C++, which is a different language. Can we= <br> 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 "flags" 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> static const struct MagicFunctions magicfuncs_customop_xop =3D {<br> .ver =3D 2,<br> .shape =3D MGv2s_BASE,<br> .debug_name =3D "customop_xop",<br= > .free_mg =3D &customop_xop_free,<br> };<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> op.c:16427:1: warning: missing initializer for member<br> =91MagicFunctions::_v1_vtbl=92 [-Wmissing-fi= eld-initializers]<br> 16427 | };<br> | ^<br> op.c:16427:1: warning: missing initializer for member<br> =91MagicFunctions::flags=92 [-Wmissing-field= -initializers]<br> op.c:16427:1: warning: missing initializer for member<br> =91MagicFunctions::user_size=92 [-Wmissing-f= ield-initializers]<br> op.c:16427:1: warning: missing initializer for member<br> =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> op.c:16432:1: error: designator order for field =91MagicFunctions::f= lags=92<br> 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> static const struct MagicFunctions magicfuncs_customop_xop =3D {<br> ._v1_vtbl =3D {},<br> .ver =3D 2,<br> .shape =3D MGv2s_BASE,<br> .flags =3D 0,<br> .debug_name =3D "customop_xop",<br= > .user_size =3D 0,<br> .free_mg =3D &customop_xop_free,<br> .clone_mg =3D NULL,<br> };<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 "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 ".foo =3D 0" 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 "helloworld.cpp" program that also pulls in <perl.= h> and<br> <XSUB.h> 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 "I'm not a C++<= br> programmer", 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 "LeoNerd" Evans<br> <br> [email protected]<br> <a href=3D"http://www.leonerd.org.uk/">http://www.leonerd.org.uk/</a> = | <a href=3D"https://metacpan.org/author/PEVANS"> https://metacpan.org/author/PEVANS</a><br> </div> </span></font></div> </body> </html> --_000_GV1PR08MB828292F61ADA9BD5F3DB806AACF72GV1PR08MB8282eurp_--