Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

strong::underlying_type_t #49

Open
davidhunter22 opened this issue Nov 7, 2024 · 5 comments
Open

strong::underlying_type_t #49

davidhunter22 opened this issue Nov 7, 2024 · 5 comments

Comments

@davidhunter22
Copy link
Contributor

I have a question about the following

    using Foo = strong::type<int const, struct FooTag>;
    using T = strong::underlying_type_t<Foo>;
    static_assert( std::is_same_v<Z, int const> );

The static_asset fails as the type T is an int not a int const.
I am wondering if this is deliberate? I certainly found it surprising.
This happens because the underlying_type_t is implemented by returning a instance of the underlying type and

    int const foo( ); // decltype( foo( ) ) is an int not an int const 

I am wondering why you don't use member types in strong::type I.E.

template<typename T, typename Tag, typename ... M>
class type : public modifier<M, type<T, Tag, M...>> ... {
    using element_type = T;
    using value_type = std::remove_cv_t<T>
    ...

like std::span does.

@rollbear
Copy link
Owner

rollbear commented Nov 7, 2024

The answer is that it honestly never occurred to me that this would be a use case. Why do you want to have a const underlying type? I think the comparison with std::span is a bit odd, since it refers to data elsewhere, whereas a strong type instance owns its data. However, I see that owning types like std::optional<T> and std::array<T, N> behave in a more desired way. Now I'm a bit nervous about if I can do anything about this without breaking people's code.

@davidhunter22
Copy link
Contributor Author

I only used span as an example, maybe shared_ptr or similar would be better.

I ran into this when I wanted to do strong::type<std::string const,...>. This was being used to represent some strings that should never change, in my case ISO defined country names. In this case I may switch to using some fixed_string type like https://github.com/unterumarmung/fixed_string. Note copying these things is fine but in general it's a coding error to try to change the values.
I must admit to being a little unsure over the advantages and disadvantages of

strong::type<std::string const, ...>
strong::type<std::string ,...> const

@rollbear
Copy link
Owner

The problem with strong::type<const X,...> is that it can't be move constructed. With your example it probably doesn't matter, but in general it's problematic.

Maybe you should write your own type, maybe implemented in terms of std::string, that doesn't expose any modifying functions?

class country_name
{
public:
    explicit country_name(std::string name) : name_(std::move(name)) {}
    country_name& operator=(const country_name&) = delete;
    explicit operator std::string_view() const { return name_; }
    // probably others too...
private:
    std::string name_;
};

A truly Machiavellian user can, of course, subvert the type system completely and do whatever they please, but it takes a little effort and is very visible. ((std::string&)country) = "foo".

Or, you can use strong_type with CRTP and explicitly disallow assignment.

struct country_name : strong::type<std::string,
                                   country_name,
                                   strong::ostreamable,
                                   strong::convertible_to<std::string_view>,
                                   strong::equality, strong::equality_with<std::string_view>>
{
    using type::type;
    country_name& operator=(const country_name&) = delete;
};

Now, this can be modified using value_of(x) = "foo", but that takes a deliberate effort and is quite visible in a code review.

This makes me think I should probably add a modifier that disallows assignment.

@rollbear
Copy link
Owner

Would this work?

https://godbolt.org/z/n9853n3ex

I'd have to figure out a way to make value_of() only return const& for immutable types.

@rollbear
Copy link
Owner

Try branch immutable and tell me if it's OK for your use case. You use it with a non-const underlying type, and a strong::immutable modifier.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants