Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

User defined literals

53 views
Skip to first unread message

restor

unread,
Apr 21, 2009, 5:55:19 PM4/21/09
to
Hi,
I am trying to imagine how defining compile-time-evaluated user
defined literals would look like. Suppose I want literals that
represent hours and minutes as one piece of data like 11:59. Obviously
due to using a colon I can only use string literal like:

"11:59"_hour

Underscore required for user defined literals. Additionaly, to be a
decent literal it needs to be a constexpr. My attempt:

template< char c1, char c2, char c3, char c4, char c5 > constexpr
hour_t operator "" _hour()
{
static_assert( c1 >= '0' && c1 <= '2', "bad char[0]" );
static_assert( c2 >= '0' && c2 <= '9', "bad char[1]" );
static_assert( c3 = ':', "char[2] not :" );
static_assert( c4 >= '0' && c4 <= '5', "bad char[3]" );
static_assert( c5 >= '0' && c5 <= '9', "bad char[4]" );

return hour_t(
(c1 - '0') * 600 +
(c2 - '0') * 60 +
(c4 - '0') * 10 +
(c5 - '0')
);
}

It will fail to compile, because constexpr function is not a single
return statement. My second attempt:

template< char c1, char c2, char c3, char c4, char c5 >
requires std::True< (c1 >= '0' && c1 <= '2') >
&& std::True< (c2 >= '0' && c2 <= '9') >
&& std::True< (c3 = ':') >
&& std::True< (c4 >= '0' && c4 <= '5') >
&& std::True< (c5 >= '0' && c5 <= '9') >
constexpr hour_t operator "" _hour()
{
return hour_t(
(c1 - '0') * 600 +
(c2 - '0') * 60 +
(c4 - '0') * 10 +
(c5 - '0')
);
}


My question: is the latter example going to work?
Even if it is, I think the one with asserts looks more clear because I
can include a custom message. Is there a good reason for preventing
static_assers statements in constexpr functions? After all there must
be some point at which the static_asserts are removed from the code,
and, on the other hand, they do not provide any ambiguity or
complicate parsing.

Regards,
&rzej

--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std...@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]

litb

unread,
Apr 22, 2009, 3:07:22 PM4/22/09
to
On 21 Apr., 23:55, restor <akrze...@interia.pl> wrote:
> Hi,
> I am trying to imagine how defining compile-time-evaluated user
> defined literals would look like. Suppose I want literals that
> represent hours and minutes as one piece of data like 11:59. Obviously
> due to using a colon I can only use string literal like:
>
> á á"11:59"_hour

My second attempt:
>
> á átemplate< char c1, char c2, char c3, char c4, char c5 >
> á árequires std::True< (c1 >= '0' && c1 <= '2') >
> á á á á á&& std::True< (c2 >= '0' && c2 <= '9') >
> á á á á á á á á && std::True< (c3 = ':') >
> á á á á á á á á && std::True< (c4 >= '0' && c4 <= '5') >
> á á á á á á á á && std::True< (c5 >= '0' && c5 <= '9') >
> á áconstexpr hour_t operator "" _hour()
> á á{
> á á áreturn hour_t(
> á á á á(c1 - '0') * 600 +
> á á á á(c2 - '0') * 60 +
> á á á á(c4 - '0') * 10 +
> á á á á(c5 - '0')
> á á á);
> á á}


>
> My question: is the latter example going to work?

My understanding of that is that you can't have a literal operator
template invoked with a string literal. At least i haven't found it in
the draft. But i'm not sure about the rationale or whether it just
needs additional work to be included into the WP. The only literal
operator templates that seem to work are integers and floating point
literals. It would be so useful to have string literals with literal
operator templates, though.

But i think your concept way would work too. Note that you could
always create a class template outside the function, put it the value
as a parameter and stick static_asserts into the template definition,
exposing the result as a static const.

Anyway, I would also vote for static_assert within constexpr functions.

restor

unread,
Apr 23, 2009, 11:07:55 PM4/23/09
to
> My understanding of that is that you can't have a literal operator
> template invoked with a string literal. At least i haven't found it in
> the draft. But i'm not sure about the rationale or whether it just
> needs additional work to be included into the WP. The only literal
> operator templates that seem to work are integers and floating point
> literals. It would be so useful to have string literals with literal
> operator templates, though.

You are right. I didn't realize that you cannot use template form for
user string literals. I found the rationale for that in N2750, but I
cannot understand it.

This restriction is added to avoid specification and
implementation difficulties that arise from the
details of the phases of translation. For example,
the literal

"Hello, " L"Worl\u0044!"

may be indistinguishable from

L"Hello, World!"

at the end of phase 6. The latter could easily be
made the basis for the raw form, but we expect that
that would be surprising to programmers. Making the
former the raw form would require significant surgery
in the phases of translation, and might very
significantly increase the implementation cost for
some compiler vendors.

It is a shame because it would provide us with the means of compile-
time string parsing. We could define EDSLs verified at compile time.
Imgine literal
"([A-Z]+)([0-9]+)"_regex
that would be validated for parentheses being balancd at compile time.
The std::bitset can be created as:

std::bitset bset( "100102" );

This will throw an exception at runtime, whereas

std::bitset bset = "100102"_bitset;

could be caught at compile-time. Well here an integral literal would
do, but in general it seams that many exceptions could be replaced
with compilation errors.

Regards,
&rzej

restor

unread,
Apr 23, 2009, 11:09:27 PM4/23/09
to
> My understanding of that is that you can't have a literal operator
> template invoked with a string literal.

> The only literal operator templates that seem to work are integers
> and floating point literals.

> Note that you could


> always create a class template outside the function, put it the value
> as a parameter and stick static_asserts into the template definition,
> exposing the result as a static const.

Let me check if I got that all right. Will the following work for
floating-point literals of the form 11.23_hour ?

template < char... literal >
struct helper
{
static_assert( false, "Invalid length" );
};

template < char c0, char c1, char c2, char c3, char c4 >
struct helper
{
static_assert( c0 >= '0' && c0 <= '2', "bad char[0]" );
static_assert( c1 >= '0' && c1 <= '9', "bad char[1]" );
static_assert( c2 == '.', "bad char[2]" );
static_assert( c3 >= '0' && c3 <= '5', "bad char[3]" );
static_assert( c4 >= '0' && c4 <= '9', "bad char[4]" );

static constexpr unsigned value = {
(c0 - '0') * 600 +
(c1 - '0') * 60 +
(c3 - '0') * 10 +
(c4 - '0')
};
};

template < char... literal > constexpr
hour_t operator""_hour()
{
return hour_t( helper<literal>::value );
}

Regards,
&rzej

daniel....@googlemail.com

unread,
Apr 24, 2009, 2:23:48 PM4/24/09
to
On Apr 24, 5:09 am, restor <akrze...@interia.pl> wrote:
> > My understanding of that is that you can't have a literal operator
> > template invoked with a string literal.
> > The only literal operator templates that seem to work are integers
> > and floating point literals.
> > Note that you could
> > always create a class template outside the function, put it the value
> > as a parameter and stick static_asserts into the template definition,
> > exposing the result as a static const.
>
> Let me check if I got that all right. Will the following work for
> floating-point literals of the form 11.23_hour ?

To begin with: Above mentioned floating-point literal
should be feasible, if a proper literal operator has
been defined.

> template < char... literal >
> struct helper
> {
> static_assert( false, "Invalid length" );
> };

This is a bad idea, because the expression
in static_assert is non-dependent, so the
compiler could legally cause a static assert
diagnostic independent from the rest of your
code and without need to instantiate anything.

Several alternatives are possible:

a)


template < char... literal >
struct helper
{

static_assert(sizeof...(literal) == 5, "Invalid length" );
};

b)
template < char... literal >
struct helper; // Not defined

template < char c0, char c1, char c2, char c3, char c4 >

struct helper<c0, c1, c2, c3, c4> {
...
};

c)
template < char... literal >
requires std::True<sizeof...(literal) == 5>
struct helper; // Not defined

template < char c0, char c1, char c2, char c3, char c4 >

struct helper<c0, c1, c2, c3, c4> {
...
};

I tend to prefer (b) or (c).

> template < char c0, char c1, char c2, char c3, char c4 >
> struct helper

This needs to be a partial specialization starting with

template < char c0, char c1, char c2, char c3, char c4 >

struct helper<c0, c1, c2, c3, c4> { ... };

> {
> static_assert( c0 >= '0' && c0 <= '2', "bad char[0]" );
> static_assert( c1 >= '0' && c1 <= '9', "bad char[1]" );
> static_assert( c2 == '.', "bad char[2]" );
> static_assert( c3 >= '0' && c3 <= '5', "bad char[3]" );
> static_assert( c4 >= '0' && c4 <= '9', "bad char[4]" );
>
> static constexpr unsigned value = {
> (c0 - '0') * 600 +
> (c1 - '0') * 60 +
> (c3 - '0') * 10 +
> (c4 - '0')
> };
> };
>
> template < char... literal > constexpr
> hour_t operator""_hour()
> {
> return hour_t( helper<literal>::value );
> }

This definition is ill-formed because operator""_hour
is considered as a single token, see e.g. 13.5.2
[over.literal]/8.

You need to write it as:

template < char... literal > constexpr
hour_t operator "" _hour() { ... } // Note the spaces!

HTH & Greetings from Bremen,

Daniel Krügler

daniel....@googlemail.com

unread,
Apr 25, 2009, 6:11:11 PM4/25/09
to
On 24 Apr., 20:23, daniel.krueg...@googlemail.com wrote:
> template < char... literal > constexpr
> hour_t operator "" _hour() { ... } // Note the spaces!

I also suggest to add at least a single requirement
to the literal operator template definition to make the
complete template definition a constrained context:

template < char... literal >
requires std::True<sizeof...(literal) == 5>

constexpr hour_t operator "" _hour() { ... }

Doing this automatically activates requirement implication
as of 14.11.1.2 [temp.req.impl] and thus would ensure
that *all* requirements of the constrained helper template
are "propagated" onto the literal operator definition. If
you don't like repeating the size constraint of the
variadic pack, you could simply say

requires std::True<true>

instead which has the same effect here (if you would chose
the helper definition labeled (c) in my previous posting).

Let me add that 13.5.8 [over.literal] does *not* reject
constrained literal operator templates. It would need
to say so, if it would explicitly want to exclude them.
For the same reason it is possible to define a const-
qualified copy assignment operator, even though 12.8
[class.copy]/10 doesn't explicitly speak that out.

daniel....@googlemail.com

unread,
Apr 25, 2009, 9:53:35 PM4/25/09
to
On 26 Apr., 00:11, daniel.krueg...@googlemail.com wrote:
> On 24 Apr., 20:23, daniel.krueg...@googlemail.com wrote:
>
> > template < char... literal > constexpr
> > hour_t operator "" _hour() { ... } // Note the spaces!
>
> I also suggest to add at least a single requirement
> to the literal operator template definition to make the
> complete template definition a constrained context:
>
> template < char... literal >
> requires std::True<sizeof...(literal) == 5>
> constexpr hour_t operator "" _hour() { ... }
>
> Doing this automatically activates requirement implication
> as of 14.11.1.2 [temp.req.impl] and thus would ensure
> that *all* requirements of the constrained helper template
> are "propagated" onto the literal operator definition. If
> you don't like repeating the size constraint of the
> variadic pack, you could simply say
>
> requires std::True<true>
>
> instead which has the same effect here (if you would chose
> the helper definition labeled (c) in my previous posting).

I apologize for this misleading sentence. Requirement
implication does not happen here, because the constrained
template helper is not part of the literal operator template
signature. This means the constrained literal operator template
needs to mention all requirements of the helper. This could be
simplified via a single auto concept that contains the whole
bunch or requirements.

> Let me add that 13.5.8 [over.literal] does *not* reject
> constrained literal operator templates. It would need
> to say so, if it would explicitly want to exclude them.
> For the same reason it is possible to define a const-
> qualified copy assignment operator, even though 12.8
> [class.copy]/10 doesn't explicitly speak that out.

This is still correct.

Sorry for the irritations,

Daniel

0 new messages