Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #145807 > unrolled thread
| Started by | 3queens@shabbyroad.com |
|---|---|
| First post | 2019-08-27 17:03 +0000 |
| Last post | 2019-09-06 14:23 +0200 |
| Articles | 20 on this page of 63 — 20 participants |
Back to article view | Back to comp.lang.c
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 →
| From | 3queens@shabbyroad.com |
|---|---|
| Date | 2019-08-27 17:03 +0000 |
| Subject | in 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]
| From | Jorgen Grahn <grahn+nntp@snipabacken.se> |
|---|---|
| Date | 2019-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]
| From | Andrew Smallshaw <andrews@sdf.org> |
|---|---|
| Date | 2019-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]
| From | Jorgen Grahn <grahn+nntp@snipabacken.se> |
|---|---|
| Date | 2019-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]
| From | Malcolm McLean <malcolm.arthur.mclean@gmail.com> |
|---|---|
| Date | 2019-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]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2019-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]
| From | Bart <bc@freeuk.com> |
|---|---|
| Date | 2019-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]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2019-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]
| From | Malcolm McLean <malcolm.arthur.mclean@gmail.com> |
|---|---|
| Date | 2019-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]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2019-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]
| From | Bart <bc@freeuk.com> |
|---|---|
| Date | 2019-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]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2019-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]
| From | Malcolm McLean <malcolm.arthur.mclean@gmail.com> |
|---|---|
| Date | 2019-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]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2019-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]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2019-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]
| From | Bart <bc@freeuk.com> |
|---|---|
| Date | 2019-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]
| From | jadill33@gmail.com |
|---|---|
| Date | 2019-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]
| From | Ike Naar <ike@iceland.freeshell.org> |
|---|---|
| Date | 2019-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]
| From | Bart <bc@freeuk.com> |
|---|---|
| Date | 2019-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]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2019-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