Monday 30 September 2024

C++ on Sea video for "Building on clang-tidy to move from printf-style to std::print-style logging and beyond" posted

cpponsea-clang-tidy

Back in July 2024 I was lucky enough to talk about my recent work implementing and applying a clang-tidy check at C++ on Sea 2024 in Folkestone. I think that the talk went well and the C++ on Sea team have now posted a recording which I haven’t dared watch myself. The slides are also available.

I’m expecting to present the talk for the online track at Meeting C++ on November 15th 2024.

Monday 23 September 2024

My Thirty Years of Linux

thirty-years-of-linux

I first installed Linux in the final week of September 1994 on my 486SX 25MHz computer, which for reasons that will take too long to explain had 20 megabytes of memory. This was a huge amount for the time when four megabytes was normal for such machines. During the summer before then I was developing 16-bit Windows 3.1 software using Visual C++ and Visual Basic.

I know I installed Slackware from a magazine cover CDROM (as they were known at the time), but I’ve been unable to find out which one. I’m reasonably sure that it was Slackware 1.2 from March 1994 - at least I think that the kernel version was 1.0.8 and that version would probably match the lead times for the CDROMs that I would have received by then.

I didn’t have access to the Internet at that point so all I could do was play around with the software included in the distribution. My editor of choice at that point was Microemacs. I had first encountered Microemacs on my Sinclair QL. On that machine it was rather too slow to actually use as an editor so I stuck with QD. I do remember being very impressed at how I could split the frame showing a single buffer into two windows and text I typed in one would lazily appear in the other though. It was a simpler time! Once I got the aforementioned 486SX PC I started using Microemacs under DOS as my primary editor, even though I was mostly running it full screen under Windows 3.1. I believe that I actually took the source code for Microemacs 3.9 from my QL C68 discs across to my new Linux installation to compile it there. This would mean that I never compiled Microemacs from source on DOS myself and only used binaries downloaded for me from CIX. That might be false though and I actually had the source for a later DOS version and compiled that. To my great surprise it compiled and ran on Linux quite happily!

I played around for a while. I remember trying to run XFree86 but not knowing about startx so all I got was a blank screen with an X mouse pointer moving around when I ran the X binary directly.

My solo investigations of Linux were curtailed by me going away to university the following weekend and I couldn’t take my computer with me.

When I got to university I was faced with the university computer services department’s X terminals and remember being amazed in my first few days how I could use Mosaic to view web pages about Linux for free! I discovered the Linux Documentation Project and made use of the computer services line printer to print documentation that I could take back to my room and read at my leisure. I started learning a lot about Unix.

I believe that Microemacs was available on the university Computing Services Unix machines. Either that or I was able to download its source code using FTP and compile it myself quite quickly. (I remember that the editor recommended by lecturers in the Computer Science department was Jove at that point - another Emacs-like editor that I didn’t use much.) I think that I moved to GNU Emacs relatively quickly, but the default incremental search confused me greatly and I rebound the C-s to non-incremental search for a good while! It wasn’t long before I started using XEmacs like the other cool kids (which continued until I noticed that Emacs had improved greatly in about 2006.)

Being on a joint Maths and Computer Science degree course meant that I was mostly doing Maths with only a small amount of Computer Science. This meant spending a lot of time up Gibbet Hill in the Maths department which had a small computer room with about six Sun Sparcstation 2s in it. Maths students in higher years hanging out in that room helped me to improve my configuration and I started using vtwm rather than the default twm X window manager and tinkered with its configuration myself. I later moved to fvwm 1 and later fvwm2. I also realised that there wasn’t enough Computer Science in my course and I switched to just Computer Science course from the second year.

I remember returning home one weekend during the term and working on a program that would eject the CD in the CD drive connected to my SoundBlaster 16. I think that I also messed around with SVGATextMode to use the 132x30 text-mode resolution on my Trident graphics card that I had previously used in Windows full-screen DOS sessions.

I continued tinkering with Linux over the Christmas holiday and when I went back to university I was able to take my computer with me to use in my university accommodation. Halls didn’t provide Internet connections back then. I remember trying to persuade the university that this would be a good idea in the hope that they would do so in time for my third year when I stood a chance of being back on campus. I failed in that. Having access to the Internet through the university meant that I could download source code to build and install on my Linux box but I had to carry it on 3.5 inch floppy disks back and forth to my Linux PC. I had to learn how to split tarballs across multiple floppies and cope with the common occurrence of being unable to read files written on the Sun floppy drives on my PC. This was a common problem for others too and no-one ever had a good explanation for it.

