The main function should be shunned

The main function (in languages that have it) is…. special. It’s the entry point of the program by convention, there can only be one of them in all the object files being linked, and you can’t run a program without it. And it’s inflexible.

Its presence means that the final output has to be an executable. It’s likely however, that the executable in question might have code that others might rather reuse than rewrite, but they won’t be able to use it in their own executables. There’s already a main function in there. Before clang nobody seemed to stumble on the idea that a compiler as a library would be a great idea. And yet…

This is why I’m now advocating for always putting the main function of an executable in its own file, all by itself. And also that it do the least amount of work possible for maximum flexibility. This way, any executable project is one excluded file away in the build system from being used as a library. This is how I’d start a, say, C++ executable project from scratch today:

#include "runtime.hpp"
#include <iostream>
#include <stdexcept>

int main(int argc, const char* argv[]) {
    try {
        run(argc, argv); // "real" main
        return 0;
    } catch(const std::exception& ex) {
        std::cout << "Oops: " << ex.what() << std::endl;
        return 1;
    }
}

In fact, I think I’ll go write an Emacs snippet for that right now.

Advertisement
Tagged ,

5 thoughts on “The main function should be shunned

  1. Another nice side effect of this point of view is that you design your app from the first moment as a library.

    I would say that main should have arg parsing in it, and the args should be put into some kind of config struct that is fed to your library.

    This is a common idiom in Go. For example, the linter is in a library: https://godoc.org/github.com/golang/lint and the main for it parses args, calls the library, and then interprets the results of the library, in this case filtering results according to an argument (minConfidence). See https://github.com/golang/lint/blob/master/golint/golint.go

    • atilaneves says:

      Yeah, in practice I usually have an Options struct that gets parsed from the args then pass that in. I probably should have written that instead.

  2. mortoray says:

    Why not just extract functionality into a library as you need it there. There’s often a lot of support code for `main` that will never need to be shared. Trying to plan ahead this way might just risk complicating things for zero future gains.

    • atilaneves says:

      The support code for main always has to be shared, in my experience. Every time I’ve thought “this doesn’t need to be part of the library”, I was wrong.

      The only downside to doing it this way is your binaries get bigger, but that’s hardly ever a consideration, and even if it were, link statically and Bob’s your uncle.

  3. This is a greeat post

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: