-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
std::optional #1367
Comments
hi |
You can dereference fmt::format("{}", value_or_null(opt)); where |
This seems to work as a solution to this:
|
@jasonbeach sadly this does not really scale: I'm currently having the issue with two libraries which both include their own specialization of |
Now that we have a formatter for |
I wonder, what'd be a good thing to print when the optional is unset? The least surprising with variant printing "monostate" would be "nullopt", would that be good ? |
|
I would say that's a good argument to include it in the library...so downstream libraries don't have to, thereby introducing the possibilities of clashes. I can put a PR together it just might be a couple days before I'm able to. |
My only concern with that is that in log files it would kind of blend in with surrounding text. I think I would prefer something like |
I quickly implemented a formatter which allows applying further manipulation depending on wether the optional is present. template <typename T>
struct formatter<std::optional<T>> {
std::string_view underlying_fmt;
std::string_view or_else;
constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
// {:<if present{}><if not present>}
auto it = ctx.begin(), end = ctx.end();
auto get_marker = [&it, end]() constexpr -> std::string_view {
if(it == end || *it != '<') return std::string_view(nullptr, 0); // no match
auto start = ++it;
while(it != end && (*it++ != '>'));
if(it == end) throw fmt::format_error("invalid format, unfinished range");
return std::string_view(start, it - 1);
};
underlying_fmt = "{}";
or_else = "";
auto first = get_marker();
if(first.data() != nullptr) underlying_fmt = first;
auto second = get_marker();
if(second.data() != nullptr) or_else = second;
// Check if reached the end of the range:
if (it != end && *it != '}') throw fmt::format_error("invalid format, no end bracket");
return it;
}
template <typename FormatContext>
auto format(const std::optional<T>& p, FormatContext& ctx) const -> decltype(ctx.out()) {
if(p.has_value()) {
return vformat_to(ctx.out(), underlying_fmt, format_arg_store<FormatContext, T>{*p});
}
else {
return format_to(ctx.out(), "{}", or_else);
}
}
}; Here's how you would use it: std::optional<int> arbitrary_optional;
fmt::print("{:<Value: {}><(NO VALUE)>}", arbitrary_optional);
// With a value -> "Value: 10"
// With nullopt -> "(NO VALUE)" |
I like rust's convention fmt::print("{}\n", std::optional{1});
// Some(1)
fmt::print("{}\n", std::optional<int>{});
// None I like to print ranges of tuples containing optionals. |
As in Rust we probably want different representations for the empty and non-empty case to avoid ambiguity ( |
How do you like to handle format specifiers with precision or other expected output format to optionals that are nullopt ? For me thats an format_error, because requested precision or width cannot be guranteed. |
Do you have code snippet for what's not working?
works for me |
Its just about expectations. If I define (as a developer) a format, where I'd expect a floating point type and get a monostate representation type as a string, I would be confused. We have discussed that in depth in our company, because we have fmt specialized for But lets start with an example Default PresentationType Behaviorfmt::print("{}", std::optional<double>{}); // i.e "(nullopt)" I think it can be argued, that some kind of representation for undefined/not-initialized is ok. We choose, due to application logic, an empty string. A json-like representation would be Specific PresentationType Behaviorfmt::print("{:.2f}", std::optional<double>{}); // "(nullopt)"
fmt::print("{:.2f}", std::optional<double>{1.234}); // "1.23" As a developer I would expect a floating point and might get a string, that does not represent a floating point type. This is difficult to teach. Throwing an exception maybe the other solution. The question is: what is least suprising way to handle this and does no break expected integrity of data produced by format. We also discussed, if the developer may also state, that an undefined/not-initialized is expected, but think, that it would degenerate the mini-dsl to specify the format-string and ppl get used to abusing the dsl. For me the conclusion is: It is an application level specific problem not a library specific one. At least, if it is provided by the library, a opt-in would be nice, but afaik thats a behavior change crossing module boundaries and can be interpreted as ABI break. |
please support std::optionalso if it has no value substitute it with "NULL" or "(null)" (see: #417) or with a preconfigured value
The text was updated successfully, but these errors were encountered: