Skip to content

task-4660 std::format instead of snprintf(buffer...) in 9 files#4736

Draft
yotam-medini wants to merge 4 commits intocanonical:mainfrom
yotam-medini:fix-issue-4660-use_std_fmt
Draft

task-4660 std::format instead of snprintf(buffer...) in 9 files#4736
yotam-medini wants to merge 4 commits intocanonical:mainfrom
yotam-medini:fix-issue-4660-use_std_fmt

Conversation

@yotam-medini
Copy link
Contributor

@yotam-medini yotam-medini commented Mar 2, 2026

It may close #4660.

#4660

What's new?

This commit replaces all calls of snprintf(buffer...) with automatic buffer with std::format.

Use of vsnprintf(...) is not changed. Moving from vsnprinf to std::format requires design decisions.

How to test

Under build directory, I ran:
ctest --exclude-regex wlcs --output-on-failure --stop-on-failure
And it ran successfully. Last message lines:

19/19 Test #19: mir_unit_tests_virtual ..................   Passed    0.38 sec
100% tests passed, 0 tests failed out of 19
Total Test time (real) = 234.74 sec

Checklist

  • Tests added and pass
  • Adequate documentation added
  • (optional) Added Screenshots or videos

@yotam-medini yotam-medini requested a review from a team as a code owner March 2, 2026 12:42
auto now = std::chrono::system_clock::now();
auto now_us = std::chrono::time_point_cast<std::chrono::microseconds>(now);
auto local = std::chrono::zoned_time{std::chrono::current_zone(), now_us};
std::string fmt_now = std::format("{:%F %T}", local);
Copy link
Contributor

@RAOF RAOF Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fairly certain this is missing the old 6-digit nsec field?

Copy link
Contributor Author

@yotam-medini yotam-medini Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now_ft.cpp
yotam@zorani:ym-misc 2503:$ g++ -Wall -g --std=c++20 -o /tmp/now_ft now_ft.cpp
yotam@zorani:ym-misc 2504:$ /tmp/now_ft
fmt_now=2026-03-04 12:01:07.958230

Copy link
Contributor

@RAOF RAOF left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks reasonable, with the exception of the log timestamp format that I'm not sure about. (I shall check!)

Copy link
Contributor

@AlanGriffiths AlanGriffiths left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While there are advantages to std::format() and std::print() they are not always appropriate. Which means every change site needs to be examined carefully: Is it noexcept? Is it async-signal-safe? Is it performance critical?

AFAICS there's only one place where this is an issue here, but I would err to the cautious side: not improving things where there is any doubt.

There are a couple of places where the narrow window of changes proposed could be expanded to incorporate improvements to the surrounding code.

Comment on lines +86 to +89
auto now = std::chrono::system_clock::now();
auto now_us = std::chrono::time_point_cast<std::chrono::microseconds>(now);
auto local = std::chrono::zoned_time{std::chrono::current_zone(), now_us};
std::string fmt_now = std::format("{:%F %T}", local);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than creating a std::string via std::format() it would be more efficient and clearer to format directly to out using std::print() (replacing the current streaming operator useage).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did most of this, except that std::print does not work with std::ostream

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::print does not work with std::ostream

It should: https://en.cppreference.com/w/cpp/io/basic_ostream/print.html

I guess it was missing with g++-13:

Formatted output 	P2093R14 	14.1 	__cpp_lib_print >= 202207L

A pity you're looking just now: In a couple of months we will be able to use it as we will switch our baseline support to Ubuntu 26.04 (g++-15)

Copy link
Contributor Author

@yotam-medini yotam-medini Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed that. I was looking at:
print
This std::print(ostream, ...) is indeed in C++-23 standard but current g++ does not support it

Comment on lines +202 to +206
std::string path;
path = std::format(x11_lock_fmt, xdisplay);
unlink(path.c_str());
path = std::format(x11_socket_fmt, xdisplay);
unlink(path.c_str());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know it is paranoia: but I feel uncomfortable allocating memory in noexcept code (like a destructor).

