Re: C23 thoughts and opinions

Liste des GroupesRevenir à cl c  
Sujet : Re: C23 thoughts and opinions
De : Keith.S.Thompson+u (at) *nospam* gmail.com (Keith Thompson)
Groupes : comp.lang.c
Date : 27. May 2024, 01:17:44
Autres entêtes
Organisation : None to speak of
Message-ID : <87plt8yxgn.fsf@nosuchdomain.example.com>
References : 1 2 3 4 5 6 7 8 9 10 11 12
User-Agent : Gnus/5.13 (Gnus v5.13) Emacs/27.2 (gnu/linux)
David Brown <david.brown@hesbynett.no> writes:
On 26/05/2024 00:58, Keith Thompson wrote:
David Brown <david.brown@hesbynett.no> writes:
On 25/05/2024 03:29, Keith Thompson wrote:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
David Brown <david.brown@hesbynett.no> writes:
On 23/05/2024 14:11, bart wrote:
[...]
'embed' was discussed a few months ago. I disagreed with the poor
way it was to be implemented: 'embed' notionally generates a list of
comma-separated numbers as tokens, where you have to take care of
any trailing zero yourself if needed. It would also be hopelessly
inefficient if actually implemented like that.
>
Fortunately, it is /not/ actually implemented like that - it is only
implemented "as if" it were like that.  Real prototype implementations
(for gcc and clang - I don't know about other tools) are extremely
efficient at handling #embed.  And the comma-separated numbers can be
more flexible in less common use-cases.
[...]
>
I'm aware of a proposed implementation for clang:
>
https://github.com/llvm/llvm-project/pull/68620
https://github.com/ThePhD/llvm-project
>
I'm currently cloning the git repo, with the aim of building it so I can
try it out and test some corner cases.  It will take a while.
>
I'm not aware of any prototype implementation for gcc.  If you are, I'd
be very interested in trying it out.
>
(And thanks for starting this thread!)
I've built this from source, and it mostly works.  I haven't seen it
do
any optimization; the `#embed` directive expands to a sequence of
comma-separated integer constants.
Which means that this:
#include <stdio.h>
int main(void) {
      struct foo {
          unsigned char a;
          unsigned short b;
          unsigned int c;
          double d;
      };
      struct foo obj = {
#embed "foo.dat"
      };
      printf("a=%d b=%d c=%d d=%f\n", obj.a, obj.b, obj.c, obj.d);
}
given "foo.dat" containing bytes with values 1, 2, 3, and 4,
produces
this output:
a=1 b=2 c=3 d=4.000000
>
That is what you would expect by the way #embed is specified.  You
would not expect to see any "optimisation", since optimisations should
not change the results (apparent from choosing between alternative
valid results).
>
Where you will see the optimisation difference is between :
>
const int xs[] = {
#embed "x.dat"
};
>
and
>
const int xs[] = {
#include "x.csv"
};
>
>
where "x.dat" is a large binary file, and "x.csv" is the same data as
comma-separated values.  The #embed version will compile very much
faster, using far less memory.  /That/ is the optimisation.
Why would it compile faster?  #embed expands to something similar to
CSV, which still has to be parsed.
>
No, it does /not/.  That's the /whole/ point of #embed, and the main
motivation for its existence.  People have always managed to embed
binary source files into their binary output files - using linker
tricks, or using xxd or other tools (common or specialised) to turn
binary files into initialisers for constant arrays (or structs).  I've
done so myself on many projects, all integrated together in makefiles.
>
#embed has two purposes.  One is to save you from using external tools
 for that kind of thing.  The other is to do it more efficiently for
big files.
>
There are two ways this is done for examples like this.  One is that
is that the compiler does /not/ turn each byte into a series of ASCII
digits for the number, then parse that number to get back to a byte.
It jumps straight from byte in to byte out, possibly after expanding
to a bigger type size if necessary.  Secondly, compilers typically
track lots more information about each initialiser - such as the file,
line and column number so that it can give you helpful messages if
there is a value out of range, or too many or too few initialisers.
With #embed, the compiler doesn't have to do any of that.
>
The compiler will generate results /as if/ it had expanded the file to
a list of numbers and parsed them.  But it will not do that in
practice. (At least, not for more serious implementations - simple
solutions might do so to get support implemented quickly.)

I'll start by acknowledging that the prototype information apparently
*does* optimize #embed when it can.  I was mistaken on that point.

#embed *must* expand to the standard-defined comma-delimited sequence in
*some* cases.

Which means that the piece of the compiler that implements #embed has to
recognize when it must generate that sequence, and when it can do
something more efficient.

Reference:
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3220.pdf>
6.10.4.
The first one will probably initialize each int element of xs to a
single byte value extracted from x.dat.  Is that what you intended?
>
Yes, if that's what the programmer wrote - though I agree that
character types will be more common and will be the prime target for
optimisation.
>
#embed works best with arrays of unsigned char.
>
Sure, that will be a very common use.
>
If you mean that the #embed will expand to something other than the
sequence of integer constants, how does it know to do that in this
context?
>
It knows because the compiler writers are actually quite smart.  The C
standards may describe the translation process in a series of distinct
and independent phases, but that's not how it is done in practice.
The key point is that the compiler knows how the sequence of integers
is going to be used before it gets that far in the preprocessing.
>
I'd expect implementations to have extremely fast implementations for
initialising arrays of character types, and probably also for other
arrays of scaler types.  More complicated examples - such as
parameters in a macro or function call - would probably use a
fall-back of generating naïve lists of integer constants.

My problem is not just with how the compiler can figure out when it can
optimize, but how programmers are supposed to understand whatever rules
it uses.  Can I rely on the optimization being performed if I use a
typedef for unsigned char, or if I use an enumeration type whose
underlying type is unsigned char, or if I have initialization elements
befor and after the #embed directive?

Effective use of #embed requires too much "magic" for my taste --
particularly having the preprocessor rely on information from later
phases.  The semantics of #embed don't rely on that information, but
efficient use for large files does.

If you have a binary file containing a sequence of int values, you
can
use #embed to initialize an unsigned char array that's aliased with or
copied to the int array.
The *embed element width* is typically going to be CHAR_BIT bits by
default.  It can only be changed by an *implementation-defined* embed
parameter.  It seems odd that there's no standard way to specify the
element width.
It seems even more odd that the embed element width is
implementation defined and not set to CHAR_BIT by default.
>
I agree.  But it may be left flexible for situations where the host
and target have different ideas about CHAR_BIT.  (Targets with
CHAR_BIT other than 8 are very rare, hosts with CHAR_BIT other than 8
are non-existent, but C remains flexible.)

I would think that you'd want the element width to match CHAR_BIT *on
the target* (which is the only CHAR_BIT that's relevant or available).
If you're cross-compiling, you'd probably want to embed a file that
could have been used on the target system.

And if I'm not doing that kind of exotic cross-compiling, I can't rely
on the element width being CHAR_BIT *or* on any standard way to specify
that I want it to be CHAR_BIT.

Requiring the default width to be CHAR_BIT would, I'm guessing, solve
99% of cases.  Allowing it to be specified by a parameter would solve
the remaing 1%.  And I expect it *will* be CHAR_BIT in most or all
implementations, and programmers will rely on that assumption.  I think
the standard should guarantee that.

A conforming implementation could set the embed element width to,
say, 4*CHAR_BIT and then not provide an implementation-defined embed
parameter to specify a different width, making #embed unusable for
unsigned char arrays.  (N3220 is a draft, not the final C23 standard,
but I haven't heard about any changes in this area.)
The kind of optimization I was thinking about was having #embed, in
some
cases, expand to something other than the specified sequence of
comma-separated integer constants.  Such an optimization would be
intended to improve compile-time speed and memory usage, not run-time
performance.
With a straightforward implementation, the preprocessor has to
generate
a sequence of integer constants as text, and then later compiler phases
have to parse that text sequence and generate the corresponding code.
Given:
     const unsigned char data[4] = {
     #embed "four_bytes.dat"
     }
     That 4 byte data file is translated to something like "1, 2, 3,
4", then
converted into a stream of tokens, then those tokens are parsed, then,
given the context, the original 4-byte sequence is written into the
generated object file.
For a very large file, that could be a significant burden.  (I don't
have any numbers on that.)
>
I do :
>
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3017.htm#design-efficiency-metrics>
>
(That's from a proposal for #embed for C and C++.  Generating the
numbers and parsing them is akin to using xxd.)
>
More useful links:
>
<https://thephd.dev/embed-the-details#results>
<https://thephd.dev/implementing-embed-c-and-c++>
>
(These are from someone who did a lot of the work for the proposals,
and prototype implementations, as far as I understand it.)

That second link does have a lot of good information.  I think I had
seen it before, but I hadn't read it thoroughly.  It refers to prototype
implementations for both gcc and clang.  I've built the prototype on my
system, and godbolt.org has it, but the gcc prototype (for which the
article provides good performance data) doesn't seem to be available
anywhere.

My experiments with the clang prototype have been a bit confusing.  I
assumed that `clang -E` would give me meaningful results, but it always
produces the comma-delimited sequence of integer constants, and even
that output is inconsistent.  It looks like "-E" synthesizes naive and
not entirely correct output.  Feeding that output to clang produces
warnings that I don't get without "-E".  Some of this might be the
result of user error on my part.

I did some tests with 100MB file, both with #embed and with #include
using the output of "xxd".  #embed *is* much faster.

According to <https://thephd.dev/implementing-embed-c-and-c++>, it
internally generates __builtin_pp_embed, which takes as arguments the
expected type (always unsigned char for now), the filename as a string
literal, and the data encoded as a base64 string literal.  That's not
going to be as fast as a hypothetical pure binary blob, but apparently
it's still much faster than parsing a comma-delimited sequence.

I haven't been able to get "clang -E" in the prototype to generate
__builtin_pp_embed, or to get clang to recognize it.  There are internal
things going on that I don't understand.

The author points out that using binary blobs would break tools that
work with -E preprocessed source files.  If you could assume that the
preprocessed output will be processed only by the same compiler, that
wouldn't be an issue, but apparently that's not a safe assumption.

The author acknowedges that the prototype implementation doesn't handle
all cases correctly.

Note that I can't say how much of a difference this will make in real
life.  I don't know how often people need to include multi-megabyte
files in their code.  It certainly is not at a level where I would
change any of my existing projects from external generator scripts to
using #embed, but I might use it in future projects.
>
>
An optimized version might have the preprocessor generate some
compiler-specific binary output, say something like "@rawdata N"
followed by N bytes of raw data.  Later compiler phases recognize the
"@rawdata" construct and directly dump the data into the object file in
the right place.  Making #embed generate @rawdata is only part of the
solution; the compiler has to implement @rawdata in a way that allows it
to be used inside an initializer, or perhaps in any other appropriate
context.
>
That's the idea.  In theory, C pre-processors and C compilers are
independent programs with a standardised format between them - in
practice, they are often part of the same binary, and almost
invariably come from the same developers.  The "cpp" program may have
to generate standard preprocessed output, and the "cc" program may
have to accept standard preprocessed output, but there is nothing to
stop the pair of programs supporting extended formats that are more
efficient.
>
This could be substantially more efficient for something like:
     static const unsigned char data[] = {
     #embed "bigfile.dat"
     };
Of course it wouldn't handle my test case above.  But #embed can
take
parameters, so it could generate the standard sequence by default and
"@rawdata" if you ask for it.
I don't know whether this kind of optimization is worthwhile, i.e.,
whether the straightforward implementation really imposes significant
commpile-time performance penalties that @rawdata or equivalent can
solve.  I also don't know whether existing implementations will
implement this kind of optimization (so far they haven't implemented
#embed at all).
>
Prototypes have been made, and they do have such optimisations.  How
things end up in real tools remains to be seen, of course.

Here's how I personally would have preferred for #embed to be specified:

- As in current C23 drafts, #embed with no parameters must operate *as
  if* it expanded to a comma-delimited list of integer constant
  expressions.
- With no parameters, both the common cases (initializing an array of
  characters) and odd cases (e.g., initializing a struct object with
  varying types and sizes of members) must work as specified.
- A standard-defined parameter allows control over optimization.

The parameter can be "optimize(true)" or "optimize(false)".

"optimize(false)" has no formal effect, but the compiler *should*
generate the canonical sequence of constants.

"optimize(true)" causes undefined behavior if #embed is used in a
context other than the initialization of an array of character type.

A naive compiler can quietly ignore the optimize() parameter and always
generate the comma-delimited sequence.  An exceedingly clever compiler
could ignore it and always make a correct decision about whether to
optimize #embed.

Without the optimize parameter, typical compilers are expected to
optimize #embed depending on the context in which it's used, and should
produce the correct results in all cases.  The parameter can be used to
override the compiler's judgement.

Another possibility might have been to specify that #embed can *only* be
used to initialize an array of character type, and any other use either
has undefined behavior or is a constraint violation.  That would avoid
all the complication of determining from context whether it can be
optimized, and would probably cover 99% of cases.  But it's probably too
late for that.

--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
void Void(void) { Void(); } /* The recursive call of the void */

Date Sujet#  Auteur
22 May 24 * C23 thoughts and opinions524David Brown
22 May 24 +* Re: C23 thoughts and opinions355Thiago Adams
22 May 24 i+* Re: C23 thoughts and opinions352David Brown
22 May 24 ii+* Re: C23 thoughts and opinions22Thiago Adams
23 May 24 iii`* Re: C23 thoughts and opinions21David Brown
23 May 24 iii `* Re: C23 thoughts and opinions20Thiago Adams
23 May 24 iii  +* Re: C23 thoughts and opinions18David Brown
23 May 24 iii  i`* Re: C23 thoughts and opinions17Thiago Adams
23 May 24 iii  i `* Re: C23 thoughts and opinions16Keith Thompson
24 May 24 iii  i  +- Re: C23 thoughts and opinions1David Brown
24 May 24 iii  i  `* Re: C23 thoughts and opinions14Thiago Adams
24 May 24 iii  i   `* Re: C23 thoughts and opinions13Keith Thompson
24 May 24 iii  i    `* Re: C23 thoughts and opinions12Thiago Adams
24 May 24 iii  i     `* Re: C23 thoughts and opinions11Keith Thompson
25 May 24 iii  i      `* Re: C23 thoughts and opinions10Thiago Adams
25 May 24 iii  i       +* Re: C23 thoughts and opinions4Keith Thompson
25 May 24 iii  i       i`* Re: C23 thoughts and opinions3Thiago Adams
25 May 24 iii  i       i `* Re: C23 thoughts and opinions2David Brown
26 May 24 iii  i       i  `- Re: C23 thoughts and opinions1Keith Thompson
25 May 24 iii  i       `* Re: C23 thoughts and opinions5David Brown
25 May 24 iii  i        `* Re: C23 thoughts and opinions4Thiago Adams
25 May 24 iii  i         +* Re: C23 thoughts and opinions2David Brown
26 May 24 iii  i         i`- Re: C23 thoughts and opinions1bart
6 Jun 24 iii  i         `- Re: C23 thoughts and opinions1Thiago Adams
23 May 24 iii  `- Re: C23 thoughts and opinions1Thiago Adams
23 May 24 ii+* Re: C23 thoughts and opinions323Keith Thompson
23 May 24 iii+* Re: C23 thoughts and opinions313Thiago Adams
23 May 24 iiii`* Re: C23 thoughts and opinions312bart
23 May 24 iiii +* Re: C23 thoughts and opinions309David Brown
23 May 24 iiii i`* Re: C23 thoughts and opinions308Keith Thompson
24 May 24 iiii i +- Re: C23 thoughts and opinions1David Brown
25 May 24 iiii i +* Re: C23 thoughts and opinions305Keith Thompson
25 May 24 iiii i i`* Re: C23 thoughts and opinions304David Brown
26 May 24 iiii i i `* Re: C23 thoughts and opinions303Keith Thompson
26 May 24 iiii i i  +* Re: C23 thoughts and opinions300David Brown
26 May 24 iiii i i  i+* Re: C23 thoughts and opinions17bart
26 May 24 iiii i i  ii`* Re: C23 thoughts and opinions16Michael S
26 May 24 iiii i i  ii `* Re: C23 thoughts and opinions15bart
26 May 24 iiii i i  ii  `* Re: C23 thoughts and opinions14Michael S
26 May 24 iiii i i  ii   +* Re: C23 thoughts and opinions3bart
26 May 24 iiii i i  ii   i`* Re: C23 thoughts and opinions2Michael S
26 May 24 iiii i i  ii   i `- Re: C23 thoughts and opinions1bart
26 May 24 iiii i i  ii   +* Re: C23 thoughts and opinions5Malcolm McLean
26 May 24 iiii i i  ii   i`* Re: C23 thoughts and opinions4Michael S
27 May 24 iiii i i  ii   i `* Re: C23 thoughts and opinions3Lawrence D'Oliveiro
27 May 24 iiii i i  ii   i  +- Re: C23 thoughts and opinions1Chris M. Thomasson
27 May 24 iiii i i  ii   i  `- Re: C23 thoughts and opinions1David Brown
26 May 24 iiii i i  ii   +- Re: C23 thoughts and opinions1Michael S
26 May 24 iiii i i  ii   +- Re: C23 thoughts and opinions1bart
27 May 24 iiii i i  ii   +- Re: C23 thoughts and opinions1Keith Thompson
27 May 24 iiii i i  ii   `* Re: C23 thoughts and opinions2Lawrence D'Oliveiro
27 May 24 iiii i i  ii    `- Re: C23 thoughts and opinions1Michael S
26 May 24 iiii i i  i+- Re: C23 thoughts and opinions1Thiago Adams
27 May 24 iiii i i  i+* Re: C23 thoughts and opinions66Keith Thompson
27 May 24 iiii i i  ii+* Re: C23 thoughts and opinions62David Brown
28 May 24 iiii i i  iii`* Re: C23 thoughts and opinions61Keith Thompson
28 May 24 iiii i i  iii `* Re: C23 thoughts and opinions60David Brown
28 May 24 iiii i i  iii  `* Re: C23 thoughts and opinions59Keith Thompson
28 May 24 iiii i i  iii   +- Re: C23 thoughts and opinions1Michael S
29 May 24 iiii i i  iii   `* Re: C23 thoughts and opinions57David Brown
14 Jun 24 iiii i i  iii    `* Re: C23 thoughts and opinions56Keith Thompson
15 Jun 24 iiii i i  iii     +* Re: C23 thoughts and opinions12bart
15 Jun 24 iiii i i  iii     i`* Re: C23 thoughts and opinions11David Brown
15 Jun 24 iiii i i  iii     i `* Re: C23 thoughts and opinions10bart
16 Jun 24 iiii i i  iii     i  +* Re: C23 thoughts and opinions5Lawrence D'Oliveiro
16 Jun 24 iiii i i  iii     i  i`* Re: C23 thoughts and opinions4bart
16 Jun 24 iiii i i  iii     i  i +- Re: C23 thoughts and opinions1Lawrence D'Oliveiro
16 Jun 24 iiii i i  iii     i  i `* Re: C23 thoughts and opinions2Chris M. Thomasson
17 Jun 24 iiii i i  iii     i  i  `- Re: C23 thoughts and opinions1Lawrence D'Oliveiro
16 Jun 24 iiii i i  iii     i  `* Re: C23 thoughts and opinions4David Brown
16 Jun 24 iiii i i  iii     i   `* Re: C23 thoughts and opinions3bart
17 Jun 24 iiii i i  iii     i    +- Re: C23 thoughts and opinions1David Brown
17 Jun 24 iiii i i  iii     i    `- Re: C23 thoughts and opinions1Michael S
15 Jun 24 iiii i i  iii     +* Re: C23 thoughts and opinions3David Brown
16 Jun 24 iiii i i  iii     i`* Re: C23 thoughts and opinions2Lawrence D'Oliveiro
16 Jun 24 iiii i i  iii     i `- Re: C23 thoughts and opinions1David Brown
17 Jun 24 iiii i i  iii     `* Hex string literals (was Re: C23 thoughts and opinions)40Keith Thompson
17 Jun 24 iiii i i  iii      +* Re: Hex string literals (was Re: C23 thoughts and opinions)20David Brown
18 Jun 24 iiii i i  iii      i+* Re: Hex string literals (was Re: C23 thoughts and opinions)18Keith Thompson
18 Jun 24 iiii i i  iii      ii+* Re: Hex string literals (was Re: C23 thoughts and opinions)2Lawrence D'Oliveiro
18 Jun 24 iiii i i  iii      iii`- Re: Hex string literals (was Re: C23 thoughts and opinions)1Keith Thompson
18 Jun 24 iiii i i  iii      ii`* Re: Hex string literals (was Re: C23 thoughts and opinions)15David Brown
19 Jun 24 iiii i i  iii      ii +* Re: Hex string literals (was Re: C23 thoughts and opinions)6Keith Thompson
19 Jun 24 iiii i i  iii      ii i`* Re: Hex string literals (was Re: C23 thoughts and opinions)5David Brown
19 Jun 24 iiii i i  iii      ii i `* Re: Hex string literals (was Re: C23 thoughts and opinions)4Kaz Kylheku
19 Jun 24 iiii i i  iii      ii i  `* Re: Hex string literals (was Re: C23 thoughts and opinions)3Michael S
19 Jun 24 iiii i i  iii      ii i   +- Re: Hex string literals (was Re: C23 thoughts and opinions)1bart
19 Jun 24 iiii i i  iii      ii i   `- Re: Hex string literals (was Re: C23 thoughts and opinions)1Michael S
19 Jun 24 iiii i i  iii      ii `* Re: Hex string literals (was Re: C23 thoughts and opinions)8Lawrence D'Oliveiro
19 Jun 24 iiii i i  iii      ii  +* Re: Hex string literals (was Re: C23 thoughts and opinions)6David Brown
21 Jun 24 iiii i i  iii      ii  i`* Re: Hex string literals (was Re: C23 thoughts and opinions)5Lawrence D'Oliveiro
21 Jun 24 iiii i i  iii      ii  i +* Re: Hex string literals (was Re: C23 thoughts and opinions)3David Brown
22 Jun 24 iiii i i  iii      ii  i i`* Re: Hex string literals (was Re: C23 thoughts and opinions)2Lawrence D'Oliveiro
22 Jun 24 iiii i i  iii      ii  i i `- Re: Hex string literals (was Re: C23 thoughts and opinions)1David Brown
21 Jun 24 iiii i i  iii      ii  i `- Re: Hex string literals (was Re: C23 thoughts and opinions)1James Kuyper
19 Jun 24 iiii i i  iii      ii  `- Re: Hex string literals (was Re: C23 thoughts and opinions)1Keith Thompson
18 Jun 24 iiii i i  iii      i`- Re: Hex string literals (was Re: C23 thoughts and opinions)1Lawrence D'Oliveiro
17 Jun 24 iiii i i  iii      +* Re: Hex string literals (was Re: C23 thoughts and opinions)5Richard Kettlewell
17 Jun 24 iiii i i  iii      i+- Re: Hex string literals (was Re: C23 thoughts and opinions)1Richard Kettlewell
18 Jun 24 iiii i i  iii      i`* Re: Hex string literals (was Re: C23 thoughts and opinions)3Keith Thompson
18 Jun 24 iiii i i  iii      i +- Re: Hex string literals (was Re: C23 thoughts and opinions)1Lawrence D'Oliveiro
18 Jun 24 iiii i i  iii      i `- Re: Hex string literals (was Re: C23 thoughts and opinions)1Richard Kettlewell
17 Jun 24 iiii i i  iii      `* Re: Hex string literals (was Re: C23 thoughts and opinions)14bart
28 May 24 iiii i i  ii+* Re: C23 thoughts and opinions2Keith Thompson
28 May 24 iiii i i  ii`- Re: C23 thoughts and opinions1Malcolm McLean
27 May 24 iiii i i  i+* Re: C23 thoughts and opinions121Lawrence D'Oliveiro
28 May 24 iiii i i  i`* xxd -i vs DIY Was: C23 thoughts and opinions94Michael S
28 May 24 iiii i i  `* Re: C23 thoughts and opinions2Keith Thompson
12 Jun 24 iiii i `- Re: C23 thoughts and opinions1Bonita Montero
23 May 24 iiii `* Re: C23 thoughts and opinions2Keith Thompson
23 May 24 iii+* Re: C23 thoughts and opinions7Thiago Adams
23 May 24 iii`* Re: C23 thoughts and opinions2David Brown
23 May 24 ii`* Re: C23 thoughts and opinions6Michael S
23 May 24 i`* Re: C23 thoughts and opinions2Lawrence D'Oliveiro
22 May 24 +* Re: C23 thoughts and opinions10Malcolm McLean
22 May 24 +* Re: C23 thoughts and opinions9Chris M. Thomasson
23 May 24 +* Re: C23 thoughts and opinions2Lawrence D'Oliveiro
23 May 24 +* Re: C23 thoughts and opinions14Michael S
23 May 24 +* Re: C23 thoughts and opinions - why so conservative?37Michael S
23 May 24 +* Re: C23 thoughts and opinions94Bonita Montero
25 May 24 `* Re: C23 thoughts and opinions2Thiago Adams

Haut de la page

Les messages affichés proviennent d'usenet.

NewsPortal