C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Function only accepts parameter that will persist

From: Frederick Virchanza Gotham <cauldwell.thomas_at_[hidden]>
Date: Tue, 7 May 2024 11:32:39 +0100
On Mon, May 6, 2024 at 8:44 PM Arthur O'Dwyer wrote:
>
> For example,
> constexpr int x = Func(1, 2);
> is certainly required to constant-evaluate `Func`.
> const int x = Func(1, 2);
> is at least encouraged [and in fact it is required] to attempt to constant-evaluate `Func`,
> although of course that constant-evaluation attempt might fail and then it would have
> to retry as a non-constant expression.
  <snip>
> Meanwhile, the original subject of the top-level thread is about "how do I know if I've got
> a pointer to ROM/.rodata or a pointer to plain old RAM/.data (that might change)?"
  <snip>
> if `f("hello")` is supposed to call `f(__rom const char*)`, then `decltype("hello")` must be
> `__rom const char[6]` instead of plain `const char[6]`... and that's going to break a lot of
> existing metaprogrammers.
  <snip>
> Most of your suggestions in this area have been utterly wrong (as far as anything to do
> with WG21 is concerned) because you're conflating "const" with "static storage duration"
> and (even more importantly) with "lifetime."


You're right that I was barking up the wrong tree with relying on
'consteval' to determine if a string is static-duration const.

Let's say I start out with a struct:

        struct Frog {
            long i, j, k;
        };

And I code a function such as:

    void Func( Frog &&f );

And then I invoke this function as follows:

    int main(void) { Func( Frog{1,2,3} ); }

I think that most (if not all) compilers would invoke 'Func' in this
context by passing it a pointer to a static-duration const array of 3
long's (most likely located in a page of memory marked as read-only).
However a quirky compiler could possibly code 'main' as follows:

    push 3
    push 2
    push 1
    mov rdi, rsp
    call Func
    add rsp, 24 // pop three long's
    mov rdi, 0
    jmp _Exit

In the above x86_64 assembler, I allocate space for three long's on
the stack, I set the values of the 3 long's on the stack, I put the
address of the array into the RDI register (i.e. the first parameter),
I call the function, and I restore the stack pointer. So the point I
want to make here is that a compound literal might not be of
static-duration. With that said though, I don't know if there's any
extant compiler that would do this -- maybe every extant C++ compiler
would use a static-duration const array located in a read-only page of
memory.

String literals are compound arrays however they're odd in that
they're Lvalues instead of Rvalues.

What I wanted when writing the constructor for 'efficient_string' is
that I wanted to code an optimisation as follows: "If the argument is
a static-duration string that will never change, don't copy it".

So having thought about this a little more . . . I think the easiest
and most simple way to achieve this in C++26 would be as follows:

    * We have a new keyword 'static_duration_const' which is a type modifier
    * All static-duration POD-compound literals (and yes all string
literals) have the 'static_duration_const' modifier
    * An object with the 'static_duration_const' modifier implicitly
converts to an object without said modifier

So if we write a string literal like "Hello", then its type is:
char static_duration_const [6]

There is an implicit conversion from "char static_duration_const [6]"
to "char [6]".

So then we could write two constructors as follows:

    efficient_string( char static_duration_const * )
    {
        // don't copy the string
    }
    efficient_string( char const * )
    {
        // copy the string
    }

And then let's say I write a small program as follows:

    char const *const *g_argv = nullptr;

    int main(int const argc, char **const argv)
    {
        g_argv = argv;

        efficient_string s( g_argv[0] );
    }

The above snippet will copy the string -- but let's say that I want to
promise to the compiler that 'g_argv[0]' is a static-duration const
string that will never change . . . well I can use a cast to make this
promise:

        efficient_string s( (char static_duration_const*)g_argv[0] );

And so now the string isn't copied.

In all of the code I've written above: Instead of having a new
keyword 'static_duration_const', maybe we could just re-use the
'constexpr' keyword here? So we'd have:

1 - The type of "Hello" is: char constexpr [6]
2 - There is an implicit conversion from "char constexpr [6]" to "char [6]".
3 - The non-copying constructor is efficient_string( char constexpr * )
4 - The copying constructor is efficient_string( char const * )
5 - The "promise cast" is efficient_string s( (char constexpr*)g_argv[0] );

How does that sound?

Received on 2024-05-07 10:32:46