Date: Mon, 29 Jul 2024 01:43:08 +0200
Hi,
Say you have a type called Handle that can be in null state, without
further optional<Handle> or Handle* or any other wrapper.
I think you're not looking for optional<Handle> to be redefined, that's an
XY-problem: a solution pressed instead of a problem stated.
What you're looking for is a concept that tells that a class implements the
Maybe monad:
template<class T>
concept maybe_impl = requires(T t) {
{ *t, bool(t), bool(!t), T{}; };
};
Then, wherever you were to use optional<T>, you need to use maybe_impl
auto, e.g.:
auto transform(maybe_impl auto t, auto f) ->
decltype(deduce_applied_type(t, f)) { // deduce_applied_time omitted here
as it's not important for the discussion
if (t) {
return f(t);
}
return {};
}
It also shows that perhaps transform() and value_or() are not exactly in
the proper place for optional<>, similarly transform() and
transform_error() and and_then() and or_else() are not exactly for
expected<> only. Instead, these could be free functions, or even named
operators, that could be applied for: (where applicable)
- T*
- unique_ptr<T>
- shared_ptr<T>
- optional<T>
- expected<T, E>
- any user-defined type with proper overloads.
What do you think?
Thanks,
-lorro
On Sun, Jul 21, 2024 at 6:51 PM Frederick Virchanza Gotham via
Std-Proposals <std-proposals_at_[hidden]> wrote:
> 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]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
Say you have a type called Handle that can be in null state, without
further optional<Handle> or Handle* or any other wrapper.
I think you're not looking for optional<Handle> to be redefined, that's an
XY-problem: a solution pressed instead of a problem stated.
What you're looking for is a concept that tells that a class implements the
Maybe monad:
template<class T>
concept maybe_impl = requires(T t) {
{ *t, bool(t), bool(!t), T{}; };
};
Then, wherever you were to use optional<T>, you need to use maybe_impl
auto, e.g.:
auto transform(maybe_impl auto t, auto f) ->
decltype(deduce_applied_type(t, f)) { // deduce_applied_time omitted here
as it's not important for the discussion
if (t) {
return f(t);
}
return {};
}
It also shows that perhaps transform() and value_or() are not exactly in
the proper place for optional<>, similarly transform() and
transform_error() and and_then() and or_else() are not exactly for
expected<> only. Instead, these could be free functions, or even named
operators, that could be applied for: (where applicable)
- T*
- unique_ptr<T>
- shared_ptr<T>
- optional<T>
- expected<T, E>
- any user-defined type with proper overloads.
What do you think?
Thanks,
-lorro
On Sun, Jul 21, 2024 at 6:51 PM Frederick Virchanza Gotham via
Std-Proposals <std-proposals_at_[hidden]> wrote:
> 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]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
Received on 2024-07-28 23:43:21