Date: Thu, 21 Sep 2023 14:37:32 -0600
This notation also comes to mind:
`template< typename CodeUnitT, std::size_t size_ >
constexpr mp_rational operator ""_qq( const CodeUnitT (&lit)[size_] )
*constexpr* requires( matches_std_numeric_literal( lit ) )
{
// etc...
}`
On Thu, Sep 21, 2023 at 2:34 PM Chris Gary <cgary512_at_[hidden]> wrote:
> I might need to clarify: In the last example, I don't mean to imply
> exception-generating code should be inserted by the compiler, but instead
> the "materialize_qq_from_static_literal_as_fn" utility (which is probably
> too brief for the example) would branch in its definition using
> std::is_constant_evaluated and throw where appropriate.
>
> On Thu, Sep 21, 2023 at 2:21 PM Chris Gary <cgary512_at_[hidden]> wrote:
>
>> Maybe that information can be hoisted into the function's metadata,
>>> inducing a set of functions based on their parameters?
>>>
>>> Admittedly, the binding criteria are complicated.
>>>
>>> More concretely (somewhat),
>>>
>>> read this:
>>> `constexpr auto q = 123456.789_qq;`
>>>
>>> like this:
>>> `constexpr auto q = operator ""_qq( "123456.789", 10u );`
>>>
>>> or, closer to what I mean:
>>>
>>> `constexpr auto q = operator ""_qq( "123456.789" );`
>>>
>>> where the definition is:
>>>
>>> `template< std::size_t size_ >
>>> constexpr mp_rational operator ""_qq( const char (&lit)[size_] )
>>> requires( std::is_constant_evaluated() ? matches_std_numeric_literal(
>>> lit ) : true )
>>> {
>>> if( std::is_constant_evaluated() )
>>> {
>>> using MQQ = materialize_qq_from_static_literal< lit... >;
>>> }
>>> else
>>> {
>>> if( ! matches_std_numeric_literal( lit ) )
>>> {
>>> throw std::invalid_argument{};
>>> }
>>>
>>> // etc...
>>> }
>>> }`
>>>
>>> loosely speaking.
>>>
>>> Template instantiations and static function invocations given known
>>> parameters can be viewed uniformly as unique "types". This is already
>>> known, since constexpr functions can be used to instantiate templates (e.g.
>>> a non-type parameter is identified by its constructor arguments).
>>>
>>> So, an alternative assuming something like constexpr static would work
>>> intuitively:
>>>
>>> `template< typename CodeUnitT, std::size_t size_ >
>>> constexpr mp_rational operator ""_qq( const CodeUnitT (&lit)[size_] )
>>> requires( std::is_constant_evaluated() ? matches_std_numeric_literal(
>>> lit ) : true )
>>> {
>>> // At compile-time: The requires clause is evaluated, raising errors
>>> wherever
>>> // the data can be examined directly.
>>> //
>>> // At runtime: Throws an exception if lit violates whats in the
>>> requires() clause above.
>>> return materialize_qq_from_static_literal_as_fn( lit ).bind();
>>> }`
>>>
>>> Reflecting on this, what I'm really after is just materializing ordinary
>>> data from compile time into static data...
>>>
>>> To my original point, being able to expand arrays into other arrays (or
>>> function parameter lists) seems obvious to me.
>>>
>>
`template< typename CodeUnitT, std::size_t size_ >
constexpr mp_rational operator ""_qq( const CodeUnitT (&lit)[size_] )
*constexpr* requires( matches_std_numeric_literal( lit ) )
{
// etc...
}`
On Thu, Sep 21, 2023 at 2:34 PM Chris Gary <cgary512_at_[hidden]> wrote:
> I might need to clarify: In the last example, I don't mean to imply
> exception-generating code should be inserted by the compiler, but instead
> the "materialize_qq_from_static_literal_as_fn" utility (which is probably
> too brief for the example) would branch in its definition using
> std::is_constant_evaluated and throw where appropriate.
>
> On Thu, Sep 21, 2023 at 2:21 PM Chris Gary <cgary512_at_[hidden]> wrote:
>
>> Maybe that information can be hoisted into the function's metadata,
>>> inducing a set of functions based on their parameters?
>>>
>>> Admittedly, the binding criteria are complicated.
>>>
>>> More concretely (somewhat),
>>>
>>> read this:
>>> `constexpr auto q = 123456.789_qq;`
>>>
>>> like this:
>>> `constexpr auto q = operator ""_qq( "123456.789", 10u );`
>>>
>>> or, closer to what I mean:
>>>
>>> `constexpr auto q = operator ""_qq( "123456.789" );`
>>>
>>> where the definition is:
>>>
>>> `template< std::size_t size_ >
>>> constexpr mp_rational operator ""_qq( const char (&lit)[size_] )
>>> requires( std::is_constant_evaluated() ? matches_std_numeric_literal(
>>> lit ) : true )
>>> {
>>> if( std::is_constant_evaluated() )
>>> {
>>> using MQQ = materialize_qq_from_static_literal< lit... >;
>>> }
>>> else
>>> {
>>> if( ! matches_std_numeric_literal( lit ) )
>>> {
>>> throw std::invalid_argument{};
>>> }
>>>
>>> // etc...
>>> }
>>> }`
>>>
>>> loosely speaking.
>>>
>>> Template instantiations and static function invocations given known
>>> parameters can be viewed uniformly as unique "types". This is already
>>> known, since constexpr functions can be used to instantiate templates (e.g.
>>> a non-type parameter is identified by its constructor arguments).
>>>
>>> So, an alternative assuming something like constexpr static would work
>>> intuitively:
>>>
>>> `template< typename CodeUnitT, std::size_t size_ >
>>> constexpr mp_rational operator ""_qq( const CodeUnitT (&lit)[size_] )
>>> requires( std::is_constant_evaluated() ? matches_std_numeric_literal(
>>> lit ) : true )
>>> {
>>> // At compile-time: The requires clause is evaluated, raising errors
>>> wherever
>>> // the data can be examined directly.
>>> //
>>> // At runtime: Throws an exception if lit violates whats in the
>>> requires() clause above.
>>> return materialize_qq_from_static_literal_as_fn( lit ).bind();
>>> }`
>>>
>>> Reflecting on this, what I'm really after is just materializing ordinary
>>> data from compile time into static data...
>>>
>>> To my original point, being able to expand arrays into other arrays (or
>>> function parameter lists) seems obvious to me.
>>>
>>
Received on 2023-09-21 20:37:44