Abstract¶
In this first lecture, we will aim to learn enough to run C++ programs. This will involve understanding the programming toolchain, which is a set of tools used by programmers to write, maintain, and build software.
Initialization¶
To ensure the notebook runs correctly, execute the following code each time the kernel is started or restarted to initialize the environment:
from __init__ import *This will import the necessary libraries such as ROOT for running a C++ interpreter.
Motivation¶
Interactive Jupyter Notebooks¶
If you accidentally wandered into this territory, run the following cell to see the full name of the course:
%%cpp
cout << string("CS2310") +
" Computer Programming\n";CS2310 Computer Programming
%%cpp is a cell magic from a data analysis framework ROOT for running code with a C++ interpreter cling. It is a wonderful tool for learning a “compiled” language C++, but without compilation!
%%cpp
cerr << "500 Internal Error: You ran your code without compiling again! 😡\n";500 Internal Error: You ran your code without compiling again! 😡
There is nothing wrong. The above code just prints to the standard error stream std::cerr instead of the standard output stream std::cout.
Let’s make the code more interactive:
%%cpp
bool ans;
cout << "You’re taking CS2310 this semester? [0:false, 1:true]\n";
cin >> ans;
cout << (ans
? "200 OK: Welcome aboard, brave coder! 🚀\n"
: "302 Found: Redirecting you to a more suitable course... 😅\n");You’re taking CS2310 this semester? [0:false, 1:true]
302 Found: Redirecting you to a more suitable course... 😅
Did you get redirected? The above program should normally get user input with the standard input stream cin. For this to happen:
Start a terminal with
File→New→Terminal.Run the command
rootEnter the above program line-by-line or statement-by-statement. (Different from a line, an expression statement is terminated by a semi-colon
;.)
If you really are taking this course, be warned that you are getting yourself into object-oriented programming (OOP). E.g., you will learn to define a class like the following:
%%cpp
#include<string>
#include<iostream>
/**
* @brief A CityUHK course.
*/
class Course {
private:
std::string code; ///< The code of the course.
std::optional<std::string> name; ///< The optional name of the course.
public:
/**
* @brief Constructs a new Course.
*
* @param code The course code.
* @param name The optional course name (default to nullopt).
*/
Course(std::string code, std::optional<std::string> name = std::nullopt)
: code(std::move(code)), name(std::move(name)) { }
/**
* @brief Gets the course code.
*
* @return The course code.
*/
inline const std::string& get_code() const {
return code;
}
/**
* @brief Sets the course code if the new code is not empty.
*
* @param new_code The new code to set.
*/
inline void set_code(const std::string& new_code) {
if (!new_code.empty()) { code = new_code; }
}
/**
* @brief Gets the course name.
*
* @return The the course name.
*/
inline const std::optional<std::string>& get_name() const {
return name;
}
/**
* @brief Sets the course name or removes it if the new name is empty.
*
* @param new_name The new course name to set.
*/
inline void set_name(const std::string& new_name) {
if (!new_name.empty()) {
name = new_name;
} else {
name = std::nullopt;
}
}
/**
* @brief Gets the course full name (code bollowed by name).
*
* @return The course full name.
*/
std::string get_full_name() const {
return name.has_value() ? code + " " + name.value() : code;
}
/**
* @brief Prints a welcome message for the course.
*/
void say_welcome() const {
std::cout << format("Welcome to {}!\n", this->get_full_name());
}
};😨... The hard work for defining a class pays off when you can create and use its objects easily:
%%cpp
Course cs2310("CS2310");
cs2310.say_welcome();Welcome to CS2310!
The objects can be modified/mutated. E.g., to set or update the name of the course:
ROOT.cs2310.set_name('Computer Programming')
ROOT.cs2310.say_welcome()Welcome to CS2310 Computer Programming!
To understand how the program works, use %%cpptutor to step through the code:
%%cpptutor
#include<string>
#include<iostream>
/**
* @brief A CityUHK course.
*/
class Course {
private:
std::string code; ///< The code of the course.
std::optional<std::string> name; ///< The optional name of the course.
public:
/**
* @brief Constructs a new Course.
*
* @param code The course code.
* @param name The optional course name (default to nullopt).
*/
Course(std::string code, std::optional<std::string> name = std::nullopt)
: code(std::move(code)), name(std::move(name)) { }
/**
* @brief Gets the course code.
*
* @return The course code.
*/
inline const std::string& get_code() const {
return code;
}
/**
* @brief Sets the course code if the new code is not empty.
*
* @param new_code The new code to set.
*/
inline void set_code(const std::string& new_code) {
if (!new_code.empty()) { code = new_code; }
}
/**
* @brief Gets the course name.
*
* @return The the course name.
*/
inline const std::optional<std::string>& get_name() const {
return name;
}
/**
* @brief Sets the course name or removes it if the new name is empty.
*
* @param new_name The new course name to set.
*/
inline void set_name(const std::string& new_name) {
if (!new_name.empty()) {
name = new_name;
} else {
name = std::nullopt;
}
}
/**
* @brief Gets the course full name (code bollowed by name).
*
* @return The course full name.
*/
std::string get_full_name() const {
return name.has_value() ? code + " " + name.value() : code;
}
/**
* @brief Prints a welcome message for the course.
*/
void say_welcome() const {
std::cout << format("Welcome to {}!\n", this->get_full_name());
}
};
int main() {
Course cs2310("CS2310");
cs2310.say_welcome();
return 0;
}The same program can also be shared and run in OPT_CPP, which compiles and runs C++ in the browser, so nothing needs to be installed. The following permalink reproduces exactly the program in the cell above:
The permalink is generated by the Permalink button in OPT_CPP’s Live Mode, which encodes the entire state of the program as query parameters in the URL fragment (the part after #).
For details, you can lookup a C++ reference manual cppmanlite, which is A lightweight, serverless C++ documentation lookup tool written in pure Python to search for any standard library function and get clean documentation instantly.
Agentic Coding¶
If you need immediate help, execute the %%hermes to send the cell content to a self-learning AI Agent called Hermes powered by a large language model (LLM).
%%hermes
Define a class `Course` in C++ for constructing different course objects by
providing their course code and, optionally, name.
The object should have:
- a method `say_welcome()` that prints "Welcome to {code} {name}!", if the
the course name is provided, or "Welcome to {code}!" otherwise.
- a `set_name` method that modifies the course name if provided a non-empty
string as an argument, or remove the course name if an empty string is given.🌿 Fork/new: main (will create on click)
Hermes[2]
The cleanest way to express “code required, name optional” in C++ is a defaulted argument (name = ""), treating an empty string as “no name.” That same empty-string convention makes set_name trivial: assign the string, and an empty one naturally removes the name.
#include <iostream>
#include <string>
class Course {
public:
// code is required; name is optional (empty string means "no name").
Course(std::string code, std::string name = "")
: code(std::move(code)), name(std::move(name)) {}
// "Welcome to {code} {name}!" when a name exists, "Welcome to {code}!" otherwise.
void say_welcome() const {
if (name.empty())
std::cout << "Welcome to " << code << "!\n";
else
std::cout << "Welcome to " << code << " " << name << "!\n";
}
// Assign the new name; passing an empty string removes it.
void set_name(const std::string& new_name) {
name = new_name;
}
private:
std::string code;
std::string name;
};
int main() {
Course c1("CS2310"); // code only
Course c2("CS2310", "Toolchain"); // code + name
c1.say_welcome(); // Welcome to CS2310!
c2.say_welcome(); // Welcome to CS2310 Toolchain!
c1.set_name("Toolchain"); // add a name
c1.say_welcome(); // Welcome to CS2310 Toolchain!
c2.set_name(""); // remove the name
c2.say_welcome(); // Welcome to CS2310!
return 0;
}Design notes:
Optional name via default argument —
Course("CS2310")andCourse("CS2310", "Toolchain")both work, matching “providing their course code and, optionally, name.”Empty string = no name — one shared sentinel for both the constructor and
set_name, so “remove the course name if an empty string is given” is just an assignment; no special-casing needed.say_welcomeisconst(it only reads state), andset_nametakes its argument byconst&.
One alternative worth knowing: if you’d rather distinguish “no name given” from “name set to empty” as distinct states, use std::optional<std::string> for name instead of the empty-string sentinel — but for this spec the empty-string approach is simpler and reads better.
You can also use a chat interface to chat with the Hermes persona. Try introducing yourself in a chat. Hermes can remember you.
Let’s rename the course:
@interact(name="Computer Programming Design")
def update_cs2310(name):
ROOT.cs2310.set_name(name)
print(ROOT.cs2310.get_full_name())You can interact with the program by modifying the text box above. (Grab your chance to change the course name to whatever you like!)
Troubleshooting
For the above program to work, we need to import the interact object from ipywidgets as well, which should have been done by executing the initialization script:
...
from ipywidgets import interact
...If you forgot to run the initialization cell after the kernel starts/restarts, a simple way to run it is to open a new view of the notebook:
Right click on the notebook tab
Toolchain.ipynband select the entryNew View for Notebook.Execute the initialization cells in the new view, and close the view afterwards.
CILOs¶
Now we come to the important question:
What is the course intended learning outcomes (CILOs)?
You can read about them on the frontpage of the book, which is accessible without login:
To be more concrete, we hope that after completing the course, you will have honed the computational thinking skills needed to create a fantastic project like one of the following open-source projects:
Building C++ Projects with AI Agents¶
Two open-source C++ projects developed with the help of AI agents exemplify the intended learning outcome of this course — building C++ projects with agentic coding techniques:
jupyter-cpp-tutor: A Jupyter extension that provides the%%cpptutorcell magic used earlier in this notebook. It compiles C++ code withg++ -g, traces the execution with GNU Debugger (GDB), and renders a step-by-step visualization of the variables, the call stack, and the pointer relationships right inside a notebook cell.OPT_CPP: A serverless C++ visualizer that runs in the browser. It compiles and runs C++ using WebAssembly viaxeus-cpp, so nothing needs to be installed. It also includes an Ask AI feature powered by WebLLM that runs an LLM in the browser to help identify bugs.
Both projects were motivated by the challenge of sharing and debugging C++ programs, faced by students of this course. They can be built within a short period of time using locally deployed Agentic Coding tools. The AI agents did most of the building in a inner agent loop, while we test the build and report errors to AI to fix in an outer human-agent loop. In this course, you will get to use the AI tools to build your own project!
History of C++¶
C++ is a language with a long and rich history. Before we dive into its toolchain, you may want to take a look at this documentary about the people and events that shaped the language:
Compilation¶
A C++ program is typically compiled into machine code before it can be executed directly by the CPU/GPU/NPU. The compilation process is complex because it must target different platforms (such as various architectures and operating systems) and serve different purposes (such as release versus debugging builds).
How to run a C++ program?¶
To illustrate this process, run the following cells to create the source code of a hello-world program:[4]
!mkdir -p private # Create a private directory for files to be generated%%writefile 'private/hello.cpp'
#include <iostream>
#include <format>
int main() {
auto who="World";
std::cout << std::format("Hello, {}!\n", who);
return 0;
}Overwriting private/hello.cpp
The C++ source code file is called hello.cpp.[5] The following shell command uses a C++ compiler to compile the source code to an executable (file).
%%bash
# change directory
cd private && \
# compile the source
clang++ \
-fuse-ld=lld \
--std=c++23 \
--stdlib=libc++ \
hello.cpp \
-o hello && \
# list the files
ls -la hello.cpp hello-rwxr-xr-x 1 jovyan users 322376 Sep 3 20:12 hello
-rw-r--r-- 1 jovyan users 141 Sep 3 20:12 hello.cpp
From the output, note that:
The size of the executable
hellois significantly larger than that of the source codehello.cpp.The file permission string
-rwxr-xr-xindicates that the file is executable by the user, group, and others, as denoted by thexcharacters in the three different locations.
To run the executable:
%%bash
./private/helloHello, World!
To see the returned value, run the following cell right after executing the hello-world program.
%%bash
echo ${?}0
How does the original source code translate into an executable?¶
The compilation can be broken down into several steps.
Step 1: Preprocessor¶
The first step expands the source code to include the content from the required header files:
%%bash
cd private && \
clang++ --std=c++23 --stdlib=libc++ -E hello.cpp -o hello.ii && \
ls -la hello.cpp hello.ii-rw-r--r-- 1 jovyan users 141 Sep 3 20:12 hello.cpp
-rw-r--r-- 1 jovyan users 2456207 Sep 3 20:21 hello.ii
Solution to Exercise 1
The function is located at the end of the file. The preprocessor directive is substituted by the content of the header file /usr/lib/llvm-22/bin/../include/c++/v1/iostream.
Step 2: Compiler¶
Then, the expanded source code is converted to an assembly code in the so-called code generation phase.
%%bash
cd private && \
clang++ --std=c++23 -S hello.ii -o hello.s && \
ls -la hello.ii hello.s-rw-r--r-- 1 jovyan users 2456207 Sep 3 20:21 hello.ii
-rw-r--r-- 1 jovyan users 2817038 Sep 3 20:25 hello.s
Note that the compiled code is much smaller than the original source code. We can use the optimization flag -O to instruct the compiler to apply various optimization techniques to improve the generated code for different purposes:
%%bash
cd private
clang++ --std=c++23 -S -O0 hello.ii -o hello_O0.s # no optimization (for debugging)
clang++ --std=c++23 -S -O1 hello.ii -o hello_O1.s # between -O0 and -O2
clang++ --std=c++23 -S -O2 hello.ii -o hello_O2.s # moderate level for stability and size in addition to performance
clang++ --std=c++23 -S -O3 hello.ii -o hello_O3.s # maximum optimization for performance
clang++ --std=c++23 -S -O3 -ffast-math hello.ii -o hello_Of.s # aggressive, might violate standards
clang++ --std=c++23 -S -Og hello.ii -o hello_Og.s # optimize while keeping debugging smooth
clang++ --std=c++23 -S -Os hello.ii -o hello_Os.s # optimize for size
clang++ --std=c++23 -S -Oz hello.ii -o hello_Oz.s # optimize for more size reduction in the expense of performance%%bash
ls -la private/hello*.s-rw-r--r-- 1 jovyan users 2817038 Aug 25 15:03 private/hello.s
-rw-r--r-- 1 jovyan users 2817038 Aug 25 15:07 private/hello_O0.s
-rw-r--r-- 1 jovyan users 804000 Aug 25 15:07 private/hello_O1.s
-rw-r--r-- 1 jovyan users 826868 Aug 25 15:07 private/hello_O2.s
-rw-r--r-- 1 jovyan users 823529 Aug 25 15:07 private/hello_O3.s
-rw-r--r-- 1 jovyan users 817314 Aug 25 15:07 private/hello_Of.s
-rw-r--r-- 1 jovyan users 931537 Aug 25 15:08 private/hello_Og.s
-rw-r--r-- 1 jovyan users 832152 Aug 25 15:08 private/hello_Os.s
-rw-r--r-- 1 jovyan users 1120786 Aug 25 15:08 private/hello_Oz.s
The two commonly used optimizations are:
-O3, which is generally good for software release as it attempts to rearrange the code as much as needed to make the final executable run faster, but such rearrangements make it difficult to debug the source code because the mapping from the machine code to the source is not preserved.-Og, which is good for debugging as it minimizes rearrangements of the source and retains function names and variable scopes, allowing a debugger to step through the execution and show relevant information from the corresponding source code.
Step 3: Asssembler¶
The assembly code is further converted to a machine code.
%%bash
cd private
clang++ -c hello.s -o hello.o
clang++ -c hello_O0.s -o hello_O0.o
clang++ -c hello_O1.s -o hello_O1.o
clang++ -c hello_O2.s -o hello_O2.o
clang++ -c hello_O3.s -o hello_O3.o
clang++ -c hello_Of.s -o hello_Of.o
clang++ -c hello_Og.s -o hello_Og.o
clang++ -c hello_Os.s -o hello_Os.o
clang++ -c hello_Oz.s -o hello_Oz.o%%bash
ls -la private/hello*.o-rw-r--r-- 1 jovyan users 617584 Aug 25 15:08 private/hello.o
-rw-r--r-- 1 jovyan users 617584 Aug 25 15:08 private/hello_O0.o
-rw-r--r-- 1 jovyan users 160728 Aug 25 15:08 private/hello_O1.o
-rw-r--r-- 1 jovyan users 163968 Aug 25 15:08 private/hello_O2.o
-rw-r--r-- 1 jovyan users 161856 Aug 25 15:08 private/hello_O3.o
-rw-r--r-- 1 jovyan users 160648 Aug 25 15:08 private/hello_Of.o
-rw-r--r-- 1 jovyan users 173848 Aug 25 15:08 private/hello_Og.o
-rw-r--r-- 1 jovyan users 171488 Aug 25 15:08 private/hello_Os.o
-rw-r--r-- 1 jovyan users 238528 Aug 25 15:08 private/hello_Oz.o
hello.o is called an object file, which is a binary file containing the binary machine code (opcode+operand) directly understandable by the processor.
Step 4: Linker¶
The final step is the linking process, which turns one or more object files into an executable.
%%bash
cd private && \
clang++ -fuse-ld=lld --stdlib=libc++ hello.o -o hello_againSolution to Exercise 3
Omitting --std=c++23 when expanding the source code does not fail immediately. However, subsequent conversion to assembly, whether the option --std=c++23 is specified or not, fails.
error: no member named 'format' in namespace 'std'
6 | std::cout << std::format("Hello, {}!\n", who);
| ~~~~~^This is because std:format from <format> require c++23 standard, but the default standard used by the compiler is currently C++17.
Omitting --stdlib=libc++ causes the linking process will fail with the error:
/opt/conda/bin/ld: cannot find -lstdc++: No such file or directory
clang++: error: linker command failed with exit code 1 (use -v to see invocation)This is because hello.o needs to be linked to the standard library in order to use the cout object.
%%hermes
Why use the option `-fuse-ld=lld`?🌿 Fork/new: main (will create on click)
Hermes
Building from Source¶
A C++ project often consists of multiple source code files and libraries that depend on one another in certain way. E.g.,
addmul.cpp defines the main application that uses functions from
io.cpp to get user input;
add.cpp to carry out addition of two numbers; and
mul.cpp to carry out multiplication of three numbers.
mymath.hpp and util.hpp are header files that declares the functions shared by multiple source code files.
To compile the main application, its dependencies must be compiled first. For large projects with complex interdependencies, manual compilation is both challenging and error-prone, which naturally calls for the following tool in the programming toolchain.
How to build a C++ project with CMake?¶
Let’s clean up all the build files in case you have run the Make command before:
!make clean-allrm -rf "build" bin/ lib/ .cache/
Next, run the following command to generate files in a build directory:
!make generate && ls -la build//usr/bin/cmake -B "build" -S . -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=
-- The CXX compiler identification is Clang 22.1.8
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/clang++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Setting build type to 'Debug' as none was specified.
-- Configuring done (2.6s)
-- Generating done (0.3s)
-- Build files have been written to: /home/jovyan/cs2310i26a/source/Lecture1/build
total 1
drwxr-xr-x 4 jovyan users 8 Sep 3 20:48 .
drwxr-xr-x 13 jovyan users 24 Sep 3 20:48 ..
-rw-r--r-- 1 jovyan users 14092 Sep 3 20:48 CMakeCache.txt
drwxr-xr-x 7 jovyan users 15 Sep 3 20:48 CMakeFiles
-rw-r--r-- 1 jovyan users 7624 Sep 3 20:48 Makefile
-rw-r--r-- 1 jovyan users 2319 Sep 3 20:48 cmake_install.cmake
-rw-r--r-- 1 jovyan users 2414 Sep 3 20:48 compile_commands.json
drwxr-xr-x 3 jovyan users 5 Sep 3 20:48 src
The above Make command executes the CMake command
/usr/bin/cmake -B "build" -S . -DCMAKE_CXX_COMPILER=clang++where
-B "build"specifies the build directory where CMake will generate the build system files and perform the build process.-S .specifies the source directory containing the CMake configuration file CMakeLists.txt.-DCMAKE_CXX_COMPILER=/usr/bin/clang++sets the C++ compiler to/usr/bin/clang++for compiling C++ source files.
Next, to compile the source code into binary files:
!make build && ls -la bin/ lib/ build//usr/bin/cmake --build "build"
gmake[1]: Entering directory '/home/jovyan/cs2310i26a/source/Lecture1/build'
[ 14%] Building CXX object CMakeFiles/mymath.dir/mymath/add.cpp.o
[ 28%] Building CXX object CMakeFiles/mymath.dir/mymath/mul.cpp.o
[ 42%] Linking CXX shared library /home/jovyan/cs2310i26a/source/Lecture1/lib/libmymath.so
[ 42%] Built target mymath
[ 57%] Building CXX object CMakeFiles/util.dir/util/io.cpp.o
[ 71%] Linking CXX static library libutil.a
[ 71%] Built target util
[ 85%] Building CXX object src/CMakeFiles/addmul.dir/addmul.cpp.o
[100%] Linking CXX executable /home/jovyan/cs2310i26a/source/Lecture1/bin/addmul
[100%] Built target addmul
gmake[1]: Leaving directory '/home/jovyan/cs2310i26a/source/Lecture1/build'
bin/:
total 1
drwxr-xr-x 2 jovyan users 3 Sep 3 20:49 .
drwxr-xr-x 13 jovyan users 24 Sep 3 20:49 ..
-rwxr-xr-x 1 jovyan users 681144 Sep 3 20:49 addmul
build/:
total 14
drwxr-xr-x 4 jovyan users 9 Sep 3 20:49 .
drwxr-xr-x 13 jovyan users 24 Sep 3 20:49 ..
-rw-r--r-- 1 jovyan users 14092 Sep 3 20:48 CMakeCache.txt
drwxr-xr-x 7 jovyan users 15 Sep 3 20:49 CMakeFiles
-rw-r--r-- 1 jovyan users 7624 Sep 3 20:48 Makefile
-rw-r--r-- 1 jovyan users 2319 Sep 3 20:48 cmake_install.cmake
-rw-r--r-- 1 jovyan users 2414 Sep 3 20:48 compile_commands.json
-rw-r--r-- 1 jovyan users 120908 Sep 3 20:49 libutil.a
drwxr-xr-x 3 jovyan users 5 Sep 3 20:48 src
lib/:
total 5
drwxr-xr-x 2 jovyan users 3 Sep 3 20:48 .
drwxr-xr-x 13 jovyan users 24 Sep 3 20:49 ..
-rwxr-xr-x 1 jovyan users 17320 Sep 3 20:48 libmymath.so
The CMake command option --build "build" instructs CMake to look for the build system files in the build subfolder and compile the source code to generate:
lib/libmymath.so, a dynamic/shared library providing theaddandmulfunctions;build/libutil.a, a static library providing theget_inputfunction; andbin/addmul, the main application.
%%bash
make run <<EOF
1
2
3
EOF"./bin/addmul"
Enter an integer: Enter an integer: Enter an integer:
add(1, 2) == 3
mul(1, 2, 3) = 6
Try opening a terminal to run the executable "${HOME}/cs2310_26a/Lecture1/bin/addmul" directly.
How to edit and debug the source code?¶
Although Jupyter does not have visual debugging capability for C++ code, we can use the Visual Student Code (VS Code) interface.
Before starting VS Code, run the following cell for a one-time installation of some useful VSCode extensions for debugging:
%%bash
if [ -z "${NBGRADER_EXECUTION}" ]; then
make setup
fiSetting up clangd for Code Server...
Installing extensions...
Extension 'llvm-vs-code-extensions.vscode-clangd' v0.6.0 is already installed. Use '--force' option to update to latest version or provide '@<version>' to install a specific version, for example: 'llvm-vs-code-extensions.vscode-clangd@1.2.3'.
Installing extensions...
(node:30402) [DEP0169] DeprecationWarning: `url.parse()` behavior is not standardized and prone to errors that have security implications. Use the WHATWG URL API instead. CVEs are not issued for `url.parse()` vulnerabilities.
(Use `node --trace-deprecation ...` to show where the warning was created)
Installing extension 'vadimcn.vscode-lldb'...
Extension 'vadimcn.vscode-lldb' v1.12.3 was successfully installed.
Done!
Run the cell below to generate the link to open Lecture1 in VS Code:
vscode('.', target='VSC1')Alternatively,
launch VS Code from JupyterLab with
File→New Launcherand thenopen
Lecture1folder in VS Code with☰→File→Open Folder....
Prior to debugging, you should have built the project and configured the debugger for the project with a file .vscode/launch.json at the project root folder:
!mkdir -p .vscode%%writefile .vscode/launch.json
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://code.visualstudio.com/docs/debugtest/debugging#_launch-configurations
"version": "0.2.0",
"configurations": [
{
"name": "Launch",
"type": "lldb",
"request": "launch",
"program": "${workspaceFolder}/bin/addmul",
"args": [],
"stopOnEntry": false
}
]
}Writing .vscode/launch.json
As commented in the first line, you can learn more about the configuration using IntelliSense in VS Code. We enabled IntelliSense for C++ earlier by the following steps:
Install a C++ language server extension: Install an extension such as clangd. This was done with
make setup.Export the compilation commands: Create the compile
_commands .json file in the build directory. This was done with make generate.
Solution to Exercise 4
This is done by
set(CMAKE_EXPORT_COMPILE_COMMANDS ON).This is done by
set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE)
To keep the intermediate files, uncomment the line # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -save-temps").
Follow the steps below to debug.
Open source files:
Navigate to the
Explorerview by clicking on the folder icon on the sidebar or pressing Ctrl/Cmd+Shift+E.Find and open the
addmul.cppfile.
Set a breakpoint:
Click on the line number next to
int main() {to set a breakpoint. A red dot will appear indicating the breakpoint.Navigate to the
Run and Debugview by clicking on the play-with-a-bug icon on the sidebar or pressing Ctrl/Cmd+Shift+DClick on the green play button (or press F5) to start debugging. This will launch the program and pause execution at the breakpoint.
Inspect variables:
Use the
Variablespane to view the current values of variables likea,b, andc.Observe how these values change as you step through the code.
Examine the call stack:
The
Call Stackpane shows the sequence of function calls leading to the current point in the program.This helps understand the flow of execution and how values are passed between functions.
Watch expressions:
Add expressions to the
Watchpane (usually on the left side during debugging) to monitor specific calculations or function calls. E.g., watch the expression\nat mul(a, b, c). See the manual for codelldb.
Step through the code:
Step Over: Press F10 to execute the next line of code without stepping into any function calls. E.g.,try step over until you reach the call to
mul(a, b, c);enter some non-zero integers in the terminal when prompted; and
observe the
VariablesandWatchpanes.
Step Into: Press F11 to step into the function call on the current line. This allows you to inspect the function’s internal operations. E.g.,try stepping into the call of
mul(a, b, c); andstepping out of it.
Continue: Press F5 to continue running the program until the next breakpoint or the end of the program.
To rebuild the project for release instead of debugging:
Access the Terminal by going to the menu and select
Terminal>New Terminalor press Ctrl+` (backtick).Rebuild the project by running
make -e CMAKE_BUILD_TYPE=release.Enter any integer when prompted by the application.
Try running the debugger again to see that it does not stop at any breakpoints. You can set the CMAKE_BUILD_TYPE=release explicitly at the beginning of the Makefile.
# Makefile for a C++ project
# =====================================================================
# For building a C++ project with CMake
# ---------------------------------------------------------------------
# Directory where the build artifacts will be stored
BUILD_DIR ?= build
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html
# Options: "Debug" "Release" "MinSizeRel" "RelWithDebInfo"
# Default is set to "Debug" in ./CMakelists.txt
CMAKE_BUILD_TYPE ?=
# Set the C++ compiler
# Default to $CXX
CMAKE_CXX_COMPILER ?= clang++
export CXX CMAKE_BUILD_TYPE
# Use bash for Make commands
SHELL:=/bin/bash
# Path to cmake executable
CMAKE_COMMAND ?= $(shell which cmake)
# Directory where `make install` places the artifacts.
# Defaults to the user-local prefix ~/.local (no sudo required).
# Override with, e.g., make install PREFIX=/home/me/.local
PREFIX ?= $(HOME)/.local
# Phony targets that are not actual files, but rather a command to be executed
.PHONY: all generate build run clean install uninstall clean-all vscode-clangd
# The main target to build the project
all: generate build run
# Generate compile commands for the project
# See:
# - CMake manual: https://cmake.org/cmake/help/latest/manual/cmake.1.html#generate-a-project-buildsystem
generate:
$(CMAKE_COMMAND) -B "$(BUILD_DIR)" -S . -DCMAKE_CXX_COMPILER=$(CMAKE_CXX_COMPILER) -DCMAKE_BUILD_TYPE=$(CMAKE_BUILD_TYPE) -DCMAKE_INSTALL_PREFIX=$(PREFIX)
# Build the project after generate
# See: https://cmake.org/cmake/help/latest/manual/cmake.1.html#build-a-project
build:
$(CMAKE_COMMAND) --build "$(BUILD_DIR)"
# Run the executable after build
run:
"./bin/addmul"
# Install the artifacts (executable, libraries, headers) under $(PREFIX)
# See: https://cmake.org/cmake/help/latest/manual/cmake.1.html#install-a-project
# The executable finds the shared library relative to itself ($ORIGIN/../lib),
# so no LD_LIBRARY_PATH is needed; only $(PREFIX)/bin may need a PATH entry.
# `generate` is re-run so that overriding PREFIX (e.g.
# `make install PREFIX=/home/me/.local`) always takes effect — CMake caches
# the prefix from the previous configure otherwise.
install: generate build
$(CMAKE_COMMAND) --install "$(BUILD_DIR)"
@echo "Installed to $(PREFIX)."
@echo "If $(PREFIX)/bin is not on your PATH, add the following to ~/.bashrc:"
@echo " export PATH=\"$(PREFIX)/bin:\$$PATH\""
# Remove the files previously installed by `make install`
# (the uninstall script is generated at configure time by CMakeLists.txt)
uninstall:
$(CMAKE_COMMAND) -P "$(BUILD_DIR)/cmake_uninstall.cmake"
# Clean up the artifacts
clean:
$(CMAKE_COMMAND) --build "$(BUILD_DIR)" --target clean
# Clean up the build system
clean-all:
rm -rf "$(BUILD_DIR)" bin/ lib/ .cache/
# =====================================================================
# For one-time setup
# ---------------------------------------------------------------------
# Install and configure clangd a debugger extension for Coder Server
# See:
# - CodeLLDB manual: https://github.com/vadimcn/codelldb/blob/master/MANUAL.md#expressions
# - VSCode clangd: https://github.com/clangd/vscode-clangd
# - VSCode debug: https://code.visualstudio.com/docs/debugtest/debugging
setup:
@echo "Setting up clangd for Code Server..." && \
code-server --install-extension llvm-vs-code-extensions.vscode-clangd && \
code-server --install-extension vadimcn.vscode-lldb && \
echo "Done!"Different Compilers¶
GNU Compiler Collection¶
There are C++ compilers other than clang++. For instance, a popular and mature C++ compiler for Linux is g++ from the GNU Compiler Collection (GCC):
%%bash
cd private && \
g++ --std=c++23 hello.cpp -o hello_gcc && \
./hello_gccHello, World!
The executables compiled from the same source code can be different for different compilers, as they use different compiler configurations and libraries:
%%bash
ls -la ./private/hello_gcc ./private/hello-rwxr-xr-x 1 jovyan users 322376 Aug 25 15:00 ./private/hello
-rwxr-xr-x 1 jovyan users 250920 Aug 25 15:53 ./private/hello_gcc
Actually Portable Executable¶
Both clang++ and g++ are installable on nearly any platforms, but the executable compiled in one platform cannot run in another:
%%hermes
Why an executable compiled by GCC in one platform may not run in another platform?↻ Session: main (ACP: 691fa645-f3b…)
Hermes
Because a compiled executable is a binary that “speaks” one platform’s language. When g++ compiles, it produces machine code and a binary format targeted at the specific OS + CPU the compiler is running on. Portability breaks at three layers:
1. CPU instruction set (ISA) The machine code in the executable is written for one architecture (x86-64, AArch64, RISC-V…). A CPU of a different architecture can’t even decode those instructions. Example: a binary built on your Mac (Apple Silicon, AArch64) won’t run on an Intel laptop.
2. Executable file format & OS kernel Linux expects ELF, macOS expects Mach-O, Windows expects PE/COFF. The OS kernel/loader only understands its own format — drop a Linux ELF binary on a macOS machine and it’s just an unreadable file. Even on the same ISA (Linux x86-64 vs Windows x86-64), this alone stops it.
3. System interface & ABI Even where the file format matches, the binary assumes a specific system contract:
System calls — the code calls the OS through the platform’s syscall interface (Linux
read/writevs macOS/BSD variants vs WindowsReadFile).C/C++ runtime library — it’s linked against the platform’s
glibc/libc++/MSVCRT; those libraries exist in different versions and interfaces per platform.ABI (calling convention, name mangling, struct layout, exception handling) — e.g. the System V AMD64 ABI (Linux/macOS) differs from the Microsoft x64 ABI (Windows), so even the way functions are called is platform-specific.
It’s not that GCC is “non-portable” — it’s that portability lives at the source level, not the binary level:
The same C++ source can be compiled by GCC on nearly any platform, but each run produces a binary for that platform. Hence “compile here, run there” doesn’t apply to native executables — you must recompile on (or for) the target platform (cross-compilation lets one machine build for another).
That’s what the “Actually Portable Executable” section contrasts: truly portable binaries are the ones run by a uniform interpreter/runtime — e.g. Java bytecode (the JVM) or WebAssembly — where one artifact runs on any platform that provides the runtime, at the cost of an interpretation/translation layer.
So the answer to “why”: the executable embeds decisions made at compile time (instruction set, file format, ABI, linked libraries) that are specific to the platform GCC was built and run for — a different platform’s CPU or OS simply doesn’t honor the same contract.
However, an executable compiled using the Cosmopolitan Libc framework is an exception. Since Cosmo does not support c++23 standard yet, we will demonstrate using the following source code without using <format>:
%%writefile 'private/hello_cosmo.cpp'
#include <iostream>
int main() {
std::cout << "Hello, World!\n";
return 0;
}Writing private/hello_cosmo.cpp
The cosmoc++ compiler can create a single binary, called Actually Portable Executable (APE), that can run on multiple platforms, including Linux, Windows, and macOS.
%%bash
cd private && \
cosmoc++ hello_cosmo.cpp -o hello_cosmo && \
./hello_cosmoHello, World!
As seeing is believing, try running the executable on a platform other than the current platform by following the official documentation.
Emscripten¶
It is even possible to compile C++ code to a web page that can run in-browser.[6]
%%bash
mkdir -p ~/www/hello && \
em++ --std=c++23 ./private/hello.cpp -o ~/www/hello/index.html && \
ls -la ~/www/hello/*shared:INFO: (Emscripten: Running sanity checks)
-rw-r--r-- 1 jovyan users 21960 Aug 25 15:56 /home/jovyan/www/hello/index.html
-rw-r--r-- 1 jovyan users 177925 Aug 25 15:56 /home/jovyan/www/hello/index.js
-rwxr-xr-x 1 jovyan users 458280 Aug 25 15:56 /home/jovyan/www/hello/index.wasm
After running the above shell, the web page should be available at:[7]
The C++ code is compiled to WebAssembly
index.wasm, along withthe HTML
index.htmland JavaScriptindex.jsto serve the WebAssembly.
The JavaScript script file index.js can also be executed using Node.js:[8]
%%bash
node ~/www/hello/index.jsHello, World!
Supplementary Notes¶
file permission string¶
In operating systems, the file permission string represents the read, write, and execute permissions of a file for the owner, group, and others.
%%hermes
Explain the permission string -rwxr-xr-x.shell command¶
The %%bash magic runs the cell content using a (bash) shell, which is a command-line interpreter for accessing the operating system’s services.
%%hermes
What is bash and why is it called a "shell"?%%hermes
What is the standard way to specify command-line options?exit status code¶
%%hermes
Briefly list the exit codes for C++ programs.↻ Session: main (ACP: 691fa645-f3b…)
Hermes
Briefly, the exit codes a C++ program can produce:
The basics
0— success. Returned byreturn 0;, or implicitly ifmainreaches its end without areturnstatement.Non-zero (any other value) — indicates failure. The specific number is program-defined; by convention
1is the generic “something went wrong”.
Worth knowing
Only the low 8 bits are visible — the shell (and
echo ${?}) only ever sees the value modulo 256, so effectively exit codes are0–255.<cstdlib>constants:std::exit(0)/std::exit(1)mirror success/failure (POSIX systems also defineEX_OK= 0 andEX_FAIL= 1).sysexits.h(POSIX) suggests specific non-zero codes for specific failure kinds:EX_USAGE(64, bad command-line usage),EX_DATAERR(65),EX_NOINPUT(66),EX_IOERR(74),EX_SOFTWARE(70), etc.Signal terminations show up as
128 + signal number(when reported by a shell): e.g. 139 = 128 + 11 (SIGSEGV, segfault), 134 = 128 + 6 (SIGABRT, fromstd::abort()/failed assertions). Strictly, a program killed by a signal has no C++ exit code of its own — the runtime reports it.
Shell conventions (not set by the C++ program itself)
126— command found but not executable127— command not found
So in practice for the notebook’s purposes: main returns 0 for success, anything else (customarily 1) for failure, and the shell exposes it via ${?} — exactly what the cell earlier in the notebook demonstrates by running ./private/hello and then echo ${?}.
%%hermes
Is running `./private/hello` the same as running `cd private && ./hello` in bash?↻ Session: main (ACP: 691fa645-f3b…)
Hermes
They launch the same binary, but they are not strictly the same — the key difference is the program’s working directory.
%%hermes
Is running `./hello` the same as running `hello` in bash?↻ Session: main (ACP: 691fa645-f3b…)
Hermes
No. The difference is how the shell resolves the command name, and it changes which program actually runs — or whether one runs at all.
compiler configurations¶
The following commands use the flags
-vto output the details of the compiler’s configuration, and-fsyntax-onlyto instruct the compiler to only check the syntax of the source code without generating any object code or executable.
%%bash
g++ --std=c++23 -v -fsyntax-only ./private/hello.cppUsing built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-linux-gnu/15/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 15.2.0-16ubuntu1' --with-bugurl=file:///usr/share/doc/gcc-15/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2,rust,cobol,algol68 --prefix=/usr --with-gcc-major-version-only --program-suffix=-15 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/libexec --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-libstdcxx-backtrace --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-15-j35TAX/gcc-15-15.2.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-15-j35TAX/gcc-15-15.2.0/debian/tmp-gcn/usr --enable-offload-defaulted --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu --with-build-config=bootstrap-lto-lean --enable-link-serialization=2
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 15.2.0 (Ubuntu 15.2.0-16ubuntu1)
COLLECT_GCC_OPTIONS='-std=c++23' '-v' '-fsyntax-only' '-shared-libgcc' '-mtune=generic' '-march=x86-64' '-dumpdir' 'a-'
/usr/libexec/gcc/x86_64-linux-gnu/15/cc1plus -quiet -v -imultiarch x86_64-linux-gnu -D_GNU_SOURCE ./private/hello.cpp -quiet -dumpdir a- -dumpbase hello.cpp -dumpbase-ext .cpp -mtune=generic -march=x86-64 -std=c++23 -version -fsyntax-only -o /dev/null -fasynchronous-unwind-tables -fstack-protector-strong -Wformat -Wformat-security -fstack-clash-protection -fcf-protection -fzero-init-padding-bits=all -Wbidi-chars=any
GNU C++23 (Ubuntu 15.2.0-16ubuntu1) version 15.2.0 (x86_64-linux-gnu)
compiled by GNU C version 15.2.0, GMP version 6.3.0, MPFR version 4.2.2, MPC version 1.3.1, isl version isl-0.27-GMP
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
ignoring duplicate directory "/usr/include/x86_64-linux-gnu/c++/15"
ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/15/include-fixed/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/15/include-fixed"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/15/../../../../x86_64-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
/usr/include/c++/15
/usr/include/x86_64-linux-gnu/c++/15
/usr/include/c++/15/backward
/usr/lib/gcc/x86_64-linux-gnu/15/include
/usr/local/include
/usr/include/x86_64-linux-gnu
/usr/include
End of search list.
Compiler executable checksum: b661a74662ad7cf413c4193391ab5290
COMPILER_PATH=/usr/libexec/gcc/x86_64-linux-gnu/15/:/usr/libexec/gcc/x86_64-linux-gnu/15/:/usr/libexec/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/15/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/15/:/usr/lib/gcc/x86_64-linux-gnu/15/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/15/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/15/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-std=c++23' '-v' '-fsyntax-only' '-shared-libgcc' '-mtune=generic' '-march=x86-64' '-dumpdir' 'a.'
%%bash
clang++ --std=c++23 -v -fsyntax-only --stdlib=libc++ ./private/hello.cppUbuntu clang version 22.1.8 (++20260714015917+ca7933e47d3a-1~exp1~20260714135927.17)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/lib/llvm-22/bin
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/14
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/15
Selected GCC installation: /usr/lib/gcc/x86_64-linux-gnu/15
Candidate multilib: .;@m64
Selected multilib: .;@m64
(in-process)
"/usr/lib/llvm-22/bin/clang" -cc1 -triple x86_64-pc-linux-gnu -fsyntax-only -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name hello.cpp -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=all -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/home/jovyan/cs2310i26a/source/Lecture1 -v -fcoverage-compilation-dir=/home/jovyan/cs2310i26a/source/Lecture1 -resource-dir /usr/lib/llvm-22/lib/clang/22 -internal-isystem /usr/lib/llvm-22/bin/../include/c++/v1 -internal-isystem /usr/lib/llvm-22/lib/clang/22/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/15/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include --std=c++23 -fdeprecated-macro -ferror-limit 19 -fgnuc-version=4.2.1 -fno-implicit-modules -fskip-odr-check-in-gmf -fcxx-exceptions -fexceptions -faddrsig -fdwarf2-cfi-asm -x c++ ./private/hello.cpp
clang -cc1 version 22.1.8 based upon LLVM 22.1.8 default target x86_64-pc-linux-gnu
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/15/../../../../x86_64-linux-gnu/include"
ignoring nonexistent directory "/include"
#include "..." search starts here:
#include <...> search starts here:
/usr/lib/llvm-22/bin/../include/c++/v1
/usr/lib/llvm-22/lib/clang/22/include
/usr/local/include
/usr/include/x86_64-linux-gnu
/usr/include
End of search list.
%%bash
em++ --std=c++23 -v -fsyntax-only --stdlib=libc++ ./private/hello.cpp "/opt/emsdk/upstream/bin/clang++" -target wasm32-unknown-emscripten -fignore-exceptions -mllvm -combiner-global-alias-analysis=false -mllvm -enable-emscripten-sjlj -mllvm -disable-lsr --sysroot=/opt/emsdk/upstream/emscripten/cache/sysroot -Xclang -iwithsysroot/include/fakesdl -Xclang -iwithsysroot/include/compat --std=c++23 -v -fsyntax-only --stdlib=libc++ ./private/hello.cpp
clang version 24.0.0git (https:/github.com/llvm/llvm-project 4bfd08c2d769736841ae4f5705d76fa6daa39027)
Target: wasm32-unknown-emscripten
Thread model: posix
InstalledDir: /opt/emsdk/upstream/bin
(in-process)
"/opt/emsdk/upstream/bin/clang-24" -cc1 -triple wasm32-unknown-emscripten -fsyntax-only -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name hello.cpp -mrelocation-model static -mframe-pointer=none -ffp-contract=on -fno-rounding-math -mconstructor-aliases -target-cpu generic -fvisibility=hidden -debugger-tuning=gdb -fdebug-compilation-dir=/home/jovyan/cs2310i26a/source/Lecture1 -v -fcoverage-compilation-dir=/home/jovyan/cs2310i26a/source/Lecture1 -resource-dir /opt/emsdk/upstream/lib/clang/24 -isysroot /opt/emsdk/upstream/emscripten/cache/sysroot -internal-isystem /opt/emsdk/upstream/emscripten/cache/sysroot/include/wasm32-emscripten/noeh/c++/v1 -internal-isystem /opt/emsdk/upstream/emscripten/cache/sysroot/include/wasm32-emscripten/c++/v1 -internal-isystem /opt/emsdk/upstream/emscripten/cache/sysroot/include/c++/v1 -internal-isystem /opt/emsdk/upstream/lib/clang/24/include -internal-isystem /opt/emsdk/upstream/emscripten/cache/sysroot/include/wasm32-emscripten -internal-isystem /opt/emsdk/upstream/emscripten/cache/sysroot/include --std=c++23 -fdeprecated-macro -ferror-limit 19 -fgnuc-version=4.2.1 -fno-implicit-modules -fskip-odr-check-in-gmf -fcxx-exceptions -fignore-exceptions -fexceptions -iwithsysroot/include/fakesdl -iwithsysroot/include/compat -mllvm -combiner-global-alias-analysis=false -mllvm -enable-emscripten-sjlj -mllvm -disable-lsr -x c++ ./private/hello.cpp
clang -cc1 version 24.0.0git based upon LLVM 24.0.0git default target x86_64-unknown-linux-gnu
ignoring nonexistent directory "/opt/emsdk/upstream/emscripten/cache/sysroot/include/wasm32-emscripten/noeh/c++/v1"
ignoring nonexistent directory "/opt/emsdk/upstream/emscripten/cache/sysroot/include/wasm32-emscripten/c++/v1"
ignoring nonexistent directory "/opt/emsdk/upstream/emscripten/cache/sysroot/include/wasm32-emscripten"
#include "..." search starts here:
#include <...> search starts here:
/opt/emsdk/upstream/emscripten/cache/sysroot/include/fakesdl
/opt/emsdk/upstream/emscripten/cache/sysroot/include/compat
/opt/emsdk/upstream/emscripten/cache/sysroot/include/c++/v1
/opt/emsdk/upstream/lib/clang/24/include
/opt/emsdk/upstream/emscripten/cache/sysroot/include
End of search list.
header files¶
A header file in C++ is a file with a .h or .hpp extension that contains declarations and definitions that can be shared across multiple source files.
%%hermes
Why does C++ use header files? Are header files common in other programming languages?↻ Session: main (ACP: 691fa645-f3b…)
Hermes
binary file¶
Hex editors such as radare2 can help us read and edit machine code.
%%hermes
Give a brief but concrete example how one can reverse engineer a software using a hex editor.↻ Session: main (ACP: 691fa645-f3b…)
Hermes
dynamic library¶
A dynamic library in C++ is a collection of functions and data that can be loaded and linked at runtime, allowing multiple programs to share the same code. The lib folder is commonly used to store these dynamic library files, such as .so files on Unix-like systems or .dll files on Windows.
For instance, the standard library for clang++ is:
!ldconfig -p | grep libc++.so libc++.so.1 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libc++.so.1
The standard library for g++ is:
!ldconfig -p | grep libstdc++ libstdc++.so.6 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libstdc++.so.6
libstdc++.so.6 (libc6) => /usr/lib32/libstdc++.so.6
static library¶
A static library in C++ is a collection of object files combined into a single archive file (typically with a .a extension), which is included directly into the executable at compile time.
%%hermes
When is it preferrable to use a static library instead of a dynamic library, or
the object files directly.↻ Session: main (ACP: 691fa645-f3b…)
Hermes
Quick recap of the three forms, then the decision rules:
Object files (
hello.o) — what the assembler emits; linked directly.Static library (
libfoo.a) — an archive of object files with an index; the linker pulls out only the members it needs.Dynamic library (
libfoo.so) — a shared object loaded at runtime by the dynamic linker (thelibc++.so/libstdc++.soyou listed withldconfig -pabove).
See the Jupyter blog for more details.
This is a sample response generated by the above prompt. Click me to expand the content.
See Compilation for more details.
%%writefileis an IPython cell magic for creating a file with the content of the magic cell.Other common extensions are:
.cc,.cxx,.Cand also.cufor CUDA.Running in-browser means an application can be served statically via a web server and executed directly within a web browser without requiring a sustained internet connection once loaded. E.g., try the WebLLM Chat or its android app without internet after loading a model.
The static page is served via a basic proxy
jupyter-www-proxy.Node.js is a JavaScript runtime built on Chrome’s V8 JavaScript engine, allowing developers to run JavaScript outside of the browser; for more information, see the official Node.js documentation.