Jez Higgins

Freelance software grandad
software created
extended or repaired


Follow me on Mastodon
Applications, Libraries, Code
Talks & Presentations

Hire me
Contact

Older posts are available in the archive or through tags.

Feed

Wednesday 15 July 2020 The Forest Road Reader, No 2.50 : STinC++ - archive create

STinC++ rewrites the programs in Software Tools in Pascal using C++

Up until now, when I sit down to write the next chunk of STinC++ I’ve already written the code. Consequently, I’m writing retrospectively, I know what I’ve done, which blind alleys I went down and came back from and so on. I can fit it all together and tell a nice little story. Not so today - I’m writing the code as I go.

Creating an archive

As I mentioned yesterday, archive is a pretty hefty program by the standards of what we’ve encountered so far, so I’m taking it one piece at a time. I’m starting with archive creation, because if we haven’t got an archive file to play around with none of the other functionality comes into play anyway.

In their description of the program, Kernighan and Plauger have take one of the design decisions away from us

An archive is a concatenation of zero or more entities, each consisting of a header and an exact copy of the original file. The header format is

-h- name length

Our archive will, then, distribute its table of contents through the archive file. We can think about how this might make certain operations easier, perhaps at the expense of time efficiency. Given the time we’re talking about is probably going to be measured in the milliseconds, I’m happy with their choice. It feels more straightforward to implement. Let’s find out!

To begin with, I’m not going to worry about files at all. Here’s a test.

SECTION("no input files creates empty archive") {
  std::ostringstream archiveOut;

  create_archive(archiveOut);

  auto archive = archiveOut.str();
  REQUIRE(archive.empty());
}

All I know at the moment is I want my archive to write out to a stream. Not only have I said those no input, I haven’t even described how the input’s going to get in there. That’s not going to be enough, but I’ll find everything else out as I go.

Of course, it doesn’t compile yet but that’s ok. Let’s make it compile.

namespace stiX {
  void create_archive(
    std::ostream& archive_out
  ) {

  } // create_archive
}

Boom. Let’s commit that. Now what? What’s the next smallest step? Adding one file of zero length. Given what we know about our file structure, if our input is the zero length file nothing, the output archive file must be

-h- nothing 0

I don’t want to worry about where the file size comes from, so I’ll just pass that in along with the name. Something like this,

SECTION("one zero-length input file") {
  std::ostringstream archiveOut;

  auto input = stiX::input_file { "nothing", 0 };
  stiX::create_archive(input, archiveOut);

  auto archive = archiveOut.str();
  REQUIRE(archive == "-h- nothing 0\n");
}

In the Acknowledgments of his book Test-Driven Development By Example, Kent Beck writes

Finally, to the unknown author of the book which I read as a weird 12-year-old that suggested you type in the expected output tape from a real input tape, the code until the actual results matched the expected result, thank you, thank you, thank you.

That does appear to be pretty much the exercise I’m engaged in here. I’ll perhaps save you from every step of every single commit I make, lest I inadvertently reproduce the early chapters of that excellent book. If you’re feeling especially keen you can, of course, browse the commit history.

.create.hpp

namespace stiX {
  struct input_file {
    std::string const name;
    size_t const filesize;
  };

  void write_header(
    stiX::input_file const& input,
    std::ostream& archive_out
  ); // write_header

  template<typename FileReader> (1)
  void write_contents(
    stiX::input_file const& input,
    std::ostream& archive_out,
    FileReader fileReader
  ) {
    auto inputStream = fileReader(input.name);
    copy(inputStream, archive_out); (2)
  } // write_contents

  template<typename FileReader>
  void create_archive(
    std::vector<input_file> const &input,
    std::ostream &archive_out,
    FileReader fileReader
  ) {
    for (auto i : input) {
      write_header(i, archive_out);
      write_contents(i, archive_out, fileReader);
    } // for ...
  } // create_archive
} // namespace stiX

Well, it’s past bedtime and a few tests later this is where I am. Parameterising create_archive on the FileReader function has exactly the same motivation and explanation as when I pulled the same trick while working on include. The copy function in the middle is our old friend from right back when we began, popping up again. I’m slightly unhappy with the fact that the input structure already knows the file size, but makes a call to read the file. It feels like both should be function calls, or both should be known when we invoke create_archive. That’ll resolve itself in due course, I’m sure.

Kernighan and Plauger also started with creating an archive. They worked outside-in, so they spend a little bit of time setting up application scaffolding to parse arguments, call placeholder functions, and what not, before getting into the business of creating and updating an archive. They treat the two as the same operation, which may turn out to be true for us too in due course. As before, their code deals directly with the file system, rather than with the abstractions C++ allows us. At this point though, we’re in broadly similar places.

Endnotes

This whole endeavour relies on Software Tools in Pascal and Software Tools, both by Brian W Kernighan and PJ Plauger. I love these books and commend them to you. They’re both still in print, but new copies are, frankly, just ridiculously expensive. Happily, here are plenty of second-hand copies floating round, or you can borrow them from The Internet Archive using the links above.

For this project I’ve been using JetBrain’s CLion, which I liked enough to buy a license.

The test harness I’m using is Catch. I’ve been aware of Catch pretty much since it was first released, but this project is the first time really getting in and using it. I like it and will use it again.


Tagged code, and software-tools-in-c++


Jez Higgins

Freelance software grandad
software created
extended or repaired

Follow me on Mastodon
Applications, Libraries, Code
Talks & Presentations

Hire me
Contact

Older posts are available in the archive or through tags.

Feed