Non-constant constant strings

R

Rick C. Hodgin

This is fascinating. My algorithm just has to look for a single character
which is always a line-ending and never has to worry about it.

Would that such a feature works everywhere. It doesn't. It's why I began
working with binary files a long time back (1990s). I have never looked
back. It may have been due to buggy function implementations back then,
but nonetheless, when I have control over code myself it's never an issue.
You're missing the point. If you were doing this the sane way, you would
not have to type the \r, *and* you would never have problems in text editors.

It's part of having total ownership of the code. By me being in control of
exactly what it does at all points, I can always find out exactly what the
problem is. When I rely on other tools to do something for me ... there's
that black box thing. I don't know exactly what's going on in there, so it's
a potential cause for problems. In some cases the black box is unavoidable,
and in most cases it's not an issue. As I indicated, I had issues with text
file processing back a long while ago, and I just switched and have not had
any issues since then.
Except that you're creating files which have extra characters in them which
will sometimes be interpreted as literal characters which are part of the line.

I haven't seen an editor yet that does that. If I encountered one, I would stop
using that editor because there are plenty that don't. :)
I am wondering if perhaps you have been confused by the existence of binary
and text modes for ftp, where you really do always want binary.

I want a literal input, byte per byte, on my input files. No translation
except what I do to the files after they're loaded.

Best regards,
Rick C. Hodgin
 
R

Rick C. Hodgin

I appreciate everyone's reply and help. I found the solution I was looking
for. Joe Keane proposed the solution which handles this condition of non-
constant constant declarations:

char* list[] =
{
"one",
"two",
"three"
}

By default, the compiler creates constant strings for list[0], list[1], and
list[2]. This can be overridden by using a compound literal in GCC, as in:

char* list[] =
{
(char []) { "one" },
(char []) { "two" },
(char []) { "three" }
}

And this can be simplified by using a macro to make the item read/write:

#define rw(x) (char []) { x }

char* list[] =
{
rw("one"),
rw("two"),
rw("three")
}

Thank you, Joe. And thank you everyone else for the assistance.

Best regards,
Rick C. Hodgin
 
J

James Kuyper

Would that such a feature works everywhere. It doesn't.

If the program that wrote the file uses a conforming implementation of C
to open the file in text mode, and the program that reads it uses a
conforming implementation of C to open the file in text mode, it should
work so long as both implementations implement the same text file
conventions. That will generally be the case if they target the same
operating system. It's also likely to be true for other text-processing
programs targeting that same operating system, that don't use C at all.
 
R

Rick C. Hodgin

IF the program that wrote the file uses a conforming implementation of C
to open the file in text mode, AND the program that reads it uses a
conforming implementation of C to open the file in text mode, it SHOULD
work so long as BOTH implementations implement THE SAME text file
conventions. That will GENERALLY be the case IF they target THE SAME
OPERATING SYSTEM. It's also LIKELY to be true for other text-processing
programs targeting that same operating system, that don't use C at all.

That's a lot of caveats and addendums there, James Kuyper.

As I say... would that such a feature works everywhere. It doesn't. :) It's
proven far easier to just always handle it myself ... and then there's never
an issue no matter where I go.

Remember what Rumsfeld taught us:
(1) Known knowns.
(2) Known unknowns.
(3) Unknown unknowns.

It's (3) that bites you. :)

Best regards,
Rick C. Hodgin
 
J

James Kuyper

That's a lot of caveats and addendums there, James Kuyper.

Almost every useful statement about reality has some caveats, even if
they're not explicitly stated. Often, when they are stated, it's
precisely the caveats that contain most of the useful information
conveyed by the statement.
 
K

Keith Thompson

Rick C. Hodgin said:
I appreciate everyone's reply and help. I found the solution I was looking
for. Joe Keane proposed the solution which handles this condition of non-
constant constant declarations:

char* list[] =
{
"one",
"two",
"three"
}

By default, the compiler creates constant strings for list[0], list[1], and
list[2]. This can be overridden by using a compound literal in GCC, as in:

char* list[] =
{
(char []) { "one" },
(char []) { "two" },
(char []) { "three" }
}

And this can be simplified by using a macro to make the item read/write:

#define rw(x) (char []) { x }

char* list[] =
{
rw("one"),
rw("two"),
rw("three")
}

Thank you, Joe. And thank you everyone else for the assistance.

It's conventional to write macro names in all-caps. That convention is
not universally followed, but in this case it avoids some possible
confusion. Reading your rw("one"), I'd assume that rw is the name of a
function. If I saw RW("one"), I'd assume it's a macro, and look for the
definition to understand exactly what it means.

Is your list defined at file scope? If not, as I mentioned earlier, you
could have problems with the lifetimes of the objects created by the
compound literals.
 
R

Rick C. Hodgin

And this can be simplified by using a macro to make the item read/write:
#define rw(x) (char []) { x }
char* list[] =
{
rw("one"),
rw("two"),
rw("three")
}
Thank you, Joe. And thank you everyone else for the assistance.

It's conventional to write macro names in all-caps. That convention is
not universally followed, but in this case it avoids some possible
confusion.

How could there be confusion within the definition of a char* list[] array?
Reading your rw("one"), I'd assume that rw is the name of a function.
If I saw RW("one"), I'd assume it's a macro, and look for the definition
to understand exactly what it means.

Wouldn't the context convey its meaning? At that point in source code it
can only be a macro. Or is there something else it could be? To my
knowledge, that's it.
Is your list defined at file scope? If not, as I mentioned earlier, you
could have problems with the lifetimes of the objects created by the
compound literals.

Yes. It's a global variable.

Best regards,
Rick C. Hodgin
 
B

Ben Bacarisse

Rick C. Hodgin said:
The comma responses came notably later in the thread after my original
question was asked, and even re-worded a few times.

You are right; I have it backwards. I stopped thinking about the
original issue when you narrowed it down ("There are lots of solutions
and workarounds. I'm looking for a compiler directive...") and *then* I
got turned off. Your setting C's designers to rights had nothing to do
with my not coming up with a solution.

<snip>
 
K

Keith Thompson

Rick C. Hodgin said:
And this can be simplified by using a macro to make the item read/write:
#define rw(x) (char []) { x }
char* list[] =
{
rw("one"),
rw("two"),
rw("three")
}
Thank you, Joe. And thank you everyone else for the assistance.

It's conventional to write macro names in all-caps. That convention is
not universally followed, but in this case it avoids some possible
confusion.

How could there be confusion within the definition of a char* list[] array?

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

char *rw(char *s) {
/* similar to the non-standard strdup */
char *result = malloc(strlen(s) + 1);
if (result != NULL) {
strcpy(result, s);
}
return result;
}

int main(void) {
char* list[] = {
rw("one"),
rw("two"),
rw("three")
};
for (size_t i = 0; i < sizeof list / sizeof *list; i ++) {
puts(list);
}
}
Wouldn't the context convey its meaning? At that point in source code it
can only be a macro. Or is there something else it could be? To my
knowledge, that's it.

At file scope, it could be a macro, or an incorrect function call, or
something else I'm not thinking of at the moment.

If you want to force me to stop and think through all the things it
could be until I've eliminated every possibility other than a macro, go
ahead and call it rw. If you want to help the reader to understand your
code, I suggest calling it RW.
 
J

James Kuyper

And this can be simplified by using a macro to make the item read/write:
#define rw(x) (char []) { x }
char* list[] =
{
rw("one"),
rw("two"),
rw("three")
}
Thank you, Joe. And thank you everyone else for the assistance.

It's conventional to write macro names in all-caps. That convention is
not universally followed, but in this case it avoids some possible
confusion.

How could there be confusion within the definition of a char* list[] array?
Reading your rw("one"), I'd assume that rw is the name of a function.
If I saw RW("one"), I'd assume it's a macro, and look for the definition
to understand exactly what it means.

Wouldn't the context convey its meaning? At that point in source code it
can only be a macro. Or is there something else it could be? To my
knowledge, that's it.

An initializer for an object can be any expression that could legally
appear on the right hand side of an assignment to that object
(6.7.9p11). C90 had tighter restrictions on initializers, but those
restrictions were relaxed when C99 was approved.

Even in a C90 context, another possible reason for the appearance of
rw() in that code could be a mistake. One of the main purposes of
conventions like that is to make such mistakes more obvious. By not
following that convention, you're forcing people who are used to that
convention to check up on the definition of rw(), to make sure that it
wasn't a mistaken function call.
Yes. It's a global variable.

You had not previously mentioned that fact. In that case, it has static
storage duration. Initializers for objects with static or thread local
storage duration must be either constant expressions or string literals
(6.7.9p5). In that context, you're right - function calls would not be
possible, it would have to be a macro. Would you never consider using
this macro for objects with automatic storage duration? If there's any
chance it might be used that way, then it should be clearly
distinguished from a function call, which could also be used in that
context.
 
R

Rick C. Hodgin

I believe everything should be read-write unless explicitly cast
otherwise.
Does that mean that every program should be able to set your bank
account balance to zero unless it's explicitly told not to?
Yes.

const static char helpmsg[] = "... text of message ...";
does not have a cast in it but I certainly hope that a compiler capable
of putting variables in read-only memory would take the hint and do so.

The cast condition I indicate is using the "const" prefix. You've cast
that variable declaration to be a constant.
I would also be interested in how you get the effect of an explicitly
(I'm guessing you would need a keyword like "nonconst" to make it
explicit) read-write variable x:

I think for these purposes it should be ro and rw, as in:
ro static char helpmsg1[] = "...";
rw static char helpmsg2[] = "...";

By default, the global compiler switch would be used:
rdc -ro file.rdc

In this case, everything would be ro (read-only) if not indicated, and in
order to make something read-write it would need rw.

By default it would be the other way around, meaning everything defined
would be read-write unless it was explicitly indicated to be ro, as using
const or ro.
void func(void)
{
nonconst int x;
... bunch of stuff that uses and changes x ...;
}
using only one or more casts.

I think my use of the word cast is confusing you/here. I was referring
not to something like (char*)malloc(5) as casting the void* return result
to char*, but rather casting something as a thing ... perhaps in C that
is the wrong word to use. Please forgive my overt ignorance.
There you go with the casts again. How do you cast a *variable*
(as distinguished from the value of a variable) to read-only or
read-write with a cast? I've seen GCC sort of do this with __attribute__
but that's not Standard C. And something like:
*(nonconst int *)&x = 1;
would *not* assure that x is allocated in read-write memory.

The variable would be defined as a particular type, and would be allocated
at compile-time, or at runtime, as that type of memory. References to its
memory address would determine at runtime if it's read-only or not (on
architectures which support the same), and for the rest it would be based
on the legal use of the variables as defined.
I certainly don't agree with having main() (that is, the *code*)
be read-write by default.

Source code is read-only by default. However, that's a debatable issue
as well.
That encourages viruses and makes bugs
harder to detect. Also, you can't share code segments. Many
processors require you to leap through some hoops (typically, mapping
the same memory through two virtual addresses) in order to have the
same memory writable and executable at the same time.

My targets are x86 and ARM. So far as I'm concerned about these ideas I'm
purporting ... they'll likely never see any other architecture. And if they
do, they'll adapt to it as well.

Best regards,
Rick C. Hodgin
 
B

Ben Bacarisse

Rick C. Hodgin said:
In moving forward, I would write some new ability to create the list
properly, giving the compiler a new ability to handle variable items:

char *archiveFormats[] =
{
#elementif CPIO_SUPPORTED "cpio"
#elementif TAR_SUPPORTED "tar"
#elementif ZIP_SUPPORTED "ZIP"
#elementif APK_SUPPORTED "apk"
null // Optional
};

Something like this might suit:

#define OPT(which, what) which(what)

#define YES(x) x,
#define NO(x)

#define X YES
#define Y NO

const char *list[] = {
OPT(X, "abc")
OPT(Y, "def")
};

<snip>
 
R

Rick C. Hodgin

How could there be confusion within the definition of a char* list[] array?
Wouldn't the context convey its meaning? At that point in source code it
can only be a macro. Or is there something else it could be? To my
knowledge, that's it.

An initializer for an object can be any expression that could legally
appear on the right hand side of an assignment to that object
(6.7.9p11). C90 had tighter restrictions on initializers, but those
restrictions were relaxed when C99 was approved.

Even in a C90 context, another possible reason for the appearance of
rw() in that code could be a mistake. One of the main purposes of
conventions like that is to make such mistakes more obvious. By not
following that convention, you're forcing people who are used to that
convention to check up on the definition of rw(), to make sure that it
wasn't a mistaken function call.

FWIW, I have never heard of that convention. In all of my code I do follow
a convention. It just happens to be a different one. :)
You had not previously mentioned that fact. In that case, it has static
storage duration. Initializers for objects with static or thread local
storage duration must be either constant expressions or string literals
(6.7.9p5). In that context, you're right - function calls would not be
possible, it would have to be a macro. Would you never consider using
this macro for objects with automatic storage duration? If there's any
chance it might be used that way, then it should be clearly
distinguished from a function call, which could also be used in that
context.

I do not believe in automatic storage. I think it's something that hides
operations and removes them from overt, explicit, code execution. I define
all of my variables at the top and then include an explicit section which
initializes them below, usually as the first thing done in the function. In
that way, anyone stepping through the code or looking at the code, would see
step by step exactly what's happening. There would be no room for confusion,
nor would one have to deduce what's going on.

FWIW, I would never, under any circumstances, code something like Keith's
example. In fact, when I saw that code I had to try it out to see if it
was actually legal. :) I looked at the assembly it was doing and it was
interesting. There was an issue in Visual C++ with the char* result = malloc()
bit. malloc()'s void* return needed to be cast to char* to compile. :)

I just don't code like that. It seems overtly confusing. I would much
rather have defined blocks which do everything explicitly.

Best regards,
Rick C. Hodgin
 
J

James Kuyper

I believe everything should be read-write unless explicitly cast
otherwise.
....
const static char helpmsg[] = "... text of message ...";
does not have a cast in it but I certainly hope that a compiler capable
of putting variables in read-only memory would take the hint and do so.

The cast condition I indicate is using the "const" prefix. You've cast
that variable declaration to be a constant. ....
I think my use of the word cast is confusing you/here. I was referring
not to something like (char*)malloc(5) as casting the void* return result
to char*, but rather casting something as a thing ... perhaps in C that
is the wrong word to use.

Very much so. The const keyword is a type qualifier; it's always part of
a type specification. As part of a type specification, it can appear in
a cast expression, but is not itself a cast, and it's appearance in
other contexts, such as this one, has nothing to do with casting. The
word that apparently fits what your trying to say is "declared", not "cast".
 
R

Rick C. Hodgin

Source code is read-only by default. However, that's a debatable issue
as well.


I take this back. I would make executable code (which is what I meant when I
wrote "source code") as read-write by default in debug mode, because in this
way edit-and-continue is supported. I would provide the option though to make
it ro or rw based on a switch. By default in release mode it would be ro.

Best regards,
Rick C. Hodgin
 
R

Rick C. Hodgin

Very much so.

That's what you get when you have a C user not trained in the semantics and
specifics of C nomenclature trying to convey things to C folk.
The const keyword is a type qualifier; it's always part of
a type specification. As part of a type specification, it can appear in
a cast expression, but is not itself a cast, and it's appearance in
other contexts, such as this one, has nothing to do with casting. The
word that apparently fits what your trying to say is "declared", not "cast".

Then my ro and rw would be new type qualifiers which alter the declaration
of the variable to be either a constant read-only or variable read-write.
By default it would always be rw, unless the compiler switch -RO was used,
and any variable can be explicitly type qualified using ro or rw based upon
need in source code.

How does C handle read-only variables allocated and initialized at runtime?
I know in Windows and Visual C++ we can mark a memory range to be a particular
type (such as read-only), change it to read-write, populate a new variable,
and then set it back, but that is an OS convention, not a language convention.

Is there a C method to handle runtime-allocated read-only variables apart
from the const type qualifier, which would've been only enforceable at
compile time?

And again, please forgive me if I'm using the wrong words. As I have
dyslexia, even on things where I know the proper words to use I sometimes
use the wrong words. :)

Best regards,
Rick C. Hodgin
 
K

Keith Thompson

Rick C. Hodgin said:
I believe everything should be read-write unless explicitly cast
otherwise.
Does that mean that every program should be able to set your bank
account balance to zero unless it's explicitly told not to?
Yes.

const static char helpmsg[] = "... text of message ...";
does not have a cast in it but I certainly hope that a compiler capable
of putting variables in read-only memory would take the hint and do so.

The cast condition I indicate is using the "const" prefix. You've cast
that variable declaration to be a constant.

A "cast" is an operator, represented as a parenthesized type name, that
specifies an explicit conversion of a value from some scalar type to
some (possibly different) scalar type. Please do not use the word
"cast" to refer to something else.

[...]
 
B

Ben Bacarisse

Rick C. Hodgin said:
It does categorically. Even if it's only computing data into registers,
data is being read into the CPU, processed, and then written back somewhere.
It is impossible to have a computing system that does not do this.

That's mixing levels. Your remarks were about what should be seen at
the language level. Most computers work with voltages, but there are no
voltage setting commands in most programming languages. Kaz's point is
that programming does not imply what you seemed to be talking about --
modification of things (variables, objects and so on) in the programming
language.
I have no idea what you're talking about.

Yet you know Kaz is categorically wrong? Surely you accept that there
might be people who know more about programming languages and language
design than you do?

My FWIW is that Kaz's remark is pertinent to your goal of massively
parallel programming. The read/write model is highly problematic for
many parallel architectures and, if I *had* put money on it, I would say
that a functional programming language (albeit probably not a purely
functional one) will the winner in this field. You might want to look
into it the idea rather than dismiss it as categorically wrong.

<snip>
 
R

Rick C. Hodgin

Something like this might suit:

#define OPT(which, what) which(what)

#define YES(x) x,
#define NO(x)

#define X YES
#define Y NO

const char *list[] = {
OPT(X, "abc")
OPT(Y, "def")
};

Aren't you now required to include somewhere in your source code an explicit
list of operators for each optional entry? And with code generators, don't
those usually come from external sources?

#define OPT(which, what) which(what)

#define YES(x) x,
#define NO(x)

#define CPIO_SUPPORTED YES
#define TAR_SUPPORTED NO
#define ZIP_SUPPORTED YES
#define APK_SUPPORTED YES

const char *list[] = {
OPT(CPIO_SUPPORTED, "cpio")
OPT(TAR_SUPPORTED, "tar")
OPT(ZIP_SUPPORTED, "ZIP")
OPT(APK_SUPPORTED, "apk")
};

Best regards,
Rick C. Hodgin
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,797
Messages
2,569,646
Members
45,373
Latest member
Vast3

Latest Threads

Top