-
Notifications
You must be signed in to change notification settings - Fork 261
Idea: Adding from_string
to enum metafunction
#1185
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
Conversation
Thanks for your pull request! It looks like this may be your first contribution to cppfront. I'll ping you by email to send the Contributor License Agreement (CLA) to your email, and once it's signed I can look at your pull request. Thanks again for your contribution. |
…dling Changed failures from unconditional abort to a type_safety violation (which defaults to abort but allows a customizable handler) because it really is a precondition violation so a contract makes sense
Thanks! This seems like a good idea. I've taken a pass over it and updated the PR. The only reason I didn't merge it is that it's labeled "Draft"... ok to merge? |
Yeah its ready, I've only wondered if there is a good way to extend this to bitwise enums, but maybe that can be a seperate PR when someone actually needs it. |
I did that in my update to this PR, just by removing the check to prevent generating it for flag enums. It seems to work fine for any named enumerator, including named enumerators that are combinations of others? I added the test case: f_from_string := file_attributes::from_string("cached_and_current");
std::cout << "f_from_string is " << f_from_string.to_string() << "\n"; which prints:
That seems correct, right -- two bits set? |
Ah okay, I missed that. I was mostly thinking that now the output of I made a second branch where I added this, but it's a bit messy, so maybe it's not the best idea to add it right now. |
Ah, good point! Working on it... |
OK, how does this look -- I think it's ready to merge, review please? |
@@ -1555,6 +1556,26 @@ if (((*this) & perl_code_syntax_in_classes) == perl_code_syntax_in_classes) {_re | |||
return cpp2::move(_ret) + ")"; | |||
} | |||
|
|||
[[nodiscard]] auto expression_flags::from_string(cpp2::impl::in<std::string_view> s) -> expression_flags{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@hsutter Why do all of these lines start at column 0? There is leading whitespace in the metafunction. Is it because the whitespace is tabs, and those are being stripped somehow?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question, I don't know offhand.
return std::isspace(str[pos]) || str[pos] == ','; | ||
}; | ||
|
||
while( pos < std::ssize(str) ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick, if you care: style difference between this while
and the next three while/if
, spaces inside parens here and not below, space after keyword below and not here.
@@ -117,4 +119,10 @@ main: () = { | |||
is (cpp2::has_flags(f2)) = "includes all f2's flags ('cached' and 'current')"; | |||
is _ = "something else"; | |||
} << "\n"; | |||
|
|||
f_from_string := file_attributes::from_string("cached_and_current"); | |||
std::cout << "f_from_string is " << f_from_string.to_string() << "\n"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It bothers me slightly that both of these:
file_attributes::to_string(file_attributes::from_string("cached_and_current"));
(file_attributes::cached | file_attributes::current).to_string()
result in this:
"(cached, current, cached_and_current)"
I'm wondering if a bitwise enum to_string()
should be a single combined value if it exactly matches a combined value, and then if it doesn't, only include the bare flags.
That would make the first ones result in "cached_and_current"
. Then (file_attributes::current | file:attributes.obsolete).to_string()
would be (current, obsolete)
.
Also, should to_string()
include file_attributes::
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
It bothers me slightly that both of these:
file_attributes::to_string(file_attributes::from_string("cached_and_current")); (file_attributes::cached | file_attributes::current).to_string()
result in this:
"(cached, current, cached_and_current)"
I'm wondering if a bitwise enum
to_string()
should be a single combined value if it exactly matches a combined value, and then if it doesn't, only include the bare flags.
What I've been doing with to_string
is emitting all of the named values that are matched. That way it's consistent with has
.
Also, should
to_string()
includefile_attributes::
?
to_string
has a this
parameter, from_string
doesn't so it needs the qualification.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I've been doing with to_string is emitting all of the named values that are matched. That way it's consistent with has.
My "nightmare scenario" is something like the creating an enum for the file permissions in <sys/stat.h>
, and then doing ALLPERMS.to_string()
and getting this:
"(S_ISUID, S_ISGID, S_ISTXT, S_ISVTX, S_IRWXU, S_IRUSR, S_IWUSR, S_IXUSR,
S_IREAD, S_IWRITE, S_IEXEC, S_IRWXG, S_IRGRP, S_IWGRP, S_IXGRP,
S_IRWXO, S_IROTH, S_IWOTH, S_IXOTH, ACCESSPERMS, ALLPERMS,
DEFFILEMODE)"
to_string has a this parameter, from_string doesn't so it needs the qualification.
To clarify, I meant in the output string. It was just a thought, not for computer consumption, but more for human consumption. I was thinking that the user always has to spell it as file_attributes::current
, unless they're inside the class, so would it make sense for to_string()
to produce that? That would make from_string()
more difficult, I guess.
Related to that, would it make sense to use |
instead of ,
so that it makes actually usable as code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Related to that, would it make sense to use | instead of , so that it makes actually usable as code?
👍🏻 I was thinking of this myself when I used flag_enum
s a while ago.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! I just pushed a commit to change ,
to |
for flag enums.
Allowing qualification is doable but just makes reading and parsing harder. A poor person's way to make it a qualified string would be to prepend flag_name::
and then replace every |
with | flag_name::
... perhaps it's worth adding a to_string_qualified
that just calls to_string
and then does that, and a from_string_qualified
that replaces all flag_name::
with nothing and calls from_string
...?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, it could be a thing. Python for example has __repr__
and __str__
which cover both of these use-cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if there are use cases here, just things that came to mind as I was reading it.
Maybe the "a single combined value if it exactly matches a combined value, and then if it doesn't, only include the bare flags" would be a separate function and it could have a qualified
option?
And fixed bug to correct error case `break`
to_string/from_string round-trip, don't enum_name::-qualify, and represent a list with comma separators to_code/from_code round-trip, do enum_name::-qualify, and represent a list with | separators
OK, I've pushed a commit that now supports both:
Please check? How does this look? |
source/reflect.h2
Outdated
"_ret += _comma + \"(e.name)$\"; _comma = \", \"; " | ||
"}\n"; | ||
to_string_impl += " if (this & (e.name)$) == (e.name)$ { " | ||
"ret += sep + cpp2::to_string(prefix) + \"(e.name)$\"; sep = separator; " |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This results in code like this:
if (((*this) & case_insensitive) == case_insensitive) {ret += sep + cpp2::to_string(prefix) + "case_insensitive";sep = separator;}
if (((*this) & multiple_lines) == multiple_lines) {ret += sep + cpp2::to_string(prefix) + "multiple_lines";sep = separator;}
if (((*this) & single_line) == single_line) {ret += sep + cpp2::to_string(prefix) + "single_line";sep = separator;}
This calls sep + cpp2::to_string(prefix)
for every matched bit.
Could the function start something like this:
auto first = cpp2::to_string(prefix);
auto next = separator + first;
auto cur = &first;
and then for each bit
if (((*this) & case_insensitive) == case_insensitive) {ret += cur + "case_insensitive"; cur = &next;}
Add regress test case for to_code / from_code Update regression test and generated files
Signed-off-by: Herb Sutter <[email protected]>
Signed-off-by: Herb Sutter <[email protected]>
So that this works:
Could something like this make sense?