Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #145807 > unrolled thread

in place swap of float types

Started by3queens@shabbyroad.com
First post2019-08-27 17:03 +0000
Last post2019-09-06 14:23 +0200
Articles 20 on this page of 63 — 20 participants

Back to article view | Back to comp.lang.c


Contents

  in place swap of float types 3queens@shabbyroad.com - 2019-08-27 17:03 +0000
    Re: in place swap of float types Jorgen Grahn <grahn+nntp@snipabacken.se> - 2019-08-27 19:22 +0000
      Re: in place swap of float types Andrew Smallshaw <andrews@sdf.org> - 2019-09-02 15:25 +0000
        Re: in place swap of float types Jorgen Grahn <grahn+nntp@snipabacken.se> - 2019-09-02 19:37 +0000
          Re: in place swap of float types Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2019-09-03 06:37 -0700
            Re: in place swap of float types David Brown <david.brown@hesbynett.no> - 2019-09-03 16:13 +0200
              Re: in place swap of float types Bart <bc@freeuk.com> - 2019-09-03 16:54 +0100
                Re: in place swap of float types David Brown <david.brown@hesbynett.no> - 2019-09-04 11:19 +0200
                  Re: in place swap of float types Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2019-09-04 02:35 -0700
                    Re: in place swap of float types David Brown <david.brown@hesbynett.no> - 2019-09-04 12:31 +0200
                  Re: in place swap of float types Bart <bc@freeuk.com> - 2019-09-04 12:12 +0100
                    Re: in place swap of float types David Brown <david.brown@hesbynett.no> - 2019-09-04 15:53 +0200
                      Re: in place swap of float types Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2019-09-04 07:17 -0700
                        Re: in place swap of float types David Brown <david.brown@hesbynett.no> - 2019-09-05 15:12 +0200
                          Re: in place swap of float types Ben Bacarisse <ben.usenet@bsb.me.uk> - 2019-09-05 21:11 +0100
                      Re: in place swap of float types Bart <bc@freeuk.com> - 2019-09-04 16:30 +0100
                        Re: in place swap of float types jadill33@gmail.com - 2019-09-04 10:26 -0700
                        Re: in place swap of float types Ike Naar <ike@iceland.freeshell.org> - 2019-09-05 06:00 +0000
                          Re: in place swap of float types Bart <bc@freeuk.com> - 2019-09-05 11:14 +0100
                        Re: in place swap of float types David Brown <david.brown@hesbynett.no> - 2019-09-05 14:54 +0200
                          Re: in place swap of float types Bart <bc@freeuk.com> - 2019-09-05 15:48 +0100
                            Re: in place swap of float types David Brown <david.brown@hesbynett.no> - 2019-09-06 10:35 +0200
                              Re: in place swap of float types Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2019-09-06 02:52 -0700
                                Re: in place swap of float types David Brown <david.brown@hesbynett.no> - 2019-09-06 13:18 +0200
                              Re: in place swap of float types Bart <bc@freeuk.com> - 2019-09-06 12:14 +0100
                                Re: in place swap of float types David Brown <david.brown@hesbynett.no> - 2019-09-06 15:08 +0200
                                  Re: in place swap of float types Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2019-09-06 06:23 -0700
                      Re: in place swap of float types Keith Thompson <kst-u@mib.org> - 2019-09-04 14:16 -0700
                        Re: in place swap of float types Bart <bc@freeuk.com> - 2019-09-04 22:42 +0100
                        Re: in place swap of float types Keith Thompson <kst-u@mib.org> - 2019-09-04 15:21 -0700
                        Re: in place swap of float types David Brown <david.brown@hesbynett.no> - 2019-09-05 15:15 +0200
                Re: in place swap of float types Joe Pfeiffer <pfeiffer@cs.nmsu.edu> - 2019-09-04 08:05 -0600
                  Re: in place swap of float types David Brown <david.brown@hesbynett.no> - 2019-09-05 15:17 +0200
                    Re: in place swap of float types Joe Pfeiffer <pfeiffer@cs.nmsu.edu> - 2019-09-05 20:28 -0600
                      Re: in place swap of float types David Brown <david.brown@hesbynett.no> - 2019-09-06 10:39 +0200
                        Re: in place swap of float types Joe Pfeiffer <pfeiffer@cs.nmsu.edu> - 2019-09-06 08:11 -0600
                          Re: in place swap of float types scott@slp53.sl.home (Scott Lurndal) - 2019-09-06 18:31 +0000
            Re: in place swap of float types Jorgen Grahn <grahn+nntp@snipabacken.se> - 2019-09-04 19:02 +0000
        Re: in place swap of float types David Brown <david.brown@hesbynett.no> - 2019-09-02 22:16 +0200
    Re: in place swap of float types Robert Wessel <robertwessel2@yahoo.com> - 2019-08-27 17:59 -0500
      Re: in place swap of float types Richard Damon <Richard@Damon-Family.org> - 2019-08-27 17:45 -0700
      Re: in place swap of float types James Kuyper <jameskuyper@alumni.caltech.edu> - 2019-08-27 20:48 -0400
        Re: in place swap of float types Tim Rentsch <tr.17687@z991.linuxsc.com> - 2019-09-03 06:17 -0700
      Re: in place swap of float types Ben Bacarisse <ben.usenet@bsb.me.uk> - 2019-08-28 02:09 +0100
      Re: in place swap of float types Siri Cruise <chine.bleu@yahoo.com> - 2019-08-27 21:26 -0700
      Re: in place swap of float types David Brown <david.brown@hesbynett.no> - 2019-08-28 09:22 +0200
      Re: in place swap of float types Andrey Tarasevich <andreytarasevich@hotmail.com> - 2019-08-28 16:36 -0700
    Re: in place swap of float types David Brown <david.brown@hesbynett.no> - 2019-08-28 09:36 +0200
      Re: in place swap of float types Bart <bc@freeuk.com> - 2019-08-28 12:02 +0100
        Re: in place swap of float types James Kuyper <jameskuyper@alumni.caltech.edu> - 2019-08-28 05:08 -0700
          Re: in place swap of float types Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2019-08-28 05:26 -0700
          Re: in place swap of float types David Brown <david.brown@hesbynett.no> - 2019-08-28 14:40 +0200
          Re: in place swap of float types Keith Thompson <kst-u@mib.org> - 2019-08-28 13:01 -0700
      Re: in place swap of float types Philipp Klaus Krause <pkk@spth.de> - 2019-09-04 11:18 +0200
        Re: in place swap of float types David Brown <david.brown@hesbynett.no> - 2019-09-04 12:57 +0200
          Re: in place swap of float types Philipp Klaus Krause <pkk@spth.de> - 2019-09-04 13:24 +0200
    Re: in place swap of float types Bonita Montero <Bonita.Montero@gmail.com> - 2019-09-05 08:18 +0200
      Re: in place swap of float types David Brown <david.brown@hesbynett.no> - 2019-09-05 15:19 +0200
        Re: in place swap of float types Bonita Montero <Bonita.Montero@gmail.com> - 2019-09-05 16:18 +0200
          Re: in place swap of float types Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2019-09-05 07:32 -0700
            Re: in place swap of float types Bonita Montero <Bonita.Montero@gmail.com> - 2019-09-05 17:32 +0200
            Re: in place swap of float types David Brown <david.brown@hesbynett.no> - 2019-09-06 10:54 +0200
              Re: in place swap of float types Bonita Montero <Bonita.Montero@gmail.com> - 2019-09-06 14:23 +0200

Page 1 of 4  [1] 2 3 4  Next page →


#145807 — in place swap of float types

From3queens@shabbyroad.com
Date2019-08-27 17:03 +0000
Subjectin place swap of float types
Message-ID<slrnqmaoja.4b2.3queens@cpe-65-25-200-198.new.res.rr.com>
Some time ago, there was a thread concerning using "a^=b; b^=a; a^=b" 
to do an in place swap of two variables of the same size.  It was
pointed out that this can only be done with integer types.  I found a
way to do it with floating point types as well.

Declare a union with the floating point variable and an equally long
integer variable.  Then use the integer member to do the exclusive-or
process.

Naturally, this would not be efficient if the data had to be copied into
and out of the union, but if the variables were defined as unions to
begin with, this would work.

Maybe not that useful, but if space is really tight, perhaps useful in
some situations.

[toc] | [next] | [standalone]


#145809

FromJorgen Grahn <grahn+nntp@snipabacken.se>
Date2019-08-27 19:22 +0000
Message-ID<slrnqmb0nn.cdo.grahn+nntp@frailea.sa.invalid>
In reply to#145807
On Tue, 2019-08-27, 3queens@shabbyroad.com wrote:
> Some time ago, there was a thread concerning using "a^=b; b^=a; a^=b" 
> to do an in place swap of two variables of the same size.  It was
> pointed out that this can only be done with integer types.  I found a
> way to do it with floating point types as well.
...
> Maybe not that useful, but if space is really tight, perhaps useful in
> some situations.

An optimizing compiler would, I strongly suspect, do a better job with
no weird code involved.  I think it's all an intellectual exercise and
nothing more (or less).

/Jorgen

-- 
  // Jorgen Grahn <grahn@  Oo  o.   .     .
\X/     snipabacken.se>   O  o   .

[toc] | [prev] | [next] | [standalone]


#145905

FromAndrew Smallshaw <andrews@sdf.org>
Date2019-09-02 15:25 +0000
Message-ID<slrn3ha8qmqd2h.t2.andrews@sdf.lonestar.org>
In reply to#145809
On 2019-08-27, Jorgen Grahn <grahn+nntp@snipabacken.se> wrote:
> On Tue, 2019-08-27, 3queens@shabbyroad.com wrote:
>> Some time ago, there was a thread concerning using "a^=b; b^=a; a^=b" 
>> to do an in place swap of two variables of the same size.  It was
>> pointed out that this can only be done with integer types.  I found a
>> way to do it with floating point types as well.
> ...
>> Maybe not that useful, but if space is really tight, perhaps useful in
>> some situations.
>
> An optimizing compiler would, I strongly suspect, do a better job with
> no weird code involved.  I think it's all an intellectual exercise and
> nothing more (or less).

I have long suspected so to, one proviso is that I'd use a dedicated
variable rather than reusing a generic "temporary" variable which
IMHO is bad practice anyway since there is no way to indicate to
the compiler the value doesn't need to be preserved between distinct
uses. To make it as clear as possible declare it with the tightest
scope possible, e.g.

double a, b;

...

{
    double c = a;
	a = b;
	b = c;
}


-- 
Andrew Smallshaw
andrews@sdf.org

[toc] | [prev] | [next] | [standalone]


#145910

FromJorgen Grahn <grahn+nntp@snipabacken.se>
Date2019-09-02 19:37 +0000
Message-ID<slrnqmqrsd.cdo.grahn+nntp@frailea.sa.invalid>
In reply to#145905
On Mon, 2019-09-02, Andrew Smallshaw wrote:
> On 2019-08-27, Jorgen Grahn <grahn+nntp@snipabacken.se> wrote:
>> On Tue, 2019-08-27, 3queens@shabbyroad.com wrote:
>>> Some time ago, there was a thread concerning using "a^=b; b^=a; a^=b" 
>>> to do an in place swap of two variables of the same size.  It was
>>> pointed out that this can only be done with integer types.  I found a
>>> way to do it with floating point types as well.
>> ...
>>> Maybe not that useful, but if space is really tight, perhaps useful in
>>> some situations.
>>
>> An optimizing compiler would, I strongly suspect, do a better job with
>> no weird code involved.  I think it's all an intellectual exercise and
>> nothing more (or less).
>
> I have long suspected so to, one proviso is that I'd use a dedicated
> variable rather than reusing a generic "temporary" variable which
> IMHO is bad practice anyway since there is no way to indicate to
> the compiler the value doesn't need to be preserved between distinct
> uses. To make it as clear as possible declare it with the tightest
> scope possible, e.g.
>
> double a, b;
>
> ...
>
> {
>     double c = a;
> 	a = b;
> 	b = c;
> }

I'd use a function: swap(&a, &b);

I think/assume that would get inlined adequately.

/Jorgen

-- 
  // Jorgen Grahn <grahn@  Oo  o.   .     .
\X/     snipabacken.se>   O  o   .

[toc] | [prev] | [next] | [standalone]


#145926

FromMalcolm McLean <malcolm.arthur.mclean@gmail.com>
Date2019-09-03 06:37 -0700
Message-ID<9e0efdf9-a63a-49e1-af8d-6411ba5e5b9a@googlegroups.com>
In reply to#145910
On Monday, 2 September 2019 20:37:58 UTC+1, Jorgen Grahn  wrote:
> On Mon, 2019-09-02, Andrew Smallshaw wrote:
> > On 2019-08-27, Jorgen Grahn <grahn+nntp@snipabacken.se> wrote:
> >> On Tue, 2019-08-27, 3queens@shabbyroad.com wrote:
> >>> Some time ago, there was a thread concerning using "a^=b; b^=a; a^=b" 
> >>> to do an in place swap of two variables of the same size.  It was
> >>> pointed out that this can only be done with integer types.  I found a
> >>> way to do it with floating point types as well.
> >> ...
> >>> Maybe not that useful, but if space is really tight, perhaps useful in
> >>> some situations.
> >>
> >> An optimizing compiler would, I strongly suspect, do a better job with
> >> no weird code involved.  I think it's all an intellectual exercise and
> >> nothing more (or less).
> >
> > I have long suspected so to, one proviso is that I'd use a dedicated
> > variable rather than reusing a generic "temporary" variable which
> > IMHO is bad practice anyway since there is no way to indicate to
> > the compiler the value doesn't need to be preserved between distinct
> > uses. To make it as clear as possible declare it with the tightest
> > scope possible, e.g.
> >
> > double a, b;
> >
> > ...
> >
> > {
> >     double c = a;
> > 	a = b;
> > 	b = c;
> > }
> 
> I'd use a function: swap(&a, &b);
> 
> I think/assume that would get inlined adequately.
> 
It would be 

swap(&a, &b, sizeof(a));

Then the only way of implementing it portably is via a byte copying loop.
So you're demanding quite a lot of the compiler to see that that is
equivalent to a store to a temporary. It would be interesting to see
if different compilers manage to optimise it.

[toc] | [prev] | [next] | [standalone]


#145932