At some points during the first year I upgraded to newer Slackware versions and compiling my own newer 1.1.x development kernels. By the second year I had started running the 1.3.x development kernels and remember being excited as the 2.0 kernel approached. I reinstalled Slackware again from scratch to migrate from a.out to ELF format binaries.

Access to the Internet meant access to the comp.os.linux.* USENET hierarchy which I participated in eagerly. Borrowing the O’Reilly books from the library I learnt how to run BIND for DNS, INN for news and how to tweak my XF86Config to get the best resolution at a sensible refresh rate out of my monitor. Along with my housemate in the second year we set up an IP network and we managed to share his dial-up Internet connection to a certain extent. It was set to dial up automatically in the early morning and if I left my computer on over night email would arrive at it and the beep of xbiff would wake me up! This was a time when you could still forward email through another machine by using % in the local part!

Over those three years at university I learnt an awful lot about Linux but when I got back I home I knew that my Slackware install that I’d installed newer versions of INN, the kernel and various other packages on wasn’t really sustainable. I had heard about Debian and decided to complete reinstall using that. I must have ordered a CD containing Debian 1.3 “Bo” from The Linux Emporium. I think that I upgraded to Debian 2.0 “Hamm” slightly by accident because by that point my Linux PC was connected to the Internet permanently, though rather slowly. By that point I was working on the empeg Car MP3 player and Linux was fully entrenched in my life both in the products I was working on and the operating system I was using to build them.

I had no idea when I first installed Linux thirty years ago that it would influence my career from that point onwards. I’m glad I did!

Saturday 21 September 2024

New modernize-use-std-format clang-tidy check and modernize-use-std-print improvements in Clang 19

clang-tidy-modernize-use-std-format

Clang 19 has recently been released. It includes my new modernize-use-std-format check and fixes a bug in the modernize-use-std-print check that I added in Clang 17. (I also fixed another bug in the intervening Clang 18 release.)

The new modernise-use-std-format check by default turns calls to absl::StrFormat into calls to std::format. For example, it turns:

return absl::StrFormat("The %s is %d", description.c_str(), value);

into

return std::format("The {} is {}", description, value);

The full documentation for the check is available on the clang-tidy web site.

Although the default behaviour of the check is to replace calls to absl::StrFormat, many codebases have their own “wrap snprintf to return a std::string” function. Ours was called strprintf.

Given a file named example.cpp containing:

#include <string>

extern std::string strprintf(const char *format, ...);

std::string format_row(int quantity, const std::string &name, double mass, const std::string &description)
{
  return strprintf("| %3d | %10s | %6.3fkg | %-30s |\n", quantity, name.c_str(), mass, description.c_str());
}

int main()
{
  puts(format_row(4, "carrots", 0.25, "Long orange things").c_str());
  puts(format_row(9, "sprouts", 0.1, "Roundish green things").c_str());
}

then running

clang-tidy '-checks=-*,modernize-use-std-format' \
    -config="{CheckOptions: { modernize-use-std-format.StrFormatLikeFunctions: '::strprintf;' }}" \
    -fix example.cpp -- -std=c++20

will result in the file being rewritten to:

#include <format>
#include <string>                                                                                              'format' file not found [pp_file_not_found]

extern std::string strprintf(const char *format, ...);

std::string format_row(int quantity, const std::string &name, double mass, const std::string &description)
{
  return std::format("| {:3} | {:>10} | {:6.3f}kg | {:30} |\n", quantity, name, mass, description);
}

int main()
{
  puts(format_row(4, "carrots", 0.25, "Long orange things").c_str());
  puts(format_row(9, "sprouts", 0.1, "Roundish green things").c_str());
}

The check has done several things:

  1. The <format> header is now included. (The declaration of strprintf has not been removed since it may still be used in ways that could not be converted.)
  2. The strprintf call has been replaced with std::format.
  3. The format string has been rewritten to use the new C++ format language.
  4. The now-unnecessary calls to std::string::c_str() for name and description have been removed.

If you aren’t able to use C++20 yet then the check can be told to use fmt::format from the {fmt} library instead with something like:

clang-tidy '-checks=-*,modernize-use-std-format' \
    -config="{CheckOptions: { \
      modernize-use-std-format.StrFormatLikeFunctions: '::strprintf;', \
      modernize-use-std-format.ReplacementFormatFunction: 'fmt::format', \
      modernize-use-std-format.FormatHeader: '<fmt/core.h>' }}" \
    -fix example.cpp

You can play with the modernize-use-std-format and modernize-use-std-print checks using the excellent Compiler Explorer.

Thanks to everyone who reviewed and made suggestions for the new check and bug fixes. I have several further improvements to these checks in development that I hope will land in time for Clang 20.