www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Okay, what happened to my literal strings?

reply "Burton Radons" <burton-radons shaw.ca> writes:
So I just installed DMD 2.004 (I thought I had a DMD 2.0 version already 
installed), and now, no matter which DMD 2.0 I install (removing the dmd and 
dm directories first), this code fails:

'char [] foo = "bar";'

With the error code:

'foo.d(1): Error: cannot implicitly convert expression ("bar") of type 
invariant char[3] to char[]'.

The only difference between the versions is that DMD 2.002 and up say 
"char[3u]" in the error message instead of "char[3]". All literal strings 
fail with this error message. The latest DMD 1.0 works fine, but I need 
traits. 
Sep 06 2007
next sibling parent BCS <ao pathlink.com> writes:
Reply to Burton,

 So I just installed DMD 2.004 (I thought I had a DMD 2.0 version
 already installed), and now, no matter which DMD 2.0 I install
 (removing the dmd and dm directories first), this code fails:
 
 'char [] foo = "bar";'
 
 With the error code:
 
 'foo.d(1): Error: cannot implicitly convert expression ("bar") of type
 invariant char[3] to char[]'.
 
 The only difference between the versions is that DMD 2.002 and up say
 "char[3u]" in the error message instead of "char[3]". All literal
 strings fail with this error message. The latest DMD 1.0 works fine,
 but I need traits.
 
I can't seem to find the thread but This came up a few days ago in one of the NGs. I think the basic issue is that with the new const stuff an implicit case was removed. replace char[] with string and that error should go away.
Sep 06 2007
prev sibling next sibling parent reply Kirk McDonald <kirklin.mcdonald gmail.com> writes:
Burton Radons wrote:
 So I just installed DMD 2.004 (I thought I had a DMD 2.0 version already 
 installed), and now, no matter which DMD 2.0 I install (removing the dmd 
 and dm directories first), this code fails:
 
 'char [] foo = "bar";'
 
 With the error code:
 
 'foo.d(1): Error: cannot implicitly convert expression ("bar") of type 
 invariant char[3] to char[]'.
 
 The only difference between the versions is that DMD 2.002 and up say 
 "char[3u]" in the error message instead of "char[3]". All literal 
 strings fail with this error message. The latest DMD 1.0 works fine, but 
 I need traits.
You missed the word "invariant". String literals are of type invariant(char)[n] in D 2.x. (Where 'n' is the length of the string.) This is part of the new const semantics in 2.x. You have two options: Either explicitly .dup the string literal, placing a mutable copy on the heap: char[] foo = "bar".dup; Or use the 'string' alias to use the immutable string literal directly: string foo = "bar"; 'string' is an alias to const(char)[]. You can implicitly convert invariant types to const types. Using 'string' is highly recommended. -- Kirk McDonald http://kirkmcdonald.blogspot.com Pyd: Connecting D and Python http://pyd.dsource.org
Sep 06 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Kirk McDonald wrote:
 Using 'string' is highly recommended.
I agree, for multiple reasons: 1) it is backward compatible with D 1.0 2) it is forward compatible in case we need to redesign const 3) it reads nicer Use char[] only if you specifically require a mutable string.
Sep 06 2007
parent Derek Parnell <derek psych.ward> writes:
On Thu, 06 Sep 2007 14:42:53 -0700, Walter Bright wrote:

 Kirk McDonald wrote:
 Using 'string' is highly recommended.
I agree, for multiple reasons: 1) it is backward compatible with D 1.0 2) it is forward compatible in case we need to redesign const 3) it reads nicer Use char[] only if you specifically require a mutable string.
FWIW, I'm using alias char[] text; a lot now. -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Sep 06 2007
prev sibling parent reply "Burton Radons" <burton-radons shaw.ca> writes:
Ah, so 'const char [] foo = "bar";' works, does it? No thank you. Const 
forcing (whatever the name given) is the kind of tiddlywinks 
obsessive-compulsive bullshit that can't even be enforced so it's useless to 
everybody* which caused me to flee from C++. I've been using D as my primary 
language for... five years and four months now, and this lack has not 
resulted in a bug once. Needless to say, C++'s const didn't save me from a 
single bug either, but instead wasted my time (just like a bug does - the 
irony of many mollycoddling features is that they cause you to spend more 
time dealing with them than you would to deal with the bugs, if they even 
existed) by having me pour over code inserting const or casts everywhere 
like the virus it is.

