user-defined literal for integral_constant

291 views
Skip to first unread message

John McFarlane

unread,
Sep 13, 2017, 6:09:16 PM9/13/17
to ISO C++ Standard - Future Proposals
I'm finding increasing need for a user-defined literal that returns an `integral_constant` such as [1] and [2].  Is there appetite for a proposal?  Is anyone else working on this?
Thanks,
John

morw...@gmail.com

unread,
Sep 14, 2017, 5:15:35 PM9/14/17
to ISO C++ Standard - Future Proposals
Sure a feature would surely be great, but now that template<auto>, I guess that we could have a whole new better class, mostly compatible with the previous one:
* template<auto> struct constant;
* User-defined literals like the ones you proposed
* 8c.run(callable); could be a fun addition to run a callable a given number of times too :)

The idea of a template<auto> constant isn't new though, see LWG2922: https://cplusplus.github.io/LWG/lwg-active.html#2922

John McFarlane

unread,
Sep 15, 2017, 12:49:05 AM9/15/17
to ISO C++ Standard - Future Proposals
Thanks for drawing my attention to this. I had not considered `template<auto>` before yesterday.

It sounds like there is a desire for the `constant` you mentioned which avoids some of the limitations of `integral_constant`, e.g. having arithmetic operations result in `constexpr` values -- rather than `constant` objects. Also, I'm a lot less inclined to pursue a UDL for `integral_constant` if its long-term appeal is in question: literal suffixes are a precious commodity!

floria...@gmail.com

unread,
Sep 15, 2017, 4:17:48 AM9/15/17
to ISO C++ Standard - Future Proposals

Le jeudi 14 septembre 2017 00:09:16 UTC+2, John McFarlane a écrit :

 I completely agree that we have to have an easy way to write integral constants, but I think this is *not* the way we should go.
Of course, this kind of proposal is easy and can be used in a transient state.

Let me explain what I have in mind:

