Setting up a modern C++ dev environment

C++
Author

Quasar

Published

May 9, 2026

Introduction

This blog post is a guide on setting up a modern C++ development environment. This can be especially helpful, if you are getting started with C++.

Here is a very brief primer on building C++ code.

C++ Compiler

A compiler translates C++ source code into low-level machine code (targeted to a specific architecture, for example x86_64, ARM etc). More specifically, each source file is translated into an object file, that contains machine code. Object files are little pieces of machine code, that can be linked together.

A function declared with external linkage can be referred to from the scopes of other translation units.

When a compiler compiles a call to such a function(or more generally symbols) foo() in the this translation unit, it has no idea where foo() is defined, so it leaves a placeholder in all calls to foo() in the object file.

When a compiler compiles (the definition of) a function with external linkage, the compiler writes the machine code of that function at some memory location, and puts the address of that memory location and the function name in .o file, where the linker can find it.

gcc/g++, clang/clang++, msvc are examples of major compilers.

Linker

The basic job of a linker is simple : it binds symbol references such as calls to foo() to the specific memory address of the function foo()’s definition.

The linker takes as its input, a set of input object files, other libraries and produces as its output a library or executable. Each input file contains a set of segments, contiguous blocks of code or data to be placed in the output file. Each input file also contains at least one symbol table. Some symbols are exported - defined within thew file for use in other files, generally the names of routines within that file that can be called from elsewhere. Other symbols are imported - used in the file but not defined, generally the names of routines called from but not present in the file.

When a linker runs, it first scans the input files to find the sizes of the segments and to collect the definitions and references of all of the symbols. It creates a segment table listing all of the segments defined in the input files and a symbol table of all symbols imported/exported.

The second pass uses the information collected in the first pass to control the actual linking process. It reads and relocates the object code, substituting numeric addresses for symbol references and adjusting memory addresses in code and data to reflect relocated segment addresses, and writes the relocated code to the output file.

A static library (.a files) is just a little more than a set of object code files catenated together. Shared (dynamic) libraries move some of the work from link time to load time. The linker identifies the shared libraries gthat resolve the undefined names in a linker run, but rather than linking anything into the program, the linker notes in the output file the names of the libraries in which symbols were found, so that the shared library can be bound when the program is loaded.

Loader

Loading is the process of bringing program into main memory.

Build system

While a hobby project starts out simple enough that you can build it just using a compiler, as your system grows more complex,

Toolchain

All of the above programming tools combined together form a toolchain (a suite of tools used in a serial manner)