This is _significantly_ against what I want from a language, and while I'm 
not sure how I'll feel in a day or two, my current inclination is to move on 
to another language or write my own. The many megabytes of code to retrofit 
don't help either.

* Some might be deluded into thinking it's useful to them for optimisation, 
bug prevention, or interface contracts. But the only thing circumventable 
const forcing does with any faculty is provide self-documentation, and even 
there the signal-to-noise ratio is pretty awful, it's easily abused (methods 
are frequently declared const in C++ when it's not a true requirement of 
what the method does, and it prevents situations where the method really is 
const but also modifies the object's data, which is not a paradox - the data 
might be cached or synthesized), often fraudulent, and usually not 
enforceable by the method. This is well-discussed and it seemed to be how 
Walter felt originally, so if someone can point out anything specific which 
changed his mind I'd be interested in seeing it. 
Sep 06 2007
next sibling parent Reiner Pope <some address.com> writes:
Burton Radons wrote:
 Ah, so 'const char [] foo = "bar";' works, does it? No thank you. Const 
 forcing (whatever the name given) is the kind of tiddlywinks 
 obsessive-compulsive bullshit that can't even be enforced so it's 
 useless to everybody* which caused me to flee from C++. I've been using 
 D as my primary language for... five years and four months now, and this 
 lack has not resulted in a bug once. Needless to say, C++'s const didn't 
 save me from a single bug either, but instead wasted my time (just like 
 a bug does - the irony of many mollycoddling features is that they cause 
 you to spend more time dealing with them than you would to deal with the 
 bugs, if they even existed) by having me pour over code inserting const 
 or casts everywhere like the virus it is.
 
 This is _significantly_ against what I want from a language, and while 
 I'm not sure how I'll feel in a day or two, my current inclination is to 
 move on to another language or write my own. The many megabytes of code 
 to retrofit don't help either.
 
 * Some might be deluded into thinking it's useful to them for 
 optimisation, bug prevention, or interface contracts. But the only thing 
 circumventable const forcing does with any faculty is provide 
 self-documentation, and even there the signal-to-noise ratio is pretty 
 awful, it's easily abused (methods are frequently declared const in C++ 
 when it's not a true requirement of what the method does, and it 
 prevents situations where the method really is const but also modifies 
 the object's data, which is not a paradox - the data might be cached or 
 synthesized), often fraudulent, and usually not enforceable by the 
 method. This is well-discussed and it seemed to be how Walter felt 
 originally, so if someone can point out anything specific which changed 
 his mind I'd be interested in seeing it.
You can always use "literal".dup if you don't want to use const. But you've never been *allowed* to modify string literals in D anyway, since they may be placed in ROM (eg on Linux). Making them invariant makes sense, then, as it expresses the already-present rule. -- Reiner
Sep 06 2007
prev sibling next sibling parent reply Robert Fraser <fraserofthenight gmail.com> writes:
Burton Radons Wrote:

 Ah, so 'const char [] foo = "bar";' works, does it? No thank you. Const 
 forcing (whatever the name given) is the kind of tiddlywinks 
 obsessive-compulsive bullshit that can't even be enforced so it's useless to 
 everybody* which caused me to flee from C++. I've been using D as my primary 
 language for... five years and four months now, and this lack has not 
 resulted in a bug once. Needless to say, C++'s const didn't save me from a 
 single bug either, but instead wasted my time (just like a bug does - the 
 irony of many mollycoddling features is that they cause you to spend more 
 time dealing with them than you would to deal with the bugs, if they even 
 existed) by having me pour over code inserting const or casts everywhere 
 like the virus it is.
 
 This is _significantly_ against what I want from a language, and while I'm 
 not sure how I'll feel in a day or two, my current inclination is to move on 
 to another language or write my own. The many megabytes of code to retrofit 
 don't help either.
 
 * Some might be deluded into thinking it's useful to them for optimisation, 
 bug prevention, or interface contracts. But the only thing circumventable 
 const forcing does with any faculty is provide self-documentation, and even 
 there the signal-to-noise ratio is pretty awful, it's easily abused (methods 
 are frequently declared const in C++ when it's not a true requirement of 
 what the method does, and it prevents situations where the method really is 
 const but also modifies the object's data, which is not a paradox - the data 
 might be cached or synthesized), often fraudulent, and usually not 
 enforceable by the method. This is well-discussed and it seemed to be how 
 Walter felt originally, so if someone can point out anything specific which 
 changed his mind I'd be interested in seeing it. 
 
As Reiner pointed out, modifying literal strings can lead to hard-to-find segfaults on Linux. It took me about an hour to find this the first time it happened to me when I started programming in C. I haven't made the mistake since, but still... For what its worth, I don't like const enforcing either, I think it's generally a waste of time. But it's very good, IMO, for one thing, and that thing is multi-threading. Knowing a value is invariant (and making sure the compiler knows it, too) means you don't have to lock it. For single-threaded programs, I would never write a "const". But locking/synchronization is slow, and if you're working on a big project (i.e. you're not familiar with thole codebase), it's always good to play it safe with shared class references. Knowing that the class reference is _invariant_ (not const as in C++ const, since there may be mutable references) means you don't need to lock it, increasing performance and making code easier to write. Walter said this about C++'s const, which displays many of the pathological problems you describe. D's const _is_ enforceable (well, const isn't, since it can be cast away, but invariant is, because invariant data can be placed into ROM).
Sep 06 2007
parent BCS <ao pathlink.com> writes:
Reply to Robert,

  D's const _is_ enforceable (well, const isn't, since it can be cast away
the only way to fully enforce const is to make refs a totally opaque type, disallow ASM etc. One of my first thoughts on seeing const in C++ was how easy it would be to get around it with sprintf/sscanf.
Sep 06 2007
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Burton Radons wrote:
 Ah, so 'const char [] foo = "bar";' works, does it? No thank you. Const 
 forcing (whatever the name given) is the kind of tiddlywinks 
 obsessive-compulsive bullshit that can't even be enforced so it's 
 useless to everybody* which caused me to flee from C++.
I hear you, Burton, and I resisted const for a long time for exactly those reasons. But there are some compelling reasons to support const: 1) It opens the door to functional programming. This means that function calls can be automatically parallelized, which is going to become an increasingly big deal as people try to figure out how to use their multicore processors. Note that C++ const CANNOT do FP, and Java CANNOT do const. This will open up a huge advantage for D. 2) People who work on large projects with several teams modifying the code tell me unequivocably that they need some sort of const to enforce modularity. 3) Const improves the self-documentation of interfaces. As for strings being invariant, experience across many languages demonstrates that invariant strings are the most natural and productive way for people to think about them. I went through Phobos replacing the char[]'s with string's, and I found out a couple of things: 1) about 99% of the char[]'s were immutable anyway, and fit right in with being string's 2) the edits can be done mechanically, just s/char[]/string/ across the files, compile, and revert the very small number of ones that are mutable. For other data types, you can often just ignore const and not use it. I want to reiterate that C++ const fails at (1), (2), and (3), because C++ const is not transitive and is not enforced. Furthermore, the shortsighted "mutable" keyword just pulls the rug out from under generic programming with const.
Sep 06 2007
parent reply "Burton Radons" <burton-radons shaw.ca> writes:
"Walter Bright" <newshound1 digitalmars.com> wrote in message 
news:fbq3d5$1j0j$1 digitalmars.com...
 1) It opens the door to functional programming. This means that function 
 calls can be automatically parallelized, which is going to become an 
 increasingly big deal as people try to figure out how to use their 
 multicore processors.

 Note that C++ const CANNOT do FP, and Java CANNOT do const. This will open 
 up a huge advantage for D.
But you can't do any serious optimisations (like SIMD) unless if you have the function body, in which case proving it's procedural (or where the output is defined based on hermetic input) is relatively simple and completely foolproof. C-type languages have too much of a fetish for becoming undefined. If my function is declared const or invariant, but it's not (which the language rules allow), it's undefined and the worst kind of undefined - the kind which works initially, and might even work with -O. But then one day it doesn't because of new optimisations or because the file is compiled in a different environment or even because the operating system has been upgraded or the same code is executed on a different processor or the code is moved into a different context or it or a dependency has been slightly modified. This can cause major damage to older code and forces dozens of discrete optimisation flags to try to get it to compile properly with a little more speed. If the language could not be put into an undefined state (but could be put into an erroneous state which could be detected in debug compilations) then you'd be free to implement any compliant optimisation without worrying about how much code it breaks. "Aggressive optimisation" wouldn't be a bad thing in that context.
 2) People who work on large projects with several teams modifying the code 
 tell me unequivocably that they need some sort of const to enforce 
 modularity.
I don't think you believe it's adequate to do that. I'd expect that to be a good situation for programmers using const incorrectly, because they might need to cast const off code which they're not in control of, particularly when the internal rule is "you should const anything you don't modify" (which is an easy trap) instead of "you should const anything you /shouldn't/ modify".
 3) Const improves the self-documentation of interfaces.
In this context, const is being used to describe a contract with the user. But contracts of this sort are impossible to reduce to a combination of keywords and unenforcable in all contexts by the compiler; there are too many subtleties. This belongs in the documentation itself. A good example is "char* strchr (const char*, char)" from C. That implies that the return value is not a pointer to an index of the first argument, but it is, it's just not declared that way because the argument itself may not be const. So the signature misses the subtlety of the true contract. If the user uses this signature as self-documentation then they could easily make an error. If it were instead "const char* strchr (const char*, char)" then the contract goes beyond what is strictly required because it's so blunt, and interpreted as self-documentation requires the user to make a copy before any modifications of the return value (and makes it impossible to do certain operations). If we decide that we don't want to lie then we can declare it as "char* strchr (char*, char)", which implies that the function modifies the string (because of the absence of const), when it never does; interpreted as self-documentation you'd always call it with mutable data. There's no happy medium: in any case the self-documentation is wrong and harms the user. This could be "fixed" through a complex set of attributes, in this case something like "argument (a) strchr (const char*a, char)", so the compiler could assume that if it calls it with a non-const argument then it gets a non-const argument in return and vice versa, and the function can be implemented without casting off const. But even keeping these literate contracts to const there are many variations that would require the user to learn a new language to interpret them when simple human-interpreted language more than suffices. If the compiler has the function body, then it doesn't require the attribute in order to be able to detect an error and is free to be as subtle as it needs to be in order to inform the user of any true problems.
 As for strings being invariant, experience across many languages 
 demonstrates that invariant strings are the most natural and productive 
 way for people to think about them.
That's fine, I can and do work like that. I don't need to tell the compiler that I know I shouldn't be modifying the string.
 I went through Phobos replacing the char[]'s with string's, and I found 
 out a couple of things:
 1) about 99% of the char[]'s were immutable anyway, and fit right in with 
 being string's
 2) the edits can be done mechanically, just s/char[]/string/ across the 
 files, compile, and revert the very small number of ones that are mutable.

 For other data types, you can often just ignore const and not use it.
My experience with C++'s const, which looks just about identical in this factor, is that you can only ignore const for so long, and that it's better to const the hell out of your code in the first place to avoid the agony of trying to use a library which uses const when your code is const-free. D is even worse in this regard since all it has is a brute-force cast, rather than C++'s subtle const_cast and other variants. This strongly encourages you to let the library infect your code with const because if you cast something when dealing with the library, it could cause a bug if the library is updated and the type changes for a method you're casting.
 I want to reiterate that C++ const fails at (1), (2), and (3), because C++ 
 const is not transitive and is not enforced. Furthermore, the shortsighted 
 "mutable" keyword just pulls the rug out from under generic programming 
 with const. 
Sep 07 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Burton Radons wrote:
 
 "Walter Bright" <newshound1 digitalmars.com> wrote in message 
 news:fbq3d5$1j0j$1 digitalmars.com...
 1) It opens the door to functional programming. This means that 
 function calls can be automatically parallelized, which is going to 
 become an increasingly big deal as people try to figure out how to use 
 their multicore processors.

 Note that C++ const CANNOT do FP, and Java CANNOT do const. This will 
 open up a huge advantage for D.
But you can't do any serious optimisations (like SIMD) unless if you have the function body, in which case proving it's procedural (or where the output is defined based on hermetic input) is relatively simple and completely foolproof. C-type languages have too much of a fetish for becoming undefined. If my function is declared const or invariant, but it's not (which the language rules allow), it's undefined and the worst kind of undefined - the kind which works initially, and might even work with -O. But then one day it doesn't because of new optimisations or because the file is compiled in a different environment or even because the operating system has been upgraded or the same code is executed on a different processor or the code is moved into a different context or it or a dependency has been slightly modified. This can cause major damage to older code and forces dozens of discrete optimisation flags to try to get it to compile properly with a little more speed. If the language could not be put into an undefined state (but could be put into an erroneous state which could be detected in debug compilations) then you'd be free to implement any compliant optimisation without worrying about how much code it breaks. "Aggressive optimisation" wouldn't be a bad thing in that context.
 2) People who work on large projects with several teams modifying the 
 code tell me unequivocably that they need some sort of const to 
 enforce modularity.
I don't think you believe it's adequate to do that. I'd expect that to be a good situation for programmers using const incorrectly, because they might need to cast const off code which they're not in control of, particularly when the internal rule is "you should const anything you don't modify" (which is an easy trap) instead of "you should const anything you /shouldn't/ modify".
 3) Const improves the self-documentation of interfaces.
In this context, const is being used to describe a contract with the user. But contracts of this sort are impossible to reduce to a combination of keywords and unenforcable in all contexts by the compiler; there are too many subtleties. This belongs in the documentation itself. A good example is "char* strchr (const char*, char)" from C. That implies that the return value is not a pointer to an index of the first argument, but it is, it's just not declared that way because the argument itself may not be const. So the signature misses the subtlety of the true contract. If the user uses this signature as self-documentation then they could easily make an error. If it were instead "const char* strchr (const char*, char)" then the contract goes beyond what is strictly required because it's so blunt, and interpreted as self-documentation requires the user to make a copy before any modifications of the return value (and makes it impossible to do certain operations). If we decide that we don't want to lie then we can declare it as "char* strchr (char*, char)", which implies that the function modifies the string (because of the absence of const), when it never does; interpreted as self-documentation you'd always call it with mutable data. There's no happy medium: in any case the self-documentation is wrong and harms the user. This could be "fixed" through a complex set of attributes, in this case something like "argument (a) strchr (const char*a, char)", so the compiler could assume that if it calls it with a non-const argument then it gets a non-const argument in return and vice versa, and the function can be implemented without casting off const. But even keeping these literate contracts to const there are many variations that would require the user to learn a new language to interpret them when simple human-interpreted language more than suffices.
I think the actual "fix" in C++ would be just to define a version of strchr overloaded on const. So you have both: const char* strchr(const char*, char); and char* strchr(char*, char); If you pass it a char* that can be modified, it'll give you back one that can be modified. Otherwise it won't. --bb
Sep 07 2007
next sibling parent Regan Heath <regan netmail.co.nz> writes:
Bill Baxter wrote:
 Burton Radons wrote:
 "Walter Bright" <newshound1 digitalmars.com> wrote in message 
 news:fbq3d5$1j0j$1 digitalmars.com...
 1) It opens the door to functional programming. This means that 
 function calls can be automatically parallelized, which is going to 
 become an increasingly big deal as people try to figure out how to 
 use their multicore processors.

 Note that C++ const CANNOT do FP, and Java CANNOT do const. This will 
 open up a huge advantage for D.
But you can't do any serious optimisations (like SIMD) unless if you have the function body, in which case proving it's procedural (or where the output is defined based on hermetic input) is relatively simple and completely foolproof. C-type languages have too much of a fetish for becoming undefined. If my function is declared const or invariant, but it's not (which the language rules allow), it's undefined and the worst kind of undefined - the kind which works initially, and might even work with -O. But then one day it doesn't because of new optimisations or because the file is compiled in a different environment or even because the operating system has been upgraded or the same code is executed on a different processor or the code is moved into a different context or it or a dependency has been slightly modified. This can cause major damage to older code and forces dozens of discrete optimisation flags to try to get it to compile properly with a little more speed. If the language could not be put into an undefined state (but could be put into an erroneous state which could be detected in debug compilations) then you'd be free to implement any compliant optimisation without worrying about how much code it breaks. "Aggressive optimisation" wouldn't be a bad thing in that context.
 2) People who work on large projects with several teams modifying the 
 code tell me unequivocably that they need some sort of const to 
 enforce modularity.
I don't think you believe it's adequate to do that. I'd expect that to be a good situation for programmers using const incorrectly, because they might need to cast const off code which they're not in control of, particularly when the internal rule is "you should const anything you don't modify" (which is an easy trap) instead of "you should const anything you /shouldn't/ modify".
 3) Const improves the self-documentation of interfaces.
In this context, const is being used to describe a contract with the user. But contracts of this sort are impossible to reduce to a combination of keywords and unenforcable in all contexts by the compiler; there are too many subtleties. This belongs in the documentation itself. A good example is "char* strchr (const char*, char)" from C. That implies that the return value is not a pointer to an index of the first argument, but it is, it's just not declared that way because the argument itself may not be const. So the signature misses the subtlety of the true contract. If the user uses this signature as self-documentation then they could easily make an error. If it were instead "const char* strchr (const char*, char)" then the contract goes beyond what is strictly required because it's so blunt, and interpreted as self-documentation requires the user to make a copy before any modifications of the return value (and makes it impossible to do certain operations). If we decide that we don't want to lie then we can declare it as "char* strchr (char*, char)", which implies that the function modifies the string (because of the absence of const), when it never does; interpreted as self-documentation you'd always call it with mutable data. There's no happy medium: in any case the self-documentation is wrong and harms the user. This could be "fixed" through a complex set of attributes, in this case something like "argument (a) strchr (const char*a, char)", so the compiler could assume that if it calls it with a non-const argument then it gets a non-const argument in return and vice versa, and the function can be implemented without casting off const. But even keeping these literate contracts to const there are many variations that would require the user to learn a new language to interpret them when simple human-interpreted language more than suffices.
I think the actual "fix" in C++ would be just to define a version of strchr overloaded on const. So you have both: const char* strchr(const char*, char); and char* strchr(char*, char); If you pass it a char* that can be modified, it'll give you back one that can be modified. Otherwise it won't.
And in D we can use a template as the body of the function is the same for const or non-const data, eg. S[] strchr(S : S[], C)(S[], C); Two types S and C are required because sometimes you want to pass a char[] for the first and invariant(char) for the 2nd. Regan
Sep 10 2007
prev sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Bill Baxter" wrote
 Burton Radons wrote:
 A good example is "char* strchr (const char*, char)" from C. That implies 
 that the return value is not a pointer to an index of the first argument, 
 but it is, it's just not declared that way because the argument itself 
 may not be const. So the signature misses the subtlety of the true 
 contract. If the user uses this signature as self-documentation then they 
 could easily make an error. If it were instead "const char* strchr (const 
 char*, char)" then the contract goes beyond what is strictly required 
 because it's so blunt, and interpreted as self-documentation requires the 
 user to make a copy before any modifications of the return value (and 
 makes it impossible to do certain operations). If we decide that we don't 
 want to lie then we can declare it as "char* strchr (char*, char)", which 
 implies that the function modifies the string (because of the absence of 
 const), when it never does; interpreted as self-documentation you'd 
 always call it with mutable data. There's no happy medium: in any case 
 the self-documentation is wrong and harms the user.
I think the actual "fix" in C++ would be just to define a version of strchr overloaded on const. So you have both: const char* strchr(const char*, char); and char* strchr(char*, char); If you pass it a char* that can be modified, it'll give you back one that can be modified. Otherwise it won't. --bb
This is not a "fix" as Burton is describing the constness as a contract for the compiler to interpret. It solves the problem of being able to pass const and non-const data to a function which does the same thing, and having it return the correct argument type. However, the compiler doesn't know that both functions are the same. How would the compiler know that the second function is not going to change any data in the string? Legally, the second function _can_ change data. The challenge is: Define the prototype for strchr in a way that it can be interpreted as not changing the data supplied by the argument, but will return the same type (const or non-const) that you pass in. Oh, and it can't cast away constness in the function, because that would be undefined ;) -Steve
Sep 10 2007
next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
Content-Disposition: inline

I realise this isn't actually meeting the challenge set, but there is one
really easy workaround you could do...

int strchr(const char *p, char c);

Just make the return value the offset from the start of p (or -1 if not
found) instead of a pointer. That way, on return from the function, p[n]
will be const or not const as desired.
Sep 10 2007
prev sibling parent "Janice Caron" <caron800 googlemail.com> writes:
Content-Disposition: inline

OK, I figured it out. Here's how you do it in C.

 #define strchr(p,c) (p + istrchr(p,c))

 int istrchr(const char * p, char c)
 {
     int i;
     for (i=0; p[i]; ++i)
     {
         if (p[i] == c) return i;
     }
     return (const char *)0 - p;
 }


As required, the parameter is const at the callee site, but the output is
the same type as the input at the caller site.
Sep 10 2007