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?
>
> 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