Date: Mon, 22 Jul 2024 15:23:20 +0000
Maybe we can go further. Here is my solution.
We add a template struct in namespace std.
template<class T>
struct invalid_state; //No definition
We can add a template specialization like this:
template<>
struct std::invalid_state<my_class>
{
static constexpr size_t offset = ... ;
static constexpr unsigned char state[] = {...};
}; //This means from the offset'th to (offset + sizeof(state))'th bytes of an object with type my_class can't be the same as state.
Here is an example:
template<class T>
class my_vector
{
T* data; //8bytes
size_t _size; //8bytes, can't be 0xffffffff
//...
};
template<class T>
struct std::invalid_state<my_vector<T>>
{
static constexpr size_t offset = 8 ;
static constexpr unsigned char state[] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
};
Then std::optional< my_vector<T>> can use this to decide whether it has a value.
-----邮件原件-----
发件人: Std-Proposals <std-proposals-bounces_at_lists.isocpp.org> 代表 Frederick Virchanza Gotham via Std-Proposals
发送时间: 2024年7月22日 0:51
收件人: std-proposals <std-proposals_at_lists.isocpp.org>
抄送: Frederick Virchanza Gotham <cauldwell.thomas_at_gmail.com>
主题: [std-proposals] all bytes zero == null object
Typically when implementing a class such as 'std::optional', the size of 'std::optional<T>' is equal to "sizeof(T) + 1u", because one more byte is needed to store a boolean to indicate whether there is a valid object in existence.
We can use "__datasizeof" or "[[no_unique_address]]" in order to try use the tail padding of T to store the boolean, and if we succeed, then sizeof(optional<T>) == sizeof(T).
If T has no tail padding, then we need an extra byte to store the boolean. Unless . . . we had the ability to mark the class 'null_bytes==null_object" as follows:
class T (null_bytes==null_object) {
. . .
};
Marking a class as 'null_bytes==null_object' means that a byte-pattern of all zeroes is a 'null object'. The standard header <type_traits> would then have "std::is_null_bytes_null_object".
So then "std::optional::has_value" could be:
bool optional::has_value(void) noexcept requires is_null_bytes_null_object_v<T>
{
return (0u == *storage) && !memcmp(storage, storage + 1u,
sizeof(storage) - 1u);
}
Another effect of marking a class as 'null_bytes==null_object' would be that the destructor doesn't get called. For instance the following program won't crash:
struct T (null==0) {
int a, b, c;
~T(void) { *(int*)nullptr = 666; }
};
int main(void)
{
T var = {0,0,0};
}
The above program doesn't have undefined behaviour, because the destructor is treated as though it were written:
~T(void)
{
char const *const p = (char*)this;
if ( (0u == *p) && !memcmp(p, p + 1u, sizeof(*this) - 1u) ) return;
*(int*)nullptr = 0;
}
--
Std-Proposals mailing list
Std-Proposals_at_[hidden]rg
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
We add a template struct in namespace std.
template<class T>
struct invalid_state; //No definition
We can add a template specialization like this:
template<>
struct std::invalid_state<my_class>
{
static constexpr size_t offset = ... ;
static constexpr unsigned char state[] = {...};
}; //This means from the offset'th to (offset + sizeof(state))'th bytes of an object with type my_class can't be the same as state.
Here is an example:
template<class T>
class my_vector
{
T* data; //8bytes
size_t _size; //8bytes, can't be 0xffffffff
//...
};
template<class T>
struct std::invalid_state<my_vector<T>>
{
static constexpr size_t offset = 8 ;
static constexpr unsigned char state[] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
};
Then std::optional< my_vector<T>> can use this to decide whether it has a value.
-----邮件原件-----
发件人: Std-Proposals <std-proposals-bounces_at_lists.isocpp.org> 代表 Frederick Virchanza Gotham via Std-Proposals
发送时间: 2024年7月22日 0:51
收件人: std-proposals <std-proposals_at_lists.isocpp.org>
抄送: Frederick Virchanza Gotham <cauldwell.thomas_at_gmail.com>
主题: [std-proposals] all bytes zero == null object
Typically when implementing a class such as 'std::optional', the size of 'std::optional<T>' is equal to "sizeof(T) + 1u", because one more byte is needed to store a boolean to indicate whether there is a valid object in existence.
We can use "__datasizeof" or "[[no_unique_address]]" in order to try use the tail padding of T to store the boolean, and if we succeed, then sizeof(optional<T>) == sizeof(T).
If T has no tail padding, then we need an extra byte to store the boolean. Unless . . . we had the ability to mark the class 'null_bytes==null_object" as follows:
class T (null_bytes==null_object) {
. . .
};
Marking a class as 'null_bytes==null_object' means that a byte-pattern of all zeroes is a 'null object'. The standard header <type_traits> would then have "std::is_null_bytes_null_object".
So then "std::optional::has_value" could be:
bool optional::has_value(void) noexcept requires is_null_bytes_null_object_v<T>
{
return (0u == *storage) && !memcmp(storage, storage + 1u,
sizeof(storage) - 1u);
}
Another effect of marking a class as 'null_bytes==null_object' would be that the destructor doesn't get called. For instance the following program won't crash:
struct T (null==0) {
int a, b, c;
~T(void) { *(int*)nullptr = 666; }
};
int main(void)
{
T var = {0,0,0};
}
The above program doesn't have undefined behaviour, because the destructor is treated as though it were written:
~T(void)
{
char const *const p = (char*)this;
if ( (0u == *p) && !memcmp(p, p + 1u, sizeof(*this) - 1u) ) return;
*(int*)nullptr = 0;
}
--
Std-Proposals mailing list
Std-Proposals_at_[hidden]rg
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
Received on 2024-07-22 15:23:25