FromDavid Brown <david.brown@hesbynett.no>
Date2019-09-03 16:13 +0200
Message-ID<qklsej$7o7$1@dont-email.me>
In reply to#145926
On 03/09/2019 15:37, Malcolm McLean wrote:
> On Monday, 2 September 2019 20:37:58 UTC+1, Jorgen Grahn  wrote:
>> On Mon, 2019-09-02, Andrew Smallshaw wrote:
>>> On 2019-08-27, Jorgen Grahn <grahn+nntp@snipabacken.se> wrote:
>>>> On Tue, 2019-08-27, 3queens@shabbyroad.com wrote:
>>>>> Some time ago, there was a thread concerning using "a^=b; b^=a; a^=b" 
>>>>> to do an in place swap of two variables of the same size.  It was
>>>>> pointed out that this can only be done with integer types.  I found a
>>>>> way to do it with floating point types as well.
>>>> ...
>>>>> Maybe not that useful, but if space is really tight, perhaps useful in
>>>>> some situations.
>>>>
>>>> An optimizing compiler would, I strongly suspect, do a better job with
>>>> no weird code involved.  I think it's all an intellectual exercise and
>>>> nothing more (or less).
>>>
>>> I have long suspected so to, one proviso is that I'd use a dedicated
>>> variable rather than reusing a generic "temporary" variable which
>>> IMHO is bad practice anyway since there is no way to indicate to
>>> the compiler the value doesn't need to be preserved between distinct
>>> uses. To make it as clear as possible declare it with the tightest
>>> scope possible, e.g.
>>>
>>> double a, b;
>>>
>>> ...
>>>
>>> {
>>>     double c = a;
>>> 	a = b;
>>> 	b = c;
>>> }
>>
>> I'd use a function: swap(&a, &b);
>>
>> I think/assume that would get inlined adequately.
>>
> It would be 
> 
> swap(&a, &b, sizeof(a));
> 
> Then the only way of implementing it portably is via a byte copying loop.
> So you're demanding quite a lot of the compiler to see that that is
> equivalent to a store to a temporary. It would be interesting to see
> if different compilers manage to optimise it.
> 

"swap" could be a _Generic macro (or, if you allow extensions, a macro
with gcc's "typeof").

I tested this briefly:

static inline void swap_a(void * pa, void * pb, size_t n) {
    unsigned char t[n];
    memcpy(t, pa, n);
    memcpy(pa, pb, n);
    memcpy(pb, t, n);
}

gcc and clang are able to handle that as efficiently as type-specific
swaps for "int" or "double" :

typedef double T;

static inline void swap_b(T * pa, T * pb) {
    T t = *pa;
    *pa = *pb;
    *pb = t;
}


But they did not handle a manual byte copy loop as well:

static inline void swap_c(void * pa, void * pb, size_t n) {
    unsigned char * p = pa;
    unsigned char * q = pb;
    while (n--) {
        unsigned char t = *p;
        *p++ = *q;
        *q++ = t;
    }
}

[toc] | [prev] | [next] | [standalone]


#145937

FromBart <bc@freeuk.com>
Date2019-09-03 16:54 +0100
Message-ID<91wbF.219020$893.137679@fx02.am4>
In reply to#145932
On 03/09/2019 15:13, David Brown wrote:
> On 03/09/2019 15:37, Malcolm McLean wrote:

>> It would be
>>
>> swap(&a, &b, sizeof(a));
>>
>> Then the only way of implementing it portably is via a byte copying loop.
>> So you're demanding quite a lot of the compiler to see that that is
>> equivalent to a store to a temporary. It would be interesting to see
>> if different compilers manage to optimise it.
>>
> 
> "swap" could be a _Generic macro (or, if you allow extensions, a macro
> with gcc's "typeof").
> 
> I tested this briefly:
> 
> static inline void swap_a(void * pa, void * pb, size_t n) {
>      unsigned char t[n];
>      memcpy(t, pa, n);
>      memcpy(pa, pb, n);
>      memcpy(pb, t, n);
> }
> 
> gcc and clang are able to handle that as efficiently as type-specific
> swaps for "int" or "double" :
> 
> typedef double T;
> 
> static inline void swap_b(T * pa, T * pb) {
>      T t = *pa;
>      *pa = *pb;
>      *pb = t;
> }

With this program:

------------------------------------
// typedef long long int T;
  typedef double T;
  extern void bill(void);
  extern T x,y;

  static inline void swap(T * pa, T * pb) {
      T t = *pa;
      *pa = *pb;
      *pb = t;
  }

  int main(void) {
      bill();
      swap(&x,&y);
      bill();
   }
------------------------------------

gcc -O0 generates the actual call as expected. With gcc-O1 to -O3, it in 
inlines the call, but the swap takes 6 instructions for x64 target. 
Slightly different code between int64 and float64, but still 6 instructions.

I had to use extern variables, and to sandwich the swap between extern 
calls, to stop it optimising the code out of existence. Using volatile 
in place of extern generates 4 instructions rather than 6, but volatile 
is not often used in real code.

My own non-optimising compiler (not for C as my C doesn't have swap) 
always generates 4 instructions for built-in swap. (And that swap can be 
written 'swap(x,y)' rather than 'swap(&x,&y)'.

So in all, built-in support for such things is better and can be more 
confidently relied on to get the best code. And each programmer doesn't 
have to re-invent their own sets of functions and macros.

[toc] | [prev] | [next] | [standalone]


#145969

FromDavid Brown <david.brown@hesbynett.no>
Date2019-09-04 11:19 +0200
Message-ID<qknvie$as0$1@dont-email.me>
In reply to#145937
On 03/09/2019 17:54, Bart wrote:
> On 03/09/2019 15:13, David Brown wrote:
>> On 03/09/2019 15:37, Malcolm McLean wrote:
> 
>>> It would be
>>>
>>> swap(&a, &b, sizeof(a));
>>>
>>> Then the only way of implementing it portably is via a byte copying
>>> loop.
>>> So you're demanding quite a lot of the compiler to see that that is
>>> equivalent to a store to a temporary. It would be interesting to see
>>> if different compilers manage to optimise it.
>>>
>>
>> "swap" could be a _Generic macro (or, if you allow extensions, a macro
>> with gcc's "typeof").
>>
>> I tested this briefly:
>>
>> static inline void swap_a(void * pa, void * pb, size_t n) {
>>      unsigned char t[n];
>>      memcpy(t, pa, n);
>>      memcpy(pa, pb, n);
>>      memcpy(pb, t, n);
>> }
>>
>> gcc and clang are able to handle that as efficiently as type-specific
>> swaps for "int" or "double" :
>>
>> typedef double T;
>>
>> static inline void swap_b(T * pa, T * pb) {
>>      T t = *pa;
>>      *pa = *pb;
>>      *pb = t;
>> }
> 
> With this program:
> 
> ------------------------------------
> // typedef long long int T;
>  typedef double T;
>  extern void bill(void);
>  extern T x,y;
> 
>  static inline void swap(T * pa, T * pb) {
>      T t = *pa;
>      *pa = *pb;
>      *pb = t;
>  }
> 
>  int main(void) {
>      bill();
>      swap(&x,&y);
>      bill();
>   }
> ------------------------------------
> 
> gcc -O0 generates the actual call as expected. With gcc-O1 to -O3, it in
> inlines the call, but the swap takes 6 instructions for x64 target.
> Slightly different code between int64 and float64, but still 6
> instructions.
> 
> I had to use extern variables, and to sandwich the swap between extern
> calls, to stop it optimising the code out of existence. Using volatile
> in place of extern generates 4 instructions rather than 6, but volatile
> is not often used in real code.

It's hard to get code that really demonstrates swapping of local
variables - the effect gets hidden in normal register movements.

> 
> My own non-optimising compiler (not for C as my C doesn't have swap)
> always generates 4 instructions for built-in swap. (And that swap can be
> written 'swap(x,y)' rather than 'swap(&x,&y)'.
> 
> So in all, built-in support for such things is better and can be more
> confidently relied on to get the best code. And each programmer doesn't
> have to re-invent their own sets of functions and macros.

As always, I am sceptical about built in functions or features with
generic names like this.  I think it really has to be a very heavily
used feature before it should be a fixed name in a language.  How often
do people really need to swap two objects?  How often will it make a
real difference to the programmer, compared to just writing it manually
with a temporary local variable?

Builtins have several significant costs:

1. They "steal" identifiers.  You can no longer use the word in your own
code.  This is a serious problem for compatibility, and if people want
to use the name for something different ("swap" could mean different
things to different people).  You can avoid this using different names,
or namespaces, depending on the details of the language and your
preferences - __swap, __builtin_swap, std::swap, sys.swap, or whatever
suits.

2. Changes to the feature means changes to the language and the
compiler, rather than just a user-level source file.  It also means
adding to IDE's for syntax highlighting or automatic completion, and
adding to any other tools used for the language.

3. They are a learning cost.  Everyone who knows a language, knows how
to swap by writing "temp = a; a = b; b = temp;".  Every built in feature
and function means something more to learn - all these easy learning
points add up.  This is especially true if there may be consistency
issues (such as some builtins taking parameters by value, others by
reference).


If you think the feature is worth having, add it to your language or
compiler.  But do so because that is the right balance based on how the
language is used - not just because you can.

[toc] | [prev] | [next] | [standalone]


#145971

FromMalcolm McLean <malcolm.arthur.mclean@gmail.com>
Date2019-09-04 02:35 -0700
Message-ID<dea7a03c-8e73-4e28-8ef9-58b82baf3571@googlegroups.com>
In reply to#145969
On Wednesday, 4 September 2019 10:19:17 UTC+1, David Brown  wrote:
> How often
> do people really need to swap two objects?  How often will it make a
> real difference to the programmer, compared to just writing it manually
> with a temporary local variable?
> 
It's often important in C++. You have a temporary, and want to assign it
to a long-term object. Doing it the normal way, you destroy the members
of the long term object, then do a memberwise copy from the temporary to
the long term variable. The temporary is then destroyed as it goes out
of scope or is used for a new object.
It's more efficient to swap the two objects. Usually, that just entails a
few pointer movements. The temporary then contains in effect garbage,
which is destroyed properly when its lifetime ceases.

[toc] | [prev] | [next] | [standalone]


#145974

FromDavid Brown <david.brown@hesbynett.no>
Date2019-09-04 12:31 +0200
Message-ID<qko3qk$2vt$1@dont-email.me>
In reply to#145971
On 04/09/2019 11:35, Malcolm McLean wrote:
> On Wednesday, 4 September 2019 10:19:17 UTC+1, David Brown  wrote:
>> How often
>> do people really need to swap two objects?  How often will it make a
>> real difference to the programmer, compared to just writing it manually
>> with a temporary local variable?
>>
> It's often important in C++. You have a temporary, and want to assign it
> to a long-term object. Doing it the normal way, you destroy the members
> of the long term object, then do a memberwise copy from the temporary to
> the long term variable. The temporary is then destroyed as it goes out
> of scope or is used for a new object.
> It's more efficient to swap the two objects. Usually, that just entails a
> few pointer movements. The temporary then contains in effect garbage,
> which is destroyed properly when its lifetime ceases.
> 

Yes, that is the case with C++ (but not with C).  My question here was
not purely rhetorical - it may be the case for some of the languages
Bart works with, or it may not.

Because there /is/ significant use of "swap" in C++ (especially in
various idioms), and the efficiency can be significantly affected by how
the swap is implemented, it makes sense to have "swap" as a feature in
C++.  And it is much better to have it as a library feature rather than
a builtin, as I suggested.

[toc] | [prev] | [next] | [standalone]


#145977

FromBart <bc@freeuk.com>
Date2019-09-04 12:12 +0100
Message-ID<s_MbF.456041$_l5.198989@fx32.am4>
In reply to#145969
On 04/09/2019 10:19, David Brown wrote:
> On 03/09/2019 17:54, Bart wrote:

>> So in all, built-in support for such things is better and can be more
>> confidently relied on to get the best code. And each programmer doesn't
>> have to re-invent their own sets of functions and macros.
> 
> As always, I am sceptical about built in functions or features with
> generic names like this.  I think it really has to be a very heavily
> used feature before it should be a fixed name in a language.  How often
> do people really need to swap two objects?  How often will it make a
> real difference to the programmer, compared to just writing it manually
> with a temporary local variable?

The incidence of swap() is not high, but it depends on the code: in a 
general code base of mine of 100K lines (mostly language stuff), it 
occurs once in about 4000 lines. That doesn't sound much, but it's 
exactly the same incidence as fopen() is called.

In a specific library, it was once in 500 lines. When you do need it, 
then it is most convenient just to have it available,  instead of 
breaking off in order to reinvent it, which also breaks the flow of the 
code for the reader.

Most uses (2/3 or more) are to swap two locals, the rest swap data in 
memory.

That some instructions include a form of exchange is another reason to 
consider it a language primitive.

> 
> Builtins have several significant costs:
> 
> 1. They "steal" identifiers.  You can no longer use the word in your own
> code.  This is a serious problem for compatibility, and if people want
> to use the name for something different ("swap" could mean different
> things to different people).  You can avoid this using different names,
> or namespaces, depending on the details of the language and your
> preferences - __swap, __builtin_swap, std::swap, sys.swap, or whatever
> suits.

Things like this are better of course:

#define SWAP(type, var1, var2) { type t = var2; var2 = var1; var1 = t; }

(From CPython sources). Which someone will then promptly use like this:

    SWAP(T, A[i++], B[j++])

(And which also introduces the extra problem of needing to keep T 
aligned with the types of A[] and B[])


> 2. Changes to the feature means changes to the language and the
> compiler, rather than just a user-level source file.  It also means
> adding to IDE's for syntax highlighting or automatic completion, and
> adding to any other tools used for the language.
> 
> 3. They are a learning cost.  Everyone who knows a language, knows how
> to swap by writing "temp = a; a = b; b = temp;".

How about reading it? Others might use a different name from 'temp'. 
Sometimes you will see:

    T t = A[i+1]; A[i+1]=B[j+1]; B[i+1]=t;

Is this a swap, is it something else, or is it intended to be a swap but 
has a typo?

> Every built in feature
> and function means something more to learn - all these easy learning
> points add up.

Have a look at C++ sometime. There, the usual argument for its plethora 
of features and paradigms is that you only need to learn and use what 
you need. (How many dozens of methods are associated with std::vector?)

Presumably 'swap' would be one straw too many!

[toc] | [prev] | [next] | [standalone]


#145987

FromDavid Brown <david.brown@hesbynett.no>
Date2019-09-04 15:53 +0200
Message-ID<qkofkt$6m7$1@dont-email.me>
In reply to#145977
On 04/09/2019 13:12, Bart wrote:
> On 04/09/2019 10:19, David Brown wrote:
>> On 03/09/2019 17:54, Bart wrote:
> 
>>> So in all, built-in support for such things is better and can be more
>>> confidently relied on to get the best code. And each programmer doesn't
>>> have to re-invent their own sets of functions and macros.
>>
>> As always, I am sceptical about built in functions or features with
>> generic names like this.  I think it really has to be a very heavily
>> used feature before it should be a fixed name in a language.  How often
>> do people really need to swap two objects?  How often will it make a
>> real difference to the programmer, compared to just writing it manually
>> with a temporary local variable?
> 
> The incidence of swap() is not high, but it depends on the code: in a
> general code base of mine of 100K lines (mostly language stuff), it
> occurs once in about 4000 lines. That doesn't sound much, but it's
> exactly the same incidence as fopen() is called.
> 

I don't think my code would have as many swaps as that.  But then, I
don't think I've used "fopen" more than once or twice in total, in all
my C code.  I don't view my own code as "typical" of C code, however, to
the extent that I even think it makes sense to talk about "typical" C code.

But of course your own code is the best source of statistical samples
for your tools.

At a rate of 1 per 4000 lines, I would not see "swap" as a candidate for
a language feature and keyword, but perhaps for a "standard library"
function.  However, as I understand it you don't have block scope
temporaries in your language, which would make { T t = a; a = b; b = t;}
sequences more inconvenient in your language than in C.  That may affect
the weighting and balance here.


> In a specific library, it was once in 500 lines. When you do need it,
> then it is most convenient just to have it available,  instead of
> breaking off in order to reinvent it, which also breaks the flow of the
> code for the reader.
> 
> Most uses (2/3 or more) are to swap two locals, the rest swap data in
> memory.

If I were making my own language, I'd support multiple assignment.  Then
there would be no need for "swap" as a function, builtin, macro or
anything else - you'd just write "a, b = b, a".

> 
> That some instructions include a form of exchange is another reason to
> consider it a language primitive.

I don't expect a programming language to have that kind of one-to-one
relationship with instruction sets.

> 
>>
>> Builtins have several significant costs:
>>
>> 1. They "steal" identifiers.  You can no longer use the word in your own
>> code.  This is a serious problem for compatibility, and if people want
>> to use the name for something different ("swap" could mean different
>> things to different people).  You can avoid this using different names,
>> or namespaces, depending on the details of the language and your
>> preferences - __swap, __builtin_swap, std::swap, sys.swap, or whatever
>> suits.
> 
> Things like this are better of course:
> 
> #define SWAP(type, var1, var2) { type t = var2; var2 = var1; var1 = t; }
> 

I'd rather have:

#define SWAP(var1, var2) \
	{ typeof(var1) t = var2; var2 = var1; var1 = t; }

Then at least the type is automatic.  (typeof is a gcc'ism, but
supported by a few other compilers too.)

> (From CPython sources). Which someone will then promptly use like this:
> 
>    SWAP(T, A[i++], B[j++])
> 
> (And which also introduces the extra problem of needing to keep T
> aligned with the types of A[] and B[])

That code is a terrible mistake.  The whole point of using all caps for
macro names is so that they scream at you "This is not a normal
function.  Don't use parameters with side effects!".  (This is one
reason I don't use all-caps macro names, /unless/ it is unsafe like this
SWAP.)

It is not a problem with macros or C - it is a problem with the
programmer here, and whoever failed to review the code properly.

There are safer ways to handle a swap macro.  A step forward is :

#define swap(a, b) \
    { typedef typeof(a) T; \
        T* pA = &(a); \
        T* pB = &(b); \
        T t = *pA; \
        *pA = *pB; \
        *pB = t; \
    }

This is safe from side-effects like above.  It will not work if the
parameters are arrays, but at least it will fail with a compile-time
error in such cases - a vast improvement over silent mistakes.


Where possible, prefer "static inline" functions to macros.  You can't
make them type generic, but the parameters are only evaluated once.


C++ templates are better:

template < typename T >
void swap(T &a, T &b) {
    T t = a;
    a = b;
    b = t;
}

With C++ concepts, it is even neater:

void swap(auto &a, auto &b) {
    auto t = a;
    a = b;
    b = t;
}


Don't re-do the weaknesses of C, and especially do not re-do mistakes
others have made in C programming (or programming in any language).  But
equally, don't knee-jerk a reaction to it.  Look at different languages,
and how they solve the problem.  Maybe you want to copy "a, b = b, a"
from Python.  Maybe you want to copy C++ methods.  Maybe you want your
own way.  Just don't assume that "different from C" means "best".

> 
> 
>> 2. Changes to the feature means changes to the language and the
>> compiler, rather than just a user-level source file.  It also means
>> adding to IDE's for syntax highlighting or automatic completion, and
>> adding to any other tools used for the language.
>>
>> 3. They are a learning cost.  Everyone who knows a language, knows how
>> to swap by writing "temp = a; a = b; b = temp;".
> 
> How about reading it? Others might use a different name from 'temp'.
> Sometimes you will see:
> 
>    T t = A[i+1]; A[i+1]=B[j+1]; B[i+1]=t;
> 
> Is this a swap, is it something else, or is it intended to be a swap but
> has a typo?

You cannot eliminate every possible coding error by making something a
builtin feature of the language.  At some point, you have to accept that
if you are going to be a programmer, you have a responsibility to write
code carefully and correctly.

> 
>> Every built in feature
>> and function means something more to learn - all these easy learning
>> points add up.
> 
> Have a look at C++ sometime. There, the usual argument for its plethora
> of features and paradigms is that you only need to learn and use what
> you need. (How many dozens of methods are associated with std::vector?)
> 

std::vector is not part of the language - it is part of the standard
library.  There is a /big/ difference.  Those that want to use a
std::vector can write "#include <vector>" and use it.  Those that don't,
don't - it has no cost at all for those that don't use it.  The same
applies to most aspect of the standard library.

There is no doubt that C++ is a /big/ language, with lots to learn in
it.  I am not denying that!  But if all the standard library - even just
the "utility" stuff - were part of the language itself, it would be
ridiculous in size.

> Presumably 'swap' would be one straw too many!
> 

In C++, "swap" could not be part of the language because it needs to be
modifiable by code.  You have to be able to write your own
specialisations of swap for your own types, if you want to.

[toc] | [prev] | [next] | [standalone]


#145989

FromMalcolm McLean <malcolm.arthur.mclean@gmail.com>
Date2019-09-04 07:17 -0700
Message-ID<fc545887-3c9d-4b6a-befe-0b7976742f89@googlegroups.com>
In reply to#145987
On Wednesday, 4 September 2019 14:53:40 UTC+1, David Brown  wrote:
>
> If I were making my own language, I'd support multiple assignment.  Then
> there would be no need for "swap" as a function, builtin, macro or
> anything else - you'd just write "a, b = b, a".
> 
That's nasty pitfall for the compiler. Asking for a bug where a and b
are set to b.
>  
> > That some instructions include a form of exchange is another reason to
> > consider it a language primitive.
> 
> I don't expect a programming language to have that kind of one-to-one
> relationship with instruction sets.
> 
Historically C does. That's one reason you have ++ as well as += 1. Many
assemblers have an INC instruction that is faster then a load immediate with
value 1 and add sequence.
If you're close to the instruction set, it makes the compiler easier to write,
and it reduces the need for optimisations.
> 
> I'd rather have:
> 
> #define SWAP(var1, var2) \
> 	{ typeof(var1) t = var2; var2 = var1; var1 = t; }
> 
> Then at least the type is automatic.  (typeof is a gcc'ism, but
> supported by a few other compilers too.)
> 
In C, I'd like memswap(void *, void *, size_t)

The advantages:
1) mem... is already in the C standard library namespace.
2) The function works like memcpy and memmove.
3) A one man compiler can easily provide it as a simple byte copying loop.
4) Because it is a standard library function, and the usual pattern will
be to swap two small variables, the compiler can optimise to a register
exchange.
5) You don't need dialects of C which aren't supported everywhere.
6) You can easily swap memory buffers whose size is determined at runtime,
using the same logic.
7) You can implement it without using any RAM.

