Jez Higgins

Freelance software generalist
software created
extended or repaired


Older posts are available in the archive or through tags.

Feed

Follow me on Twitter
My code on GitHub

Contact
About

Tuesday 19 November 2019 The Forest Road Reader, No 2.37 : STinC++ - echo

Chapter 2 of Software Tools in Pascal is entitled Filters, and thus far it’s been absolutely focused on processing streams of text. We’ve substituted tabs for spaces, messed around with printer pre-processing, compressed and expanded text files. Where on earth could we be going next? I’m going to bet you wouldn’t expect something that’s very decidedly not a filter in any shape or form.

PROGRAM

echo echo arguments to output

USAGE

echo [ argument …​ ]

FUNCTION

echo copies its command line arguments to its output as a line of text with one space between each argument. If there are no arguments, no output is produced,

EXAMPLE

echo hello world!
hello world!

Weird huh?

In many ways, this is a little chill-out program - a light palate cleanser ahead of the big meaty one coming up next. It also allows Kernighan and Plauger to go off for a page on Pascal’s, or at least standard Pascal’s, weaknesses as a proper programming language. They don’t put it in quite those terms, but they bemoan its lack of provision for command line argument handling and, more seriously, the weaknesses of its type system, particularly its lack of dedicated string type. They work around the string type problem by using a fixed length array with an end-of-string sentinel, and “pay the price of wasted storage”. They are rather more coy on how they solved the command line argument problem, other than to say they’ve defined a primitive

function getarg(n : integer; var charstr : string; maxsize : integer) : boolean

which can grab the ith command line argument. echo then is a few lines to demonstrate getarg and their string type.

This is one of the few places where Software Tools in Pascal differs in any significant way from Software Tools. As far as I’m aware neither FORTRAN nor PL/1 had command line argument handling or a string type either, but the corresponding place in chapter 2 of Software Tools instead presents a trivial text encryption program. That program also uses a getarg primitive of exactly the same sort, but it goes entirely unremarked.

C++ provides ready access to command line arguments and (setting aside the char* legacy) offers a serviceable string type, and so echo offers little challenge.

echo.cpp
    void echo(int argc, char const* argv[], std::ostream &out) {
        auto arguments = make_arguments(argc, argv); (1)
        join(
            arguments,
            std::ostream_iterator<std::string>(out)
        ); (2)
    }
  1. While char const* argv[] has a certain retro charm, I like my data to be in a nice container that knows things like its own size. make_arguments simply shuffles the contents of argv into a std::vector<std::string>. C++ can infer the type of the variable arguments for me, hence I can declare it as auto. At compile time, the compiler will cross out auto and write in the std::vector<std::string> for me.

  2. And now we can pour those arguments, interspersed with spaces, down the out stream. Job done. Sorry, pardon? join you say?

Post-it note reading Do The Simplest Thing That Could Possible Work, Refactor Merciliessly, Remember You Aren’t Going To Need It, Once And Only Once

In spite of having “Do The Simplest Thing That Could Possibly Work” taped to the side of my monitor (and I really do try to hold true to it), I chose to set that aside and indulge in a little standard library style nonsense.

join
    template<typename InputIt, typename OutputIt, typename Separator = std::string>
    OutputIt join(InputIt first, InputIt last, OutputIt dest, Separator sep = " ") {
        if (first != last)
            *dest++ = *first++;
        while (first != last) {
            *dest++ = sep;
            *dest++ = *first++;
        }
        return dest;
    }

You’re probably familiar with the general shape of join - handle the initial value in the sequence, then loop through rest, prefixing them with the separator. The declaration and return type follow the general form of standard library. It is worth just taking a moment to consider just how much is going in

            *dest++ = *first++;

I’m not sufficiently down in the details to be sure if there are four or five (or some other number) of separate operations occurring here. It’s something along the lines of

  • dereferencing the input iterator first, *first, yields a value.

  • first++ advances the input iterator to the next value in the sequence

  • assigning to *dest writes a value into the output iterator. In the case of echo that’s to standard output, but it could equally be another container, a file, or almost anything else.

  • dest++ advances the write position.

After many years, many years of waiting, the standard library is finally catching up with the fact that most of the time the sequence we’re operating over is already neatly wrapped up in a container. Having to spell out mycontainer.begin() and mycontainer.end() is a bit redundant and it would be nice if we could just lob the whole shebang over in one go. In preparation of C++ 20 then, I put together a range version of join. (I’m being overly flippant. This is the absolute least of what’s coming in ranges, which looks to be an important and exciting addition to the library.)

join on a range
    template<typename InputRange, typename OutputIt, typename Separator = std::string>
    OutputIt join(InputRange&& range, OutputIt out, Separator sep = " ") {
      return join(std::begin(range), std::end(range), out, sep);
    }

This is, I believe, the more-or-less canonical form of a range adapter function. Jonathan Boccara discusses why passing the range with a forwarding reference, InputRange&&, is the preferred form in Algorithms on Ranges. It’s a subtle business, and I’m happy to pass the heavy thinking off to some one else.

Source Code

Source code for this program, indeed the whole project, is available in the stiX GitHub repository. echo is program 5 of Chapter 2.

An old-school shell hack on a line printer describes hooking up an old printer to a Linux box as a proper old timey TTY - an actual teletypewriter. The screen you’re reading this on? It’s a pseudo-TTY - it’s just pretending to be a printer while this chap’s got the real deal just like Grandad used to have 🙂.


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


Jez Higgins

Freelance software generalist
software created
extended or repaired

Older posts are available in the archive or through tags.

Feed

Follow me on Twitter
My code on GitHub

Contact
About