Gale Paeper wrote:
Peter N Lewis wrote:
This is good, but it does have the disadvantage/risk that it uses the input parameter four times and thus is at risk if the input parameter is a function call (which at best will be called four times and at worst will cause side effects which could introduce hideous bugs).
Provided the FourCharCode type in MacTypes.pas is changed to be a UInt32 type, I don't think there will be much, if any, need to use the macro on function call results.
I hope so.
If there in fact is (should be) no need to, then it may be useful to protect the macro against being used with a non-constant argument.
Peter and I have discussed by private mail that this may already suffice. (Of course, if there's a rare case where it's needed for a non-constant value, one could write a function for it. It would have to have a different name, though. But if the compiler complained if the macro is used with a non-constant argument or the function used in a constant context, this should be no big deal.)
So, how to ensure that? One could include into the macro a builtin function call which requires a constant argument. The only ones that do (AFAIK) are ReturnAddress/FrameAddress. The following actually seems to work:
... + 0 * PtrInt (ReturnAddress (Ord (Length (s) = MaxInt)))
(Note: The condition in `Ord' must "almost" always be false, without the compiler being able to deduce it for a non-constant because some platforms don't accept a nonzero argument to ReturnAddress.)
Of course, that's very kludgy. So we might want to add a new builtin function for that.
One candidate would be a compile-time assert function. This would also cover another open issue (not complete open since compile-time assertions can be achieved by things like `const Assert1 = 1 / Ord (Condition);' which will produce a compile-time error if Condition is false; but that works only if a constant declaration is possible, i.e. not with expressions).
A compile-time assert function would make this more elegant and work for the above case as well. It could work as follows:
- If the (first) argument is not a compile-time constant or does not have the value true, it gives a compile-time error. (So one can check another condition here; in the macro, the obvious thing to check would be Length (s) = 4.)
Otherwise, it yields true.
- Perhaps (optionally?) a second argument which is returned rather than true, so one could do:
{ User can change these. m must be >= n. } const m = 100; n = 10;
var a: array [1 .. Assertion (m >= n, m)] of Integer;
- Name of the function? `Assert' is already taken for runtime assertions. I've used `Assertion' above, but it doesn't really make the difference clear.
Adriaan van Os wrote:
Peter N Lewis wrote:
Unfortunately, both aren't suitable for case constants, as in Peter's example. (Though I don't know if they're frequently used that way.)
To speak for myself, no. I looked into a typical Mac OS program with 300.000 lines of code. It had 443 case statements, but none of them used FourCharCodes as case constants.
They become a lot more prevalent in Mac OS X carbon events code and NIB, as you use them for all the commands and status displays and controls and such.
In the CarbonEvents.pas unit, we could write something like this:
const kEventParamMenuRef : FourCharCode = 'menu'; kEventParamMenuRefWord = Word32( kEventParamMenuRef);
Now, you can use "kEventParamMenuRefWord" in case statements.
Oops. This actually shouldn't work I think. (kEventParamMenuRef is not ordinal, so a value type cast doesn't apply, and a variable type cast shouldn't be usable for a constant value.) So I'll probably forbid it in the next version.
Sure. But I mean we'd have to make a decision first how to handle it. Should it be always big-endian (like Peter's macro), or use the actual target's endianness (so it gets the same memory layout as the character array)? With the macro, this decision is left to the programmer. (Though it might not actually matter if the same conversion is used for all values.)
I would say, the first character of the character array should always come first in memory, which implies that the target's endianness determines the word value.
In this case the macro needs to use the endianness conditionals.
Frank