("Paranoia" because on any system this code is likely to run on we won't get bad_alloc - memory will be overallocated and using it will kill the processes.)


I know it would mean restructuring the code somewhat, but couldn't we cache the paths in the constructor so that they are only formatted in one place?

For example: introducing a class that formats (and remembers) these paths while providing the Fd would consolidate this logic.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use automatic buffer, and format_to_n

constexpr std::string_view x11_lock_fmt = "/tmp/.X{}-lock";
constexpr std::string_view const x11_socket_fmt = "/tmp/.X11-unix/X{}";

// TODO this can be written with more modern c++
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we now remove the TODO?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AlanGriffiths - Is there still something left to be addressed ?

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR modernizes string formatting across Mir by replacing fixed-size snprintf(buffer, ...) usage with std::format, aligning with issue #4660 and reducing manual buffer management in multiple components (tests, examples, server logging).

Changes:

  • Replace multiple snprintf()-built paths/messages with std::format() strings.
  • Update call sites that require C strings (printf, popen, open, unlink, etc.) to use .c_str().
  • Refactor a few call sites to keep formatted strings alive where a const char* is stored/passed later.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
tests/performance-tests/test_glmark2-es2.cpp Switch test log filenames to std::format, adjust printf() usage, keep host log filename alive via std::string.
tests/performance-tests/test_compositor.cpp Switch server log filename to std::format.
tests/mir_test_framework/test_wlcs_display_server.cpp Switch WAYLAND_DISPLAY value construction to std::format.
src/server/report/logging/compositor_report.cpp Switch compositor report log messages to std::format.
src/server/frontend_xwayland/xwayland_spawner.cpp Switch X11 lock/socket path construction to std::format / format_to_n.
src/core/logging/logger.cpp Switch timestamp formatting to chrono + std::format.
src/common/logging/input_timestamp.cpp Switch input timestamp formatting to std::format.
examples/mir_demo_server/server_example_test_client.cpp Switch client-status messages to std::format.
examples/mir_demo_server/server_example.cpp Switch apport command construction to std::format.

Comment on lines +41 to +49
constexpr std::string_view x11_lock_fmt = "/tmp/.X{}-lock";
constexpr std::string_view const x11_socket_fmt = "/tmp/.X11-unix/X{}";

// TODO this can be written with more modern c++
int create_lockfile(int xdisplay)
{
char lockfile[256];
auto const lockfile = std::format(x11_lock_fmt, xdisplay);

snprintf(lockfile, sizeof lockfile, x11_lock_fmt, xdisplay);

mir::Fd const fd{open(lockfile, O_WRONLY | O_CLOEXEC | O_CREAT | O_EXCL, 0444)};
mir::Fd const fd{open(lockfile.c_str(), O_WRONLY | O_CLOEXEC | O_CREAT | O_EXCL, 0444)};
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::format()'s first parameter is a std::format_string<...> (intended for string literals) and generally won't accept a std::string_view variable like x11_lock_fmt. This is likely a compile error with standard library implementations. Use a std::format_string<int> constant (constructed from the literal) or inline the string literal in the std::format() call.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While it is probably better to use std::format_string<> constants over std::string_view I don't think this will fail to compile.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each of these format strings are is used twice.
Once with: int xdisplay, once with int xdisplay.
So we cannot have one flexible definition like std::format_string<int | int&>'

Comment on lines +143 to +152
auto eoln = std::format_to_n(addr.sun_path + 1, sizeof(addr.sun_path) - 2,
x11_socket_fmt, xdisplay).out;
*eoln = '\0';
path_size = eoln - (addr.sun_path + 1);
create_socket(result, &addr, path_size);

path_size = snprintf(addr.sun_path, sizeof(addr.sun_path), x11_socket_fmt, xdisplay);
eoln = std::format_to_n(addr.sun_path, sizeof(addr.sun_path) - 1,
x11_socket_fmt, xdisplay).out;
*eoln = '\0';
path_size = eoln - addr.sun_path;
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::format_to_n() also expects a compile-time checked std::format_string<...>; passing x11_socket_fmt (a std::string_view) is likely to fail to compile. Consider making x11_socket_fmt a std::format_string<int> (or inline the literal) so format_to_n() can be instantiated.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see above

Comment on lines +86 to +89
auto now = std::chrono::system_clock::now();
auto now_us = std::chrono::time_point_cast<std::chrono::microseconds>(now);
auto local = std::chrono::zoned_time{std::chrono::current_zone(), now_us};
std::string fmt_now = std::format("{:%F %T}", local);
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This timestamp construction can throw (e.g., std::chrono::current_zone()/zoned_time when the tzdb is missing/corrupt, or std::format on allocation failure). Since format_message() is used by the logging path without exception handling, an exception here can terminate the process. Please make this path non-throwing (e.g., wrap in try/catch and fall back to a simpler timestamp such as UTC, or revert to the previous clock_gettime/strftime approach).

Suggested change
auto now = std::chrono::system_clock::now();
auto now_us = std::chrono::time_point_cast<std::chrono::microseconds>(now);
auto local = std::chrono::zoned_time{std::chrono::current_zone(), now_us};
std::string fmt_now = std::format("{:%F %T}", local);
std::string fmt_now;
try
{
auto now = std::chrono::system_clock::now();
auto now_us = std::chrono::time_point_cast<std::chrono::microseconds>(now);
auto local = std::chrono::zoned_time{std::chrono::current_zone(), now_us};
fmt_now = std::format("{:%F %T}", local);
}
catch (...)
{
auto now = std::chrono::system_clock::now();
auto now_time_t = std::chrono::system_clock::to_time_t(now);
char buf[32]{};
std::tm tm{};
if (gmtime_r(&now_time_t, &tm) != nullptr &&
std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tm))
{
fmt_now = buf;
}
else
{
fmt_now = "unknown-time";
}
}

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done as suggested.

char buffer[sizeof format];
snprintf(buffer, sizeof buffer, format, signal);
ml::log(ml::Severity::informational, buffer, component);
auto const msg = std::format(" Client terminated by signal {}", signal);
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The log message has a leading space (" Client..."), which looks unintentional and changes the output compared to the previous message text. Remove the leading whitespace.

Suggested change
auto const msg = std::format(" Client terminated by signal {}", signal);
auto const msg = std::format("Client terminated by signal {}", signal);

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

char buf[32]{};
std::tm tm{};
if (gmtime_r(&now_time_t, &tm) != nullptr &&
std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tm))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No milliseconds?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

microseconds added in recent commit

when_ns, milliseconds, sub_milliseconds, sign_suffix);

return std::string(str);
return fmt_now;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for fmt_now - just return directly.

"/tmp/%s_%s_host.log",
s_host_output_filename = std::format("/tmp/{}_{}_host.log",
test_info->test_case_name(), test_info->name());
host_output_filename = s_host_output_filename.c_str();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem necessary anymore - can't we just use s_host_output_filename.c_str() in all cases below?

<< std::endl;

out << std::format("[{}] {}{}: {}\n",
fmt_now, lut[static_cast<int>(severity)], component, message);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By removing the use of std::endl we are no longer flushing the output stream - is that intentional?

Copy link
Contributor

@robert-ancell robert-ancell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a mix of simple and more complicated changes in here. As these changes are independent of each other I suggest you split this into a number of PRs. Some changes can then land quickly and others that require more thought can take longer.

@yotam-medini
Copy link
Contributor Author

@robert-ancell @RAOF @AlanGriffiths
For sure, I want to address all robert-ancell's remarks.
But in light of his last of his split-PR suggestion,
let's have some consensus from all current reviewers - if to split, and how.

This is my first PR for MIR. I would like to proceed efficiently

@robert-ancell
Copy link
Contributor

The easiest way to split is to grab a simple case like in examples/mir_demo_server/server_example.cpp and make a new PR for that. Once that lands then merge this branch with master and it will get smaller. Keep iterating until this PR becomes smaller and thus easier to review.

@yotam-medini yotam-medini marked this pull request as draft March 6, 2026 09:00
@yotam-medini
Copy link
Contributor Author

yotam-medini commented Mar 6, 2026

Followed Robert Ancell advice, I converted this PR to draft for now.
Please see #4750

github-merge-queue bot pushed a commit that referenced this pull request Mar 7, 2026
Closes nothing.

