[comp.lang.c] While talking about useful additions, how about this one??

steve@vsi1.UUCP (Steve Maurer) (01/13/88)

[l]
[line]
[lineater]
> > Why is everyone so interested in a power operator compared to, say, 
> > square root or logarithm?
>
> Basically, it's because exponentiation involving one or two integers
> can usually be done much faster than through use of the floating-point
> pow() function, and in the common case **2 can be highly optimized.

    While we are on the subject of useful additions to C, how about this
    one: pre-compiler switching operators '??' and '::', which ask for
    evaluation at compile time, instead of run time.

    For example, if a novice C user wished to create an optimized form of 
    pow(), without knowing the true implementation of it he might write:

    #define POW(X,Y) (Y) == 0.5 ? sqrt(X) : ( (Y) == 0 ? 1 : ( (Y) == 1 ? X :\ 
		     ( (Y) == 2 ? X * X : pow(X,Y) ) ) )

    intending to 'optimize' the resulting code.   Unfortunately, because
    of the way these operators work, the resulting code is horrendous.

    Consider however, the efficiency gained if the operators are evaluated
    at compile time.    Presuming of course that (Y) above is a CONSTANT, 
    various in line efficiencies could be squeezed out of a machine
    without resorting to numerous additions to the language.

    The actual definitions of '??' and '::' would be:

	* if in the precompiler, the expression evaluates to TRUE, then
	  the statement immediately following ?? is produced, else the statment
	  following the :: is produced.   If no :: is present, and the
	  expression value cannot be determined in the precompiler, then
	  no code is produced.

    So in the above example, with all "?" replaced by "??" and ":" replaced
    by "::", the expression POW(i, 20/10) would be replaced by i * i, with no
    comparisons in the resulting assembly.  { Of course you would still
    have to deal with the macro incremental assignment problem -
    e.g. POW(i++,3) - but such is life }

    Ok, boys.     Shoot it down.......

					    Steve Maurer

gwyn@brl-smoke.ARPA (Doug Gwyn ) (01/14/88)

In article <253@vsi1.UUCP> steve@vsi1.UUCP (Steve Maurer) writes:
>    Ok, boys.     Shoot it down.......

Practically any real C compiler will optimize away "dead" expressions
resulting from folding constants.

karl@haddock.ISC.COM (Karl Heuer) (01/14/88)

In article <253@vsi1.UUCP> steve@vsi1.UUCP (Steve Maurer) writes:
>While we are on the subject of useful additions to C, how about this
>one: pre-compiler switching operators '??' and '::', which ask for
>evaluation at compile time, instead of run time.

The proposed syntax is bad: "??" conflicts with trigraph notation (if they're
still in the standard), and "::" conflicts with C++ usage.

>[If one attempts to define a macro POW(X,Y) as
>Y == 0.5 ? sqrt(X) : Y == 0 ? 1 : Y == 1 ? X : Y == 2 ? X * X : pow(X,Y)
>,] the resulting code is horrendous.

If Y is a constant, any decent compiler will do the right thing.  You don't
need a new operator for that.  If I understand your proposal, the only real
enhancement is that "??" treats all non-constants as false.

I counterpropose a builtin "isconstant()", which tests whether its argument is
a compile-time constant.  Thus, "isconstant(Y) && Y == 2 ? X * X : pow(X,Y)".
This is a much smaller change than your proposal, and it's much easier to
squeeze in a new unary operator than a new ternary.

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint
________
Note 1: I intentionally removed the redundant parens to make it easier to
concentrate on the issues at hand.
Note 2: Although "pow" is used in the example, a macro can't solve the pow()
problem.  However, C++ overloading and inlining, combined with isconstant(),
would do the trick.

ned@ghostwheel.UUCP (Ned Nowotny) (01/23/88)

And now for something completely different...

Given a compiler or lint that would understand the construct and issue warnings
about type mismatches, I would like to see "struct *" used as a generic pointer
to a structure.  This is distinct from "void *" in that pointers to structures
generally have the same alignment constraints in all implementations of which I
am aware, but "char" and "short" frequently have different alignment constraints
from other data types.  The problem is that "void *" is a pun for all pointers,
and pointers for all the other basic types (e.g. char, int, long, etc.) are puns
for pointers to any objects of the same type. "struct *" (and maybe even
"union *") could provide a similar generality for inhomogeneous aggregate data
types. (As opposed to arrays which are homogeneous aggregate data types.  Too
bad they aren't first class citizens. Well, it's too late now.  Or is it?
ANSI C maybe? Nahh! ;-) )

-- 

Ned Nowotny (ned@ghostwheel.aca.mcc.com.UUCP)

gwyn@brl-smoke.ARPA (Doug Gwyn ) (01/25/88)

In article <127@ghostwheel.UUCP> ned@ghostwheel.aca.mcc.com.UUCP (Ned Nowotny) writes:
>... I would like to see "struct *" used as a generic pointer to a structure.

I didn't understand your "explanation", but I don't see what good this
would be.  It would seem to provide an unwanted escape from strict typing.
Different structure types really ought not to have their pointers mixed
up by the programmer.

karl@haddock.ISC.COM (Karl Heuer) (01/25/88)

In article <127@ghostwheel.UUCP> ned@ghostwheel.aca.mcc.com.UUCP (Ned Nowotny) writes:
>... I would like to see "struct *" used as a generic pointer to a structure.
>This is distinct from "void *" in that pointers to structures generally have
>the same alignment constraints in all implementations of which I am aware,

Most implementations I've seen will give a struct the alignment of its
strictest member.  Thus, struct{char} and struct{int} may have different
alignment constraints.  However, I believe the dpANS requires that all struct
pointers must be interchangable anyway, so this does not invalidate your
suggestion.

How would you use such a feature, if you had it?

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint

gwyn@brl-smoke.ARPA (Doug Gwyn ) (01/26/88)

In article <2376@haddock.ISC.COM> karl@haddock.ima.isc.com (Karl Heuer) writes:
>Thus, struct{char} and struct{int} may have different alignment constraints.

I used to think that, too, but a few weeks ago after discussion with
Dennis Ritchie I realized that all struct pointers have to "smell" the
same, which implies that they might as well have the same alignment.
The argument had to do with use of an incomplete struct type declaration
in order to accomplish mutual reference in two struct declarations.

The utility of maintaining a distinction between different struct types
is simply for compile-time type checking, which is VERY helpful.

ned@ghostwheel.UUCP (Ned Nowotny) (02/05/88)

In article <7169@brl-smoke.ARPA> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>In article <127@ghostwheel.UUCP> ned@ghostwheel.aca.mcc.com.UUCP (Ned Nowotny) writes:
>>... I would like to see "struct *" used as a generic pointer to a structure.
>
>I didn't understand your "explanation", but I don't see what good this
>would be.  It would seem to provide an unwanted escape from strict typing.

Well, its not "unwanted" and "strict typing" doesn't seem to apply to C. ;-)

>Different structure types really ought not to have their pointers mixed
>up by the programmer.

I don't want to mix pointers to different structure types, but I do want
to write generic code which manipulates complex objects (e.g. linked list
managers).  Currently, this requires declaring pointers as "char *" or
"long *" (worse because its not even a convention, but possibly more
correct when considering data alignment for a given implementation) in K&R C
compilers, or "void *" in ANSI psuedo-compatible C compilers.  I do not find
either of these options particularly satisfying.  "void *" is too
indiscriminating with regard to structure alignment and "char *" or "long *"
discriminate too much (besides, I want a pointer to a stucture of some
kind, not a char or a long).  "struct *" would suppress unwarrented complaints
about possible integer/structure or character/structure alignment differences
from lint or my compiler.  Any remaining possible alignment problems would then
be more likely to be treated as real problems and not line noise.

The nice thing about this proposal is that it breaks absolutely no existing
code, does not add any new keywords, and provides some additional type
information to C's typing system.

As a rational, consider that given:

int a, b;

int *a_ptr;

declares a pointer which may freely point to either "a" or "b" even though
the name implies that it should only point to "a".  Despite the name,
"a_ptr" is a generic integer pointer although "a" and "b" may be conceptually
different objects.

In fact, given:

typedef int a_int;
typedef int b_int;

a_int a;
b_int b;

int   *ptr;
a_int *a_ptr;

ptr   = &a;
a_ptr = &b;

neither lint nor my compiler complain of type mismatches although they should
if C supported strict typing.  (This compiler is a BSD C compiler running on
a VAX 11/750, but it is no different than any of a number of C compilers which
I have used.)  Of course, it is frequently desirable to have a generic pointer
to an integer (a good thing because its all you get).  In the same sense, it can
be desirable to have a generic pointer to complex objects (i.e. structures).  A
"type" cast will be necessary to get at a structure's members through such a
pointer, but this is generally not necessary where a "struct *" pointer would
be used.  After all, the intent is to have a handle on a some complex object,
not to manipulate the object.

-- 

Ned Nowotny (ned@ghostwheel.aca.mcc.com.UUCP)

karl@haddock.ISC.COM (Karl Heuer) (02/10/88)

In article <131@ghostwheel.UUCP> ned@ghostwheel.aca.mcc.com.UUCP (Ned Nowotny) writes:
>[Re "struct *" for a generic pointer to a structure]
>I don't want to mix pointers to different structure types, but I do want
>to write generic code which manipulates complex objects (e.g. linked list
>managers).

How about writing "struct generic *", where "struct generic" is never defined?
This is already syntactically and semantically legal.

>As a rational, consider that given "int a, b;", "int *a_ptr;" declares a
>pointer which may freely point to either "a" or "b"

Yes; so "int *" is a pointer to any int.  But it can't point to a short int or
a long int.  The analogy is pretty weak, I'd say.

>In fact, given: "typedef int a_int; typedef int b_int;" [both cc and lint
>allow mixing of the two types].

That's because typedef creates a new name for an existing type, rather than a
new type.  You want dimensional analysis?  I'm working on it.

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint

gwyn@brl-smoke.ARPA (Doug Gwyn ) (02/11/88)

In article <2528@haddock.ISC.COM> karl@haddock.ima.isc.com (Karl Heuer) writes:
>How about writing "struct generic *", where "struct generic" is never defined?
>This is already syntactically and semantically legal.

Is that really true?  I think technically you're supposed to have to
have pre-declared the incomplete type:
	struct generic;
and that if the type is not later completed it's supposed to be an error.