The disadvantages:
a) You can't easily test for overlapping buffers (identical buffers can be a
no-op).
b) It's less easy to read than a straight identifier "swap".
c) Objects with pointers to internal members become invalid, and again you 
can't easily warn.

[toc] | [prev] | [next] | [standalone]


#146018

FromDavid Brown <david.brown@hesbynett.no>
Date2019-09-05 15:12 +0200
Message-ID<qkr1j1$9b9$1@dont-email.me>
In reply to#145989
On 04/09/2019 16:17, Malcolm McLean wrote:
> On Wednesday, 4 September 2019 14:53:40 UTC+1, David Brown  wrote:
>>
>> If I were making my own language, I'd support multiple assignment.  Then
>> there would be no need for "swap" as a function, builtin, macro or
>> anything else - you'd just write "a, b = b, a".
>>
> That's nasty pitfall for the compiler. Asking for a bug where a and b
> are set to b.

That is something you have to consider, and decide how it will be done.
 It is always the case for swapping, and indeed any other feature of a
language - you have to consider degenerate cases.  (Look at how well the
xor swap works for swapping "a" with "a".)

You also have to consider the meaning of things like "*a++, *b++ = b,
a", or other complicating factors.

>>  
>>> That some instructions include a form of exchange is another reason to
>>> consider it a language primitive.
>>
>> I don't expect a programming language to have that kind of one-to-one
>> relationship with instruction sets.
>>
> Historically C does. That's one reason you have ++ as well as += 1. 

No, that is a myth.  "x++;" predates the famous PDP-11, and is nothing
more or less than a convenient abbreviation for "x = x + 1;".  That is
how it is defined in C, and (to my knowledge) the predecessors of C.

> Many
> assemblers have an INC instruction that is faster then a load immediate with
> value 1 and add sequence.

What you are trying to say, I think, is that many processors have an
"inc" instruction that is shorter than an "add #1" instruction.

And many processors do not.  Also, many processors that have an "add #1"
instruction also have short forms for a few other constants, such as any
signed 4-bit number.

There never has been anything close to a 1-to-1 relationship between
operations, statements, or anything else between C and object code of
any processor.


There certainly have been plenty of C compilers that do mostly a "direct
translation" turning expressions and statements in C source into
sequences of object code in a very direct manner.  This has never been
an expectation or requirement in C, but is the hallmark of simple,
cheap, quickly written, low quality C compilers.  (Such tools may be
entirely appropriate and useful despite generating sub-optimal object code.)

> If you're close to the instruction set, it makes the compiler easier to write,
> and it reduces the need for optimisations.

C is low level, not "close to the instruction set".  "Direct
translations" makes a compiler easier to write, but does not help anyone
in any other way.  It typically means the C programmer has to work
harder if they want efficient object code, learning how the compiler
works, and writing a compiler-oriented source rather than source that is
logical, readable and maintainable.  I've used such limited compilers -
it is a step up from programming in assembly, but a step below using a
decent C compiler.

>>
>> I'd rather have:
>>
>> #define SWAP(var1, var2) \
>> 	{ typeof(var1) t = var2; var2 = var1; var1 = t; }
>>
>> Then at least the type is automatic.  (typeof is a gcc'ism, but
>> supported by a few other compilers too.)
>>
> In C, I'd like memswap(void *, void *, size_t)
> 
> The advantages:
> 1) mem... is already in the C standard library namespace.

If we are talking about adding a feature to the standard library, then
that makes sense for naming.

> 2) The function works like memcpy and memmove.
> 3) A one man compiler can easily provide it as a simple byte copying loop.
> 4) Because it is a standard library function, and the usual pattern will
> be to swap two small variables, the compiler can optimise to a register
> exchange.

Yes, that's true.

But I believe we were talking about user-written code, /not/ adding
something to the compiler.  Having a function like this is likely to be
inefficient even if the function definition is visible to the compiler,
and will definitely be inefficient if it is in a separate translation
unit.  There is no point in inventing a "swap" that is inefficient, and
since a function implementation is likely to be inefficient, the choice
falls on a macro.

> 5) You don't need dialects of C which aren't supported everywhere.
> 6) You can easily swap memory buffers whose size is determined at runtime,
> using the same logic.
> 7) You can implement it without using any RAM.
> 
> The disadvantages:
> a) You can't easily test for overlapping buffers (identical buffers can be a
> no-op).

You compare the two pointers - it's not hard.

> b) It's less easy to read than a straight identifier "swap".

I disagree.

> c) Objects with pointers to internal members become invalid, and again you 
> can't easily warn.
> 

That would be going well beyond the level of sophistication you would
expect in C.  When you want something smarter, use a different language
(like C++ and its std::swap).  In C, you expect to handle such
situations manually.

[toc] | [prev] | [next] | [standalone]


#146038

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2019-09-05 21:11 +0100
Message-ID<87a7bizeoe.fsf@bsb.me.uk>
In reply to#146018
David Brown <david.brown@hesbynett.no> writes:

> On 04/09/2019 16:17, Malcolm McLean wrote:
>> On Wednesday, 4 September 2019 14:53:40 UTC+1, David Brown  wrote:
<cut>
>>> I don't expect a programming language to have that kind of one-to-one
>>> relationship with instruction sets.
>>>
>> Historically C does. That's one reason you have ++ as well as += 1. 
>
> No, that is a myth.  "x++;" predates the famous PDP-11, and is nothing
> more or less than a convenient abbreviation for "x = x + 1;". That is
> how it is defined in C, and (to my knowledge) the predecessors of C.

Yup.  Pretty much the only reason C has ++ and -- is because Thompson
thought it was a neat idea when designing B.  The += forms (originally
=+) come from Algol 68 where they could be written as either +:= or +=:
depending on which side you put the target.

(Aside: Some Algol 68 versions had "displacement assignments", rather
clumsily written :=:=, +:=:=, -:=:= and so on.  These assign the new
value but return the old one, so C's x++ was x +:=:= 1 in Algol 68C.
The plain assignment version was handy when fiddling with linked lists.)

-- 
Ben.

[toc] | [prev] | [next] | [standalone]


#145991

FromBart <bc@freeuk.com>
Date2019-09-04 16:30 +0100
Message-ID<pMQbF.165925$Xs5.5927@fx06.am4>
In reply to#145987
On 04/09/2019 14:53, David Brown wrote:
> On 04/09/2019 13:12, Bart wrote:

>> The incidence of swap() is not high, but it depends on the code: in a
>> general code base of mine of 100K lines (mostly language stuff), it
>> occurs once in about 4000 lines. That doesn't sound much, but it's
>> exactly the same incidence as fopen() is called.
>>
> 
> I don't think my code would have as many swaps as that.  But then, I
> don't think I've used "fopen" more than once or twice in total, in all
> my C code.  I don't view my own code as "typical" of C code, however, to
> the extent that I even think it makes sense to talk about "typical" C code.
> 
> But of course your own code is the best source of statistical samples
> for your tools.
> 
> At a rate of 1 per 4000 lines, I would not see "swap" as a candidate for
> a language feature and keyword, but perhaps for a "standard library"
> function.

The same code base had a slightly lower incidence of XOR (^ or ^= in C) 
yet XOR is common in languages. And MAX occurred as frequently as SWAP 
(MAX is also part of some instruction sets).

   However, as I understand it you don't have block scope
> temporaries in your language, which would make { T t = a; a = b; b = t;}
> sequences more inconvenient in your language than in C.  That may affect
> the weighting and balance here.

In interpreted byte-code languages, optimisation is difficult. Then, 
expressing your intention directly can mean fewer byte-codes and faster 
execution. And when the interpreter sees a 'swap' byte-code, it can make 
that very efficient even for large data.

And, once 'swap' is used in one language, you want to use it in others too.

>> Most uses (2/3 or more) are to swap two locals, the rest swap data in
>> memory.
> 
> If I were making my own language, I'd support multiple assignment.  Then
> there would be no need for "swap" as a function, builtin, macro or
> anything else - you'd just write "a, b = b, a".

I have multiple assignment too. There, using (a,b):=(b,a) also generates 
four instructions like swap(a,b).

But with more complex terms like (a[i],b[j+1):=(b[j+1],a), each term has 
to be written twice, duplication that the compiler now has to try and 
eliminate; it brings up the issue of side-effects; it risks extra typos; 
and it means you might not be able to see at a glance if this is indeed 
a swap, or whether something else is intended.

The real test is this: if you were writing pseudo-code to exchange A[i] 
with A[i+1], which of these would you write:

    T temp=A[i]; A[i]=A[i+1]; A[i+1]=A

    A[i], A[i+1] = A[i+1], A[i]

    swap(A[i], A[i+1])

in order to most clearly express what is intended? Pseudo-code ought to 
be a more common inspiration for language designers.

> 
> There are safer ways to handle a swap macro.  A step forward is :
> 
> #define swap(a, b) \
>      { typedef typeof(a) T; \
>          T* pA = &(a); \
>          T* pB = &(b); \
>          T t = *pA; \
>          *pA = *pB; \
>          *pB = t; \
>      }
> 
> This is safe from side-effects like above.  It will not work if the
> parameters are arrays, but at least it will fail with a compile-time
> error in such cases - a vast improvement over silent mistakes.

> Where possible, prefer "static inline" functions to macros.  You can't
> make them type generic, but the parameters are only evaluated once.
> 
> 
> C++ templates are better:
> 
> template < typename T >
> void swap(T &a, T &b) {
>      T t = a;
>      a = b;
>      b = t;
> }
> 
> With C++ concepts, it is even neater:
> 
> void swap(auto &a, auto &b) {
>      auto t = a;
>      a = b;
>      b = t;
> }
> 

It looks like you acknowledge that 'swap' can be useful, but don't want 
it as part of a language, yet you demonstrate several ways of doing just 
that.

As I said, it would be simplest as a built-in, then all the problems 
with duplication, side-effects, typos, generics and clarity are all 
taken care of. Even the name pollution is reduced to one common 
identifier across a million programmers.

[toc] | [prev] | [next] | [standalone]


#145994

Fromjadill33@gmail.com
Date2019-09-04 10:26 -0700
Message-ID<c87d3d30-0322-4017-8b0f-a184409b570c@googlegroups.com>
In reply to#145991
On Wednesday, September 4, 2019 at 11:30:38 AM UTC-4, Bart wrote:
> On 04/09/2019 14:53, David Brown wrote:
> > On 04/09/2019 13:12, Bart wrote:
> 
> >> The incidence of swap() is not high, but it depends on the code: in a
> >> general code base of mine of 100K lines (mostly language stuff), it
> >> occurs once in about 4000 lines. That doesn't sound much, but it's
> >> exactly the same incidence as fopen() is called.
> >>
> > 
> > I don't think my code would have as many swaps as that.  But then, I
> > don't think I've used "fopen" more than once or twice in total, in all
> > my C code.  I don't view my own code as "typical" of C code, however, to
> > the extent that I even think it makes sense to talk about "typical" C code.
> > 
> > But of course your own code is the best source of statistical samples
> > for your tools.
> > 
> > At a rate of 1 per 4000 lines, I would not see "swap" as a candidate for
> > a language feature and keyword, but perhaps for a "standard library"
> > function.
> 
> The same code base had a slightly lower incidence of XOR (^ or ^= in C) 
> yet XOR is common in languages. And MAX occurred as frequently as SWAP 
> (MAX is also part of some instruction sets).
> 
>    However, as I understand it you don't have block scope
> > temporaries in your language, which would make { T t = a; a = b; b = t;}
> > sequences more inconvenient in your language than in C.  That may affect
> > the weighting and balance here.
> 
> In interpreted byte-code languages, optimisation is difficult. Then, 
> expressing your intention directly can mean fewer byte-codes and faster 
> execution. And when the interpreter sees a 'swap' byte-code, it can make 
> that very efficient even for large data.
> 
> And, once 'swap' is used in one language, you want to use it in others too.
> 
> >> Most uses (2/3 or more) are to swap two locals, the rest swap data in
> >> memory.
> > 
> > If I were making my own language, I'd support multiple assignment.  Then
> > there would be no need for "swap" as a function, builtin, macro or
> > anything else - you'd just write "a, b = b, a".
> 
> I have multiple assignment too. There, using (a,b):=(b,a) also generates 
> four instructions like swap(a,b).
> 
> But with more complex terms like (a[i],b[j+1):=(b[j+1],a), each term has 
> to be written twice, duplication that the compiler now has to try and 
> eliminate; it brings up the issue of side-effects; it risks extra typos; 
> and it means you might not be able to see at a glance if this is indeed 
> a swap, or whether something else is intended.
> 
> The real test is this: if you were writing pseudo-code to exchange A[i] 
> with A[i+1], which of these would you write:
> 
>     T temp=A[i]; A[i]=A[i+1]; A[i+1]=A
> 
>     A[i], A[i+1] = A[i+1], A[i]
> 
>     swap(A[i], A[i+1])
> 
> in order to most clearly express what is intended? Pseudo-code ought to 
> be a more common inspiration for language designers.
> 
> > 
> > There are safer ways to handle a swap macro.  A step forward is :
> > 
> > #define swap(a, b) \
> >      { typedef typeof(a) T; \
> >          T* pA = &(a); \
> >          T* pB = &(b); \
> >          T t = *pA; \
> >          *pA = *pB; \
> >          *pB = t; \
> >      }
> > 
> > This is safe from side-effects like above.  It will not work if the
> > parameters are arrays, but at least it will fail with a compile-time
> > error in such cases - a vast improvement over silent mistakes.
> 
> > Where possible, prefer "static inline" functions to macros.  You can't
> > make them type generic, but the parameters are only evaluated once.
> > 
> > 
> > C++ templates are better:
> > 
> > template < typename T >
> > void swap(T &a, T &b) {
> >      T t = a;
> >      a = b;
> >      b = t;
> > }
> > 
> > With C++ concepts, it is even neater:
> > 
> > void swap(auto &a, auto &b) {
> >      auto t = a;
> >      a = b;
> >      b = t;
> > }
> > 
> 
> It looks like you acknowledge that 'swap' can be useful, but don't want 
> it as part of a language, yet you demonstrate several ways of doing just 
> that.
> 
> As I said, it would be simplest as a built-in, then all the problems 
> with duplication, side-effects, typos, generics and clarity are all 
> taken care of. Even the name pollution is reduced to one common 
> identifier across a million programmers.

The name pollution is in my opinion the most compelling argument for
standardizing swap capability.

You can write wrappers, but it's a bit of a pain.

static inline uint16_t bswap16( uint16_t x )
{
#if defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) && !defined(__STRICT_ANSI__)
  return __builtin_bswap16( x );
#elif defined(_MSC_VER) && (_MSC_VER >= 1310)
  return _byteswap_ushort( x );
#else
  return (x >> 8) | (x << 8);
#endif
}

I think most people would get by with a 16, 32, and 64-bit size swap, and
some kind of "void * memswap( void *lhs, void *rhs, size_t size )".  I don't
personally see a reason why some interface with this capability couldn't or
shouldn't be standardized.

Best regards,
John D.

[toc] | [prev] | [next] | [standalone]


#146004

FromIke Naar <ike@iceland.freeshell.org>
Date2019-09-05 06:00 +0000
Message-ID<slrn3tfsqn193c.g4a.ike@iceland.freeshell.org>
In reply to#145991
On 2019-09-04, Bart <bc@freeuk.com> wrote:
> The real test is this: if you were writing pseudo-code to exchange A[i] 
> with A[i+1], which of these would you write:
>
>     T temp=A[i]; A[i]=A[i+1]; A[i+1]=A

      T temp=A[i]; A[i]=A[i+1]; A[i+1]=temp

[toc] | [prev] | [next] | [standalone]


#146010

FromBart <bc@freeuk.com>
Date2019-09-05 11:14 +0100
Message-ID<Rd5cF.854420$c8w1.114845@fx08.am4>
In reply to#146004
On 05/09/2019 07:00, Ike Naar wrote:
> On 2019-09-04, Bart <bc@freeuk.com> wrote:
>> The real test is this: if you were writing pseudo-code to exchange A[i]
>> with A[i+1], which of these would you write:
>>
>>      T temp=A[i]; A[i]=A[i+1]; A[i+1]=A
> 
>        T temp=A[i]; A[i]=A[i+1]; A[i+1]=temp
> 

You've just highlighted one of the problems of that idiom. (And the typo 
wasn't deliberate. It even took me a few seconds to realise that what 
you typed and what I typed weren't the same.)

[toc] | [prev] | [next] | [standalone]


#146016

FromDavid Brown <david.brown@hesbynett.no>
Date2019-09-05 14:54 +0200
Message-ID<qkr0i2$322$1@dont-email.me>
In reply to#145991
On 04/09/2019 17:30, Bart wrote:
> On 04/09/2019 14:53, David Brown wrote:
>> On 04/09/2019 13:12, Bart wrote:
> 
>>> The incidence of swap() is not high, but it depends on the code: in a
>>> general code base of mine of 100K lines (mostly language stuff), it
>>> occurs once in about 4000 lines. That doesn't sound much, but it's
>>> exactly the same incidence as fopen() is called.
>>>
>>
>> I don't think my code would have as many swaps as that.  But then, I
>> don't think I've used "fopen" more than once or twice in total, in all
>> my C code.  I don't view my own code as "typical" of C code, however, to
>> the extent that I even think it makes sense to talk about "typical" C
>> code.
>>
>> But of course your own code is the best source of statistical samples
>> for your tools.
>>
>> At a rate of 1 per 4000 lines, I would not see "swap" as a candidate for
>> a language feature and keyword, but perhaps for a "standard library"
>> function.
> 
> The same code base had a slightly lower incidence of XOR (^ or ^= in C)
> yet XOR is common in languages. And MAX occurred as frequently as SWAP
> (MAX is also part of some instruction sets).
> 

There is always a balance in these things, and a matter of choices.  One
important metric is to ask how often the feature is needed (around the
same usage rate, for your statistics, but that may not match statistics
for C).  Another is to ask how you can get the results using the
existing language.  "x ^ y" is quite awkward to duplicate when you need
it but don't have an xor operation - "(x | y) & ~(x & y)".  Swap and max
are simpler.  And when considering the use of names or operator symbols,
think about how often the feature will be used in complex expressions.

Again, I am not saying "do it like C", or "don't make "swap" a built in
function in your language".  I am merely encouraging that you think
about it, and make considered decisions on a solid basis.  The focus
should be on what is best for the programmer, the user of the language.
 I feel that sometimes your focus is on showing off the simplicity of
your compiler (adding features just because it is easy to do so), or on
picking features precisely because C does not have that feature.  As
justifications for features, these are not much better than saying you
are adding the <^v> "min-max-swap" operator because you had a dream
about it.  And I know you don't want to go there!


Depending on the expected usage domains of your language, I think it
would be reasonable to relegate "xor" to a standard library function
too, leaving the ^ symbol for a power operator, or re-direction operator
(like in Pascal).


>   However, as I understand it you don't have block scope
>> temporaries in your language, which would make { T t = a; a = b; b = t;}
>> sequences more inconvenient in your language than in C.  That may affect
>> the weighting and balance here.
> 
> In interpreted byte-code languages, optimisation is difficult. Then,
> expressing your intention directly can mean fewer byte-codes and faster
> execution. And when the interpreter sees a 'swap' byte-code, it can make
> that very efficient even for large data.
> 
> And, once 'swap' is used in one language, you want to use it in others too.
> 
>>> Most uses (2/3 or more) are to swap two locals, the rest swap data in
>>> memory.
>>
>> If I were making my own language, I'd support multiple assignment.  Then
>> there would be no need for "swap" as a function, builtin, macro or
>> anything else - you'd just write "a, b = b, a".
> 
> I have multiple assignment too. There, using (a,b):=(b,a) also generates
> four instructions like swap(a,b).
> 
> But with more complex terms like (a[i],b[j+1):=(b[j+1],a), each term has
> to be written twice, duplication that the compiler now has to try and
> eliminate; it brings up the issue of side-effects; it risks extra typos;
> and it means you might not be able to see at a glance if this is indeed
> a swap, or whether something else is intended.

It is all about the level of usage.  It's fine for complex actions to
require complex code, especially if it is used only on occasion.

(And, as I have mentioned before, good use of spaces will make your code
hugely more legible.)

To me, multiple assignment removes the need for a special "swap" feature
for handling common cases.  I would want a "swap" function if it were
designed to handle more complex types (such as handling "deep swaps" of
trees or lists), and there the critical point is that it must be
extendable and overloaded by user code - /not/ a compiler built-in fixed
function.

> 
> The real test is this: if you were writing pseudo-code to exchange A[i]
> with A[i+1], which of these would you write:
> 
>    T temp=A[i]; A[i]=A[i+1]; A[i+1]=A
> 
>    A[i], A[i+1] = A[i+1], A[i]
> 
>    swap(A[i], A[i+1])
> 
> in order to most clearly express what is intended? Pseudo-code ought to
> be a more common inspiration for language designers.
> 

There is no doubt about the order of clarity there.  But I don't agree
that this is "the real test".  It is certainly an important
consideration, but it is not the only one.

>>
>> There are safer ways to handle a swap macro.  A step forward is :
>>
>> #define swap(a, b) \
>>      { typedef typeof(a) T; \
>>          T* pA = &(a); \
>>          T* pB = &(b); \
>>          T t = *pA; \
>>          *pA = *pB; \
>>          *pB = t; \
>>      }
>>
>> This is safe from side-effects like above.  It will not work if the
>> parameters are arrays, but at least it will fail with a compile-time
>> error in such cases - a vast improvement over silent mistakes.
> 
>> Where possible, prefer "static inline" functions to macros.  You can't
>> make them type generic, but the parameters are only evaluated once.
>>
>>
>> C++ templates are better:
>>
>> template < typename T >
>> void swap(T &a, T &b) {
>>      T t = a;
>>      a = b;
>>      b = t;
>> }
>>
>> With C++ concepts, it is even neater:
>>
>> void swap(auto &a, auto &b) {
>>      auto t = a;
>>      a = b;
>>      b = t;
>> }
>>
> 
> It looks like you acknowledge that 'swap' can be useful, but don't want
> it as part of a language, yet you demonstrate several ways of doing just
> that.
> 

I entirely agree that it can be useful.  And if /you/ think it is
important enough to be part of /your/ language, that is /your/ choice.

I am happy to see it as part of a standard library, I am less keen on it
being built into the language.  I see that as having lots of
disadvantages and very few advantages.

> As I said, it would be simplest as a built-in, then all the problems
> with duplication, side-effects, typos, generics and clarity are all
> taken care of. Even the name pollution is reduced to one common
> identifier across a million programmers.
> 

No, you don't take care of everything with a builtin "swap".  You take
care of /some/ things, yes.  But not others.

You can have both ways, of course - and get the maximum benefits.  You
can have "__builtin_swap(a, b)" which does a pure memory swap of "a" and
"b" in the most optimised way.  And then you could have your standard
library include:

namespace sys {
	void swap(auto &a, auto &b) {
		__builtin_swap(a, b);
	}
}

Then people can use "sys::swap" for swapping, getting the most efficient
possible code for normal types.  And they can override it for types that
need something more complex.  They can have "using namespeace sys;" or
"using sys::swap;" somewhere in their code, and then use the name "swap"
as though it were part of the language - yet it is still magically
overrideable.  They can use "swap" as their own identifier and still
access "sys::swap" or "__builtin_swap" according to preference.


(Of course you might want a different syntax for namespaces than C++,
but I would definitely recommend having something equivalent.  Lack of
namespaces is a big disadvantage of C, IMHO, and not one to copy.  It
may or may not be combined with a concept of modules.)

[toc] | [prev] | [next] | [standalone]


Page 1 of 4  [1] 2 3 4  Next page →

Back to top | Article view | comp.lang.c


csiph-web