Compiling

Or more precicely, HOW DO I RUN MY CODE???

Before we talk about C++ in particular, lets take a quick detour and talk about running any code on a computer. Computers have a specific “language” that they understand - and this is communicated via a specific set of instructions expressed through an ISA (Instruction Set Archtecture). Different computers can have different ISAs. You’ve probably heard of things like Arm and Intel. They tend to have a specific ISA that they operate with. What this means is that if you want to run a program on that system, you need to “translate” your code into some machine level code that is compatible. Something like:

std::cout << "Hello World!" << std::endl;

doesn’t really mean anything to a computer. But something like:

        push    rbp
        mov     edx, 12
        mov     esi, OFFSET FLAT:.LC0
        mov     edi, OFFSET FLAT:std::cout
        call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
        mov     rax, QWORD PTR std::cout[rip]
        mov     rax, QWORD PTR [rax-24]
        mov     rbp, QWORD PTR std::cout[rax+240]

does.

Ain’t nobody got time to write code like that (well, some do - and we’ll go over that way later in this book!). So we instead have higher level languages like C++ that let us write code that’s still readable. But something still needs to convert our easy to read C++ into machine level code. That’s where a compiler comes in. A compilers job is to translate from one language to another. Notice that we say “one language to another” and not “one language to machine code”. There are compilers that can convert from one language into another that isn’t immediately useable by a computer. Compilers are also responsible for making sure that the code you write works for the intended computer archtecture. This means that you might have to compile a program with different targets (computer archtectures) in mind somethings. This is also why you can’t just run a program that was build for linux on windows, windows on mac, mac on linux, etc without doing something fancy. You generally have to compile them independently.

Alright, now back to talking about C++.

C++ is a compiled language. Since we can’t just hand C++ code to a computer, we need something that takes our marvelous code and convert it into something a computer can understand (like assembly language). That’s exactly what we’ll be doing as well. Below is a quick view of what this would look like:

alt text

The above flow: [C++ Code] -> [Command to compile] -> [Assembly code from the compiled output] -> [Running the program]

This will generally be the flow of every program we write going forward.

Okay, okay, enough with the background. How do I compile my code?

With some handy dandy C++ compilers!

There are a ton of compilers for C++. Here is a pretty long, but incomplete list.

In general, compilers try to stay ahead of the curve in terms of support and feature functionality but there are still differences between them. We will exclusively be discussing / using clang and gcc in this book, but you are welcome to use any at your disposal. It’s very possible that you might not be able to use either of these compilers and that’s okay. There are plenty of resources online on how to get setup if so. If you really don’t want to setup anything locally, you can also always use your friendly neighborhood website Compiler Explorer.

If you would like to follow along by developing locally (highly recommended), we suggest you pick a compiler to use and then we can march forward in our adventure in C++. It doesn’t really matter too much for our purposes what you pick at this stage. Usually your computer comes with a compiler already installed, but you may want to use something different (or a newer version). This process can be a little daunting. Using something like conda might help make this easier by isolating your changes from your broader computer. If you are interested in doing so:

  • conda: https://docs.conda.io/projects/conda/en/latest/user-guide/install/

    • gcc: https://anaconda.org/conda-forge/clang

    • clang: https://anaconda.org/conda-forge/gcc

The 10-15 mins it’ll take to get up to speeds on the basics of conda will definately be worth it long term: https://docs.conda.io/projects/conda/en/latest/user-guide/getting-started.html. The primary benefit being that you can isolate your enviroments so that if you do screw something up, it’s not a big deal. If you are a power user and want multiple compilers and versions, then conda is going to be even better. Just switch environments to switch compilers trivially!

So hopefully at this point you’ve either decided to develop locally or use Compiler Explorer. If you’ve decided on the latter, feel free to skim the below section.

How about we try compiling code with an actual example:

Lets start by creating a new file called “test.cpp”. Feel free to whip out whatever text editor is most accessible for you. Go ahead and copy the following code into that file:

#include <iostream> // for std::cout
#include <cstdlib> // for EXIT_SUCCESS

int main()
{
    // A simple program that will print "Hello world!" to the terminal
    // C++ provides std::cout which lets you pipe output to stdout.
    //   << is a specific operator, just like +, =, etc that you can overload
    //   someone went ahead and overloaded the << operator so that when you use it
    //   it'll write text to the target stdout.
    //   std::endl just says add a new line and flush
    std::cout << "Hello world!" << std::endl;
    
    // A predefined value for a successful run
    // (so you don't have to figure out what to return when you are done with the main loop)
    return EXIT_SUCCESS;
}

Go ahead and save the file now. Now that we have some valid code, we can go ahead and compile it. Let’s go ahead and do precisely that.

To compile:

  • With gcc:

      g++        -std=c++17                        -o to_run        test.cpp
    
      [compiler] [standard library version to use] -o [output file] [input file]
    
  • With clang:

      clang++    -std=c++17                        -o to_run        test.cpp
    
      [compiler] [standard library version to use] -o [output file] [input file]
    

To run:

Running the above command should generate a file called to_run locally. Lets go ahead and run that by doing:

./to_run

and you should see this output:

Hello world!

What we showed above is actually just how to run the simplest of programs. This doesn’t really show you how to include other system libraries, how to add multiple files, or add special compiler flags. If you are curious about reading more about what goes on in this part of the world, feel free to look up “build systems C++” and you’ll find tons of resources related to make, cmake, and ninja to name a few.

That takes too long. Is there a faster way?

You bet! Automate all the things!

Compiling locally with tmux + vim + inotifywait

Online tools like Godbolt might not always be handy or accessible. In cases like this, it’s nice to have a local setup that will quickly compile your programs and display your output. We showed you how to compile a given C++ file, but we haven’t show you how you can automate this process. What we’d like to be able to do is edit out code, and then once we save the file, we’d like to see the compiler output and (if successfully compiled) the program output. Here is an screenshot of what this would look like locally:

alt text

Commands being run:
  • Top left pane:

    •   vim test.cpp
      
  • Top right pane:

    •   watchman-wait *.cpp --max-events 0 | while read line; do echo "----------------------------" && date && ls -lart; done
      
  • Bottom pane:

    •   watchman-wait *.cpp --max-events 0 | while read line; do echo "----------------------------" && date && g++ -o to_run $line -std=c++17 && ./to_run; done
      

Tools you can use to accomplish this:

  • tmux:

    • is an open-source terminal multiplexer for Unix-like operating systems.

    • To install:

      • Ubuntu / Debian:

        •   sudo apt-get install tmux
          
      • Fedora:

        •   sudo dnf -y install tmux
          
      • Centos:

        •   sudo yum -y install tmux
          
      • OSX:

        •   brew install tmux
          
  • Vim:

    • (a contraction of Vi IMproved) is a clone, with additions, of Bill Joy’s vi text editor program for Unix.

    • To install: https://www.vim.org/download.php

  • Something to watch for file changes:

Compilers vs. Interpreters

Note

TODO Talk about python vs. C++ example.

References:

Lots of wonderful people have publically shared information on the ins and outs of compilers, what they do, how they work, and how to best use them. This is definately a topic you can write multiple books on (and people have!). Here are a few references that we think are worth checking out if you are curious in this matter: