RFC: allowing compound assignment operators with designated initializers

classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

RFC: allowing compound assignment operators with designated initializers

Rasmus Villemoes-2
This is something I've sometimes found myself wishing was supported. The
idea being that one can say

unsigned a[] = { [0] = 1, [1] = 3, [0] |= 4, ...}

which would end up initializing a[0] to 5. As a somewhat realistic
example, suppose one is trying to build a bitmap at compile time, but
the bits to set are not really known in the sense that one can group
those belonging to each index in a usual | expression. Something like

#define _(e) [e / 8] |= 1 << (e % 8)
const u8 error_bitmap[] = { _(EINVAL), _(ENAMETOOLONG), _(EBUSY), ... }

Writing a small program to generate such a table as part of the build is
not practical in a cross-compile setting (because the constants may only
really be known to the cross-compiler, e.g. the errno values above).

I think the rules would be rather intuitive: If a compound assignment is
used for an element that doesn't have a previous ordinary initializer,
the LHS is 0. Any later ordinary initializer wipes all previous
operations. No operator precedence; the new value is computed
immediately and used as LHS in subsequent operations.

I'm not sure how to define what happens in unions, but I also don't even
know what the current rules are in a case like

union u { char c; int i; } = { .i = 0x11223344, .c = 0x55 }

where one initializes a smaller member after a larger.

Another issue is how to handle side effects in the RHS. It's probably
consistent with the current behaviour to discard all side effects prior
to the last ordinary initializer, and to do the side effects from all
the expressions that did end up affecting the final value. (Btw., the
current documentation doesn't talk about how this interacts with range
initializers, e.g. [0...5] = x++, [2...6] = y++, [0...4] = z++, does x++
happen?) But for automatic variables, one might as well do the compound
operations in code after the declaration, so it would be fine just
allowing this extension for static initialization.

Rasmus
Reply | Threaded
Open this post in threaded view
|

Re: RFC: allowing compound assignment operators with designated initializers

Florian Weimer
* Rasmus Villemoes:

> This is something I've sometimes found myself wishing was supported. The
> idea being that one can say
>
> unsigned a[] = { [0] = 1, [1] = 3, [0] |= 4, ...}
>
> which would end up initializing a[0] to 5. As a somewhat realistic
> example, suppose one is trying to build a bitmap at compile time, but
> the bits to set are not really known in the sense that one can group
> those belonging to each index in a usual | expression. Something like
>
> #define _(e) [e / 8] |= 1 << (e % 8)
> const u8 error_bitmap[] = { _(EINVAL), _(ENAMETOOLONG), _(EBUSY), ... }

I think it wouldn't be too hard to extend std::bitset with more
compile-time operations to support this, if that's what you need.

> Writing a small program to generate such a table as part of the build is
> not practical in a cross-compile setting (because the constants may only
> really be known to the cross-compiler, e.g. the errno values above).

This is not a problem if you use GCC anyway because you can use inline
assembly quite reliably to extract arbitrary compile-time constants.
Search for gen-as-const-headers in the glibc sources for an example.
Reply | Threaded
Open this post in threaded view
|

Re: RFC: allowing compound assignment operators with designated initializers

Jonathan Wakely-4
On Sun, 14 Oct 2018 at 20:46, Florian Weimer <[hidden email]> wrote:

>
> * Rasmus Villemoes:
>
> > This is something I've sometimes found myself wishing was supported. The
> > idea being that one can say
> >
> > unsigned a[] = { [0] = 1, [1] = 3, [0] |= 4, ...}
> >
> > which would end up initializing a[0] to 5. As a somewhat realistic
> > example, suppose one is trying to build a bitmap at compile time, but
> > the bits to set are not really known in the sense that one can group
> > those belonging to each index in a usual | expression. Something like
> >
> > #define _(e) [e / 8] |= 1 << (e % 8)
> > const u8 error_bitmap[] = { _(EINVAL), _(ENAMETOOLONG), _(EBUSY), ... }
>
> I think it wouldn't be too hard to extend std::bitset with more
> compile-time operations to support this, if that's what you need.

It's already doable using C++17:

template<int... N>
constexpr auto
make_error_bitmap()
{
  using std::uint8_t;
  using std::array;
  constexpr auto max_index = std::max_element({N...}) / 8;
  array<uint8_t, max_index+1> a;
  [[maybe_unused]] uint8_t sink[] = { a[N/8] |= (1 << (N%8)), ... };
  return a;
}

constexpr uint8_t error_bitmap = make_error_bitmap<EINVAL,
ENAMETOOLONG, EBUSY>();

(This won't compile in C++14 because std::array can't be modified in a
constant expression until C++17).

Of course the response will be "but I don't want to use C++" ...
Reply | Threaded
Open this post in threaded view
|

Re: RFC: allowing compound assignment operators with designated initializers

Florian Weimer
* Jonathan Wakely:

> On Sun, 14 Oct 2018 at 20:46, Florian Weimer <[hidden email]> wrote:
>>
>> * Rasmus Villemoes:
>>
>> > This is something I've sometimes found myself wishing was supported. The
>> > idea being that one can say
>> >
>> > unsigned a[] = { [0] = 1, [1] = 3, [0] |= 4, ...}
>> >
>> > which would end up initializing a[0] to 5. As a somewhat realistic
>> > example, suppose one is trying to build a bitmap at compile time, but
>> > the bits to set are not really known in the sense that one can group
>> > those belonging to each index in a usual | expression. Something like
>> >
>> > #define _(e) [e / 8] |= 1 << (e % 8)
>> > const u8 error_bitmap[] = { _(EINVAL), _(ENAMETOOLONG), _(EBUSY), ... }
>>
>> I think it wouldn't be too hard to extend std::bitset with more
>> compile-time operations to support this, if that's what you need.
>
> It's already doable using C++17:

I didn't doubt that, it's just that I'd expect to be able to use
std::bitset for this.

> template<int... N>
> constexpr auto
> make_error_bitmap()
> {
>   using std::uint8_t;
>   using std::array;
>   constexpr auto max_index = std::max_element({N...}) / 8;
>   array<uint8_t, max_index+1> a;
>   [[maybe_unused]] uint8_t sink[] = { a[N/8] |= (1 << (N%8)), ... };
>   return a;
> }
>
> constexpr uint8_t error_bitmap = make_error_bitmap<EINVAL,
> ENAMETOOLONG, EBUSY>();
>
> (This won't compile in C++14 because std::array can't be modified in a
> constant expression until C++17).

You wrote that without testing it?  I'm impressed.  It's really close.

template<int... N>
constexpr auto
make_error_bitmap()
{
  using std::uint8_t;
  using std::array;
  constexpr auto max_index = std::max({ N... });
  array<uint8_t, max_index+1> a{};
  [[maybe_unused]] uint8_t sink[] = { a[N/8] |= (1 << (N%8)) ... };
  return a;
}

constexpr auto error_bitmap = make_error_bitmap<EINVAL, ENAMETOOLONG, EBUSY>();

It seems to produce the intended bit pattern.

> Of course the response will be "but I don't want to use C++" ...

Indeed.
Reply | Threaded
Open this post in threaded view
|

Re: RFC: allowing compound assignment operators with designated initializers

Gabriel Paubert
On Mon, Oct 15, 2018 at 08:11:42PM +0200, Florian Weimer wrote:

> * Jonathan Wakely:
>
> > On Sun, 14 Oct 2018 at 20:46, Florian Weimer <[hidden email]> wrote:
> >>
> >> * Rasmus Villemoes:
> >>
> >> > This is something I've sometimes found myself wishing was supported. The
> >> > idea being that one can say
> >> >
> >> > unsigned a[] = { [0] = 1, [1] = 3, [0] |= 4, ...}
> >> >
> >> > which would end up initializing a[0] to 5. As a somewhat realistic
> >> > example, suppose one is trying to build a bitmap at compile time, but
> >> > the bits to set are not really known in the sense that one can group
> >> > those belonging to each index in a usual | expression. Something like
> >> >
> >> > #define _(e) [e / 8] |= 1 << (e % 8)
> >> > const u8 error_bitmap[] = { _(EINVAL), _(ENAMETOOLONG), _(EBUSY), ... }
> >>
> >> I think it wouldn't be too hard to extend std::bitset with more
> >> compile-time operations to support this, if that's what you need.
> >
> > It's already doable using C++17:
>
> I didn't doubt that, it's just that I'd expect to be able to use
> std::bitset for this.
>
> > template<int... N>
> > constexpr auto
> > make_error_bitmap()
> > {
> >   using std::uint8_t;
> >   using std::array;
> >   constexpr auto max_index = std::max_element({N...}) / 8;
> >   array<uint8_t, max_index+1> a;
> >   [[maybe_unused]] uint8_t sink[] = { a[N/8] |= (1 << (N%8)), ... };
> >   return a;
> > }
> >
> > constexpr uint8_t error_bitmap = make_error_bitmap<EINVAL,
> > ENAMETOOLONG, EBUSY>();
> >
> > (This won't compile in C++14 because std::array can't be modified in a
> > constant expression until C++17).
>
> You wrote that without testing it?  I'm impressed.  It's really close.
>
> template<int... N>
> constexpr auto
> make_error_bitmap()
> {
>   using std::uint8_t;
>   using std::array;
>   constexpr auto max_index = std::max({ N... });
>   array<uint8_t, max_index+1> a{};
>   [[maybe_unused]] uint8_t sink[] = { a[N/8] |= (1 << (N%8)) ... };
>   return a;
> }
>