<!-- Mention the issue this closes if applicable -->

Related: #4660

<!-- List any PRs, issues, and links that might benefit reviewers build
context around your work -->

Related to PR #4736

<!-- List the major changes of this PR -->
std::format  instead of snprintf in server_example.cpp

## How to test

<!-- List how reviewers can poke at the addition in this PR -->

## Checklist

- [ ] Tests added and pass
- [ ] Adequate documentation added
- [ ] (optional) Added Screenshots or videos
github-merge-queue bot pushed a commit that referenced this pull request Mar 8, 2026
Closes nothing.

<!-- Mention the issue this closes if applicable -->

Related: #4660

<!-- List any PRs, issues, and links that might benefit reviewers build
context around your work -->

## What's new?

Related to PR #4736

<!-- List the major changes of this PR -->

std::format instead of snprintf in TestWlcsDisplayServer(...)

## How to test

<!-- List how reviewers can poke at the addition in this PR -->

## Checklist

- [ ] Tests added and pass
- [ ] Adequate documentation added
- [ ] (optional) Added Screenshots or videos
github-merge-queue bot pushed a commit that referenced this pull request Mar 9, 2026
Closes nothing.

<!-- Mention the issue this closes if applicable -->

Related: #4660

<!-- List any PRs, issues, and links that might benefit reviewers build
context around your work -->

## What's new?

Related to PR #4736
std::format instead of snprintf in test_compositor.cpp

<!-- List the major changes of this PR -->

## How to test

<!-- List how reviewers can poke at the addition in this PR -->

## Checklist

- [ ] Tests added and pass
- [ ] Adequate documentation added
- [ ] (optional) Added Screenshots or videos
github-merge-queue bot pushed a commit that referenced this pull request Mar 10, 2026
Closes nothing.

<!-- Mention the issue this closes if applicable -->

Related: #4660

<!-- List any PRs, issues, and links that might benefit reviewers build
context around your work -->

## What's new?

Related to PR #4736
std::format instead of snprintf in test_glmark2-es2.cpp

<!-- List the major changes of this PR -->

## How to test

<!-- List how reviewers can poke at the addition in this PR -->

## Checklist

- [ ] Tests added and pass
- [ ] Adequate documentation added
- [ ] (optional) Added Screenshots or videos
github-merge-queue bot pushed a commit that referenced this pull request Mar 10, 2026
)

Closes nothing

<!-- Mention the issue this closes if applicable -->

Related: to PR #4736
std::format instead of snprintf in server_example_test_client.cpp

<!-- List any PRs, issues, and links that might benefit reviewers build
context around your work -->

## What's new?

<!-- List the major changes of this PR -->

## How to test

<!-- List how reviewers can poke at the addition in this PR -->

## Checklist

- [ ] Tests added and pass
- [ ] Adequate documentation added
- [ ] (optional) Added Screenshots or videos
github-merge-queue bot pushed a commit that referenced this pull request Mar 10, 2026
Closes nothing

<!-- Mention the issue this closes if applicable -->

Related: #4660

<!-- List any PRs, issues, and links that might benefit reviewers build
context around your work -->

Related to PR #4736

## What's new?

std::format instead of snprintf in input_timestamp.cpp

<!-- List the major changes of this PR -->

## How to test

<!-- List how reviewers can poke at the addition in this PR -->

## Checklist

- [ ] Tests added and pass
- [ ] Adequate documentation added
- [ ] (optional) Added Screenshots or videos
github-merge-queue bot pushed a commit that referenced this pull request Mar 11, 2026
Closes nothing

<!-- Mention the issue this closes if applicable -->

Related: #4660

<!-- List any PRs, issues, and links that might benefit reviewers build
context around your work -->

Related to #4736

## What's new?

std::format instead of snprintf in compositor_report.cpp

<!-- List the major changes of this PR -->

## How to test

<!-- List how reviewers can poke at the addition in this PR -->

## Checklist

- [ ] Tests added and pass
- [ ] Adequate documentation added
- [ ] (optional) Added Screenshots or videos
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

Successfully merging this pull request may close these issues.

Use std::Format instead of char buffers

5 participants