Every literal (at the grammar level) has a special type for literals. So when you write `1`, you don't write an `int` but write a `std::integral_constant<int, 1>` that you can add to other integral constants. It is automatically and transparently convertible into an `int`.
One last thing for this to work: `auto i = 1;` and `decltype(1)` should deduce `int` and *not* `std::integral_constant<int, 1>` (I think there is a proposal to customize auto deduction for a class, but I don't remember it).
It is also important that this also works when floating points literal (There is some proposal about floats in template arguments) and string literal.
For string literal, I would say it would be the opportunity to string literals not to be `const char*` or `const char (&) [N]` but have a proper type.

We don't have to use specifically `std::integral_constant`, but as we already have a class that has the meaning we want, so why not use it?

Another way to do it even better (in my opinion) would be the ability to have type specifier (like `constexpr`?) telling you if the type is a compile time constant that you could overload on it and use a template parameters even if it comes from a function argument. That way, it would be possible to reduce the number of actual types during compilation, and would also work on user defined types. I think there also are some proposals in that direction.

I think the important thing is the ability to handle literals *transparently* without having to say that a particular expression is a literal.

Whatever direction is chosen, I think it would be good to get rid of the `const char*` or `const char(&)[N]` for string literals. Those are a nightmare to manipulate within meta-programs.

John McFarlane

unread,
Sep 15, 2017, 4:47:01 AM9/15/17
to ISO C++ Standard - Future Proposals, floria...@gmail.com
On Friday, September 15, 2017 at 1:17:48 AM UTC-7, floria...@gmail.com wrote:

I completely agree that we have to have an easy way to write integral constants, but I think this is *not* the way we should go.
Of course, this kind of proposal is easy and can be used in a transient state.

Let me explain what I have in mind:

Every literal (at the grammar level) has a special type for literals. So when you write `1`, you don't write an `int` but write a `std::integral_constant<int, 1>` that you can add to other integral constants. It is automatically and transparently convertible into an `int`.
One last thing for this to work: `auto i = 1;` and `decltype(1)` should deduce `int` and *not* `std::integral_constant<int, 1>` (I think there is a proposal to customize auto deduction for a class, but I don't remember it).


It is also important that this also works when floating points literal (There is some proposal about floats in template arguments) and string literal.
For string literal, I would say it would be the opportunity to string literals not to be `const char*` or `const char (&) [N]` but have a proper type.

Yes, what works for fundamental integers currently should be ready to accept other types as they become permitted as non-type template parameters in future.

We don't have to use specifically `std::integral_constant`, but as we already have a class that has the meaning we want, so why not use it?

I am already dissuaded from using `integral_constant`. As Morwenn pointed out, `integral_constant` requires spelling out the type of the constant value whereas `template<auto>` now makes that unnecessary.

Another way to do it even better (in my opinion) would be the ability to have type specifier (like `constexpr`?) telling you if the type is a compile time constant that you could overload on it and use a template parameters even if it comes from a function argument. That way, it would be possible to reduce the number of actual types during compilation, and would also work on user defined types. I think there also are some proposals in that direction.

I think the important thing is the ability to handle literals *transparently* without having to say that a particular expression is a literal.

I agree that the less there is a distinction the better. But I'm specifically looking for a mechanism with which to determine the type of objects from a value. Achieving that may be far beyond the scope I'm thinking of.

An example of the kind of thing I'd like to do is:

    auto a = narrowest_int { 120 };  // sizeof(a) == 1
    auto b = narrowest_int { 10000 };  // sizeof(b) == 2
    auto c = narrowest_int { i };  // if narrowest_int is deduced from an int variable, there is no way to know how compact c can be and sizeof(int) must be assumed


Whatever direction is chosen, I think it would be good to get rid of the `const char*` or `const char(&)[N]` for string literals. Those are a nightmare to manipulate within meta-programs.

That would avoid a lot of inconvenience but it's a language change. I think I can achieve what I'm after with a library addition.

Florian Lemaitre

unread,
Sep 15, 2017, 5:53:22 AM9/15/17
to John McFarlane, ISO C++ Standard - Future Proposals

Le vendredi 15 septembre 2017 10:47:01 UTC+2, John McFarlane a écrit :
On Friday, September 15, 2017 at 1:17:48 AM UTC-7, floria...@gmail.com wrote:

I completely agree that we have to have an easy way to write integral constants, but I think this is *not* the way we should go.
Of course, this kind of proposal is easy and can be used in a transient state.

Let me explain what I have in mind:

Every literal (at the grammar level) has a special type for literals. So when you write `1`, you don't write an `int` but write a `std::integral_constant<int, 1>` that you can add to other integral constants. It is automatically and transparently convertible into an `int`.
One last thing for this to work: `auto i = 1;` and `decltype(1)` should deduce `int` and *not* `std::integral_constant<int, 1>` (I think there is a proposal to customize auto deduction for a class, but I don't remember it).


Exactly that one.
 

It is also important that this also works when floating points literal (There is some proposal about floats in template arguments) and string literal.
For string literal, I would say it would be the opportunity to string literals not to be `const char*` or `const char (&) [N]` but have a proper type.

Yes, what works for fundamental integers currently should be ready to accept other types as they become permitted as non-type template parameters in future.

For string literals, some people use `template <char... Chars> struct whatever;`. But for now, we are out of luck about floats.
 

We don't have to use specifically `std::integral_constant`, but as we already have a class that has the meaning we want, so why not use it?

I am already dissuaded from using `integral_constant`. As Morwenn pointed out, `integral_constant` requires spelling out the type of the constant value whereas `template<auto>` now makes that unnecessary.

Whatever, any type is fine here. `integral_constant` just makes overload a bit easier (when you want to overload for a type, but not a specific value).
 

Another way to do it even better (in my opinion) would be the ability to have type specifier (like `constexpr`?) telling you if the type is a compile time constant that you could overload on it and use a template parameters even if it comes from a function argument. That way, it would be possible to reduce the number of actual types during compilation, and would also work on user defined types. I think there also are some proposals in that direction.

I think the important thing is the ability to handle literals *transparently* without having to say that a particular expression is a literal.

I agree that the less there is a distinction the better. But I'm specifically looking for a mechanism with which to determine the type of objects from a value. Achieving that may be far beyond the scope I'm thinking of.

An example of the kind of thing I'd like to do is:

    auto a = narrowest_int { 120 };  // sizeof(a) == 1
    auto b = narrowest_int { 10000 };  // sizeof(b) == 2
    auto c = narrowest_int { i };  // if narrowest_int is deduced from an int variable, there is no way to know how compact c can be and sizeof(int) must be assumed


This example should be fairly simple to implement with `integral_constant`-like part of what I suggested. (you can even keep the code as you have written with template parameter deduction for classes and template deduction guides)
For the `constexpr` type specifier, this would imply being able to return a different type if the arguments have the same type but not the same value (this could be fine only for compile time computation).
 

Whatever direction is chosen, I think it would be good to get rid of the `const char*` or `const char(&)[N]` for string literals. Those are a nightmare to manipulate within meta-programs.

That would avoid a lot of inconvenience but it's a language change. I think I can achieve what I'm after with a library addition.

The literal type could be defined as followed:
template <auto val>
struct literal {
   constexpr literal() = default;
   constexpr literal(const literal&) = default;
   ~literal() = default;

    constexpr operator decltype(val)() const { return val; }

    //using auto = decltype(val);
};

template <auto V1, auto V2> requires(requires{V1 + V2;})
constexpr literal<V1 + V2> operator+(literal<V1>, literal<V2>) { return {}; }
// ... other operators

Then, your example would look like:
constexpr std::size_t log2ll(std::size_t);
constexpr std::size_t next_power2(std::size_t);

template <std::size_t>
struct narrower_int;

template <>
struct narrower_int<1> {
   std::int8_t i;
   using auto = std::int8_t;
   constexpr operator std::int8_t() const { return i; }
};
template <>
struct narrower_int<2> {
   std::int16_t i;
   using auto = std::int16_t;
   constexpr operator std::int16_t() const { return i; }
};
// ... other sizes

template <class T> requires(std::is_integral<T>::value)
narrower_int(T) -> narrower_int<sizeof(T)>;

template <auto val> requires(std::is_integral<decltype(val)>::value)
narrower_int(literal<val>) -> narrower_int<next_power2(1+(1+log2ll(val))/8)>;

// It is also possible to do it with functions

So if literals are automatically of type `template <auto> struct literal`, your code immediately works.
Otherwise, you need extra syntax to transform an actual literal into an object of type `literal`. This could be done by `literal<1>` or by a user-defined literal.

Of course, I'm in favor of the first one.
 

John McFarlane

unread,
Sep 25, 2017, 4:32:57 PM9/25/17
to Florian Lemaitre, ISO C++ Standard - Future Proposals
Reply all
Reply to author
Forward
0 new messages