Hmm, isn't the array roughly 8 times too large?

IOW, shouldn't you declare "array<uint8_t, (max_index+7)/8> a{};" ?

> constexpr auto error_bitmap = make_error_bitmap<EINVAL, ENAMETOOLONG, EBUSY>();
>
> It seems to produce the intended bit pattern.

Did you think of big-endian machines (just curious)?

        Gabriel
>
> > Of course the response will be "but I don't want to use C++" ...
>
> Indeed.
Reply | Threaded
Open this post in threaded view
|

Re: RFC: allowing compound assignment operators with designated initializers

Jonathan Wakely-4
On Mon, 15 Oct 2018 at 20:08, Gabriel Paubert <[hidden email]> wrote:

>
> On Mon, Oct 15, 2018 at 08:11:42PM +0200, Florian Weimer wrote:
> > * Jonathan Wakely:
> >
> > > On Sun, 14 Oct 2018 at 20:46, Florian Weimer <[hidden email]> wrote:
> > >>
> > >> * Rasmus Villemoes:
> > >>
> > >> > This is something I've sometimes found myself wishing was supported. The
> > >> > idea being that one can say
> > >> >
> > >> > unsigned a[] = { [0] = 1, [1] = 3, [0] |= 4, ...}
> > >> >
> > >> > which would end up initializing a[0] to 5. As a somewhat realistic
> > >> > example, suppose one is trying to build a bitmap at compile time, but
> > >> > the bits to set are not really known in the sense that one can group
> > >> > those belonging to each index in a usual | expression. Something like
> > >> >
> > >> > #define _(e) [e / 8] |= 1 << (e % 8)
> > >> > const u8 error_bitmap[] = { _(EINVAL), _(ENAMETOOLONG), _(EBUSY), ... }
> > >>
> > >> I think it wouldn't be too hard to extend std::bitset with more
> > >> compile-time operations to support this, if that's what you need.
> > >
> > > It's already doable using C++17:
> >
> > I didn't doubt that, it's just that I'd expect to be able to use
> > std::bitset for this.
> >
> > > template<int... N>
> > > constexpr auto
> > > make_error_bitmap()
> > > {
> > >   using std::uint8_t;
> > >   using std::array;
> > >   constexpr auto max_index = std::max_element({N...}) / 8;
> > >   array<uint8_t, max_index+1> a;
> > >   [[maybe_unused]] uint8_t sink[] = { a[N/8] |= (1 << (N%8)), ... };
> > >   return a;
> > > }
> > >
> > > constexpr uint8_t error_bitmap = make_error_bitmap<EINVAL,
> > > ENAMETOOLONG, EBUSY>();
> > >
> > > (This won't compile in C++14 because std::array can't be modified in a
> > > constant expression until C++17).
> >
> > You wrote that without testing it?  I'm impressed.  It's really close.
> >
> > template<int... N>
> > constexpr auto
> > make_error_bitmap()
> > {
> >   using std::uint8_t;
> >   using std::array;
> >   constexpr auto max_index = std::max({ N... });
> >   array<uint8_t, max_index+1> a{};
> >   [[maybe_unused]] uint8_t sink[] = { a[N/8] |= (1 << (N%8)) ... };
> >   return a;
> > }
> >
>
> Hmm, isn't the array roughly 8 times too large?

Yes, it looks like I pasted the wrong version of  the code, which is
why  it had the stray comma that Florian corrected. The final version
of the code I actually wrote is:

#include <array>
#include <algorithm>
#include <cstdint>

template<int... N>
constexpr auto
make_error_bitmap()
{
  using std::uint8_t;
  using std::array;
  constexpr auto max_index = std::max({N...}) / 8;
  array<uint8_t, max_index+1> a{};
  [[maybe_unused]] uint8_t sink[] = { a[N/8] |= (1 << (N%8)) ... };
  return a;
}

constexpr auto error_bitmap = make_error_bitmap<EINVAL, ENAMETOOLONG, EBUSY>();

(Note that max_index has the division by 8)


>
> IOW, shouldn't you declare "array<uint8_t, (max_index+7)/8> a{};" ?
>
> > constexpr auto error_bitmap = make_error_bitmap<EINVAL, ENAMETOOLONG, EBUSY>();
> >
> > It seems to produce the intended bit pattern.
>
> Did you think of big-endian machines (just curious)?

The code does what the OP asked for, I didn't try to figure out if
what it did made sense.
Reply | Threaded
Open this post in threaded view
|

Re: RFC: allowing compound assignment operators with designated initializers

Gabriel Paubert
On Mon, Oct 15, 2018 at 08:13:19PM +0100, Jonathan Wakely wrote:

> On Mon, 15 Oct 2018 at 20:08, Gabriel Paubert <[hidden email]> wrote:
> >
> > On Mon, Oct 15, 2018 at 08:11:42PM +0200, Florian Weimer wrote:
> > > * Jonathan Wakely:
> > >
> > > > On Sun, 14 Oct 2018 at 20:46, Florian Weimer <[hidden email]> wrote:
> > > >>
> > > >> * Rasmus Villemoes:
> > > >>
> > > >> > This is something I've sometimes found myself wishing was supported. The
> > > >> > idea being that one can say
> > > >> >
> > > >> > unsigned a[] = { [0] = 1, [1] = 3, [0] |= 4, ...}
> > > >> >
> > > >> > which would end up initializing a[0] to 5. As a somewhat realistic
> > > >> > example, suppose one is trying to build a bitmap at compile time, but
> > > >> > the bits to set are not really known in the sense that one can group
> > > >> > those belonging to each index in a usual | expression. Something like
> > > >> >
> > > >> > #define _(e) [e / 8] |= 1 << (e % 8)
> > > >> > const u8 error_bitmap[] = { _(EINVAL), _(ENAMETOOLONG), _(EBUSY), ... }
> > > >>
> > > >> I think it wouldn't be too hard to extend std::bitset with more
> > > >> compile-time operations to support this, if that's what you need.
> > > >
> > > > It's already doable using C++17:
> > >
> > > I didn't doubt that, it's just that I'd expect to be able to use
> > > std::bitset for this.
> > >
> > > > template<int... N>
> > > > constexpr auto
> > > > make_error_bitmap()
> > > > {
> > > >   using std::uint8_t;
> > > >   using std::array;
> > > >   constexpr auto max_index = std::max_element({N...}) / 8;
> > > >   array<uint8_t, max_index+1> a;
> > > >   [[maybe_unused]] uint8_t sink[] = { a[N/8] |= (1 << (N%8)), ... };
> > > >   return a;
> > > > }
> > > >
> > > > constexpr uint8_t error_bitmap = make_error_bitmap<EINVAL,
> > > > ENAMETOOLONG, EBUSY>();
> > > >
> > > > (This won't compile in C++14 because std::array can't be modified in a
> > > > constant expression until C++17).
> > >
> > > You wrote that without testing it?  I'm impressed.  It's really close.
> > >
> > > template<int... N>
> > > constexpr auto
> > > make_error_bitmap()
> > > {
> > >   using std::uint8_t;
> > >   using std::array;
> > >   constexpr auto max_index = std::max({ N... });
> > >   array<uint8_t, max_index+1> a{};
> > >   [[maybe_unused]] uint8_t sink[] = { a[N/8] |= (1 << (N%8)) ... };
> > >   return a;
> > > }
> > >
> >
> > Hmm, isn't the array roughly 8 times too large?
>
> Yes, it looks like I pasted the wrong version of  the code, which is
> why  it had the stray comma that Florian corrected. The final version
> of the code I actually wrote is:
>
> #include <array>
> #include <algorithm>
> #include <cstdint>
>
> template<int... N>
> constexpr auto
> make_error_bitmap()
> {
>   using std::uint8_t;
>   using std::array;
>   constexpr auto max_index = std::max({N...}) / 8;
>   array<uint8_t, max_index+1> a{};
>   [[maybe_unused]] uint8_t sink[] = { a[N/8] |= (1 << (N%8)) ... };
>   return a;
> }
>

Looks like it no more wastes memory now.

> constexpr auto error_bitmap = make_error_bitmap<EINVAL, ENAMETOOLONG, EBUSY>();
>
> (Note that max_index has the division by 8)
>
>
> >
> > IOW, shouldn't you declare "array<uint8_t, (max_index+7)/8> a{};" ?
> >

And this expression had an off-by-one error, sorry.

> > > constexpr auto error_bitmap = make_error_bitmap<EINVAL, ENAMETOOLONG, EBUSY>();
> > >
> > > It seems to produce the intended bit pattern.
> >
> > Did you think of big-endian machines (just curious)?
>
> The code does what the OP asked for, I didn't try to figure out if
> what it did made sense.

Ok.

        Gabriel