In Golang, we can use go run main.go to compile and execute the source file in one step. In C++, by default, we need to compile first and then execute.
We can create a shell script to make C++ work like Golang, enabling rapid prototyping and quick testing without manual compilation steps. This guide covers basic scripts, advanced features, watch mode, and modern C++ build tools available in 2026.
Creating the cpprun Script
Step 1: Create a script in a bin directory, named cpprun. For example, /usr/local/bin or ~/bin.
mkdir -p ~/bin
nano ~/bin/cpprun
Step 2: Add the following contents to cpprun.
#!/bin/bash
g++ -std=c++17 -O2 -Wall -Wextra -o /tmp/a.out "$@" && /tmp/a.out
Step 3: Make the script executable.
chmod +x ~/bin/cpprun
Ensure the script is in your PATH:
export PATH="$HOME/bin:$PATH"
# Add to ~/.bashrc for persistence
echo 'export PATH="$HOME/bin:$PATH"' >> ~/.bashrc
Step 4: Run the C++ file.
cpprun main.cpp
Enhanced Script with Runtime Arguments
Here’s an improved version that separates compiler arguments from runtime arguments:
#!/bin/bash
# Configuration
CXX="g++"
STD="c++17"
FLAGS="-O2 -Wall -Wextra -Werror"
TMPDIR="/tmp"
BINARY="$TMPDIR/a.out"
# Separate source files from arguments
SOURCES=()
RUNTIME_ARGS=()
ๅบๅ_source=1
for arg in "$@"; do
if [ "$arg" == "--" ]; then
ๅบๅ_source=0
elif [ $ๅบๅ_source -eq 1 ]; then
SOURCES+=("$arg")
else
RUNTIME_ARGS+=("$arg")
fi
done
# Compile
$CXX $STD $FLAGS -o "$BINARY" "${SOURCES[@]}"
if [ $? -eq 0 ]; then
# Run with arguments after --
"$BINARY" "${RUNTIME_ARGS[@]}"
fi
Usage:
cpprun main.cpp -- arg1 arg2
Advanced Script with Multiple Files and Dependencies
For projects with multiple source files and dependencies:
#!/bin/bash
# cpprun-advanced - Advanced C++ runner with multi-file support
set -e
# Configuration
CXX="${CXX:-g++}"
STD="${STD:-c++17}"
OPT="${OPT:-O2}"
WARNINGS="-Wall -Wextra -Wpedantic"
TMPDIR="${TMPDIR:-/tmp}"
BINARY="$TMPDIR/a.out"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Usage function
usage() {
echo "Usage: cpprun-advanced [options] <source files> [-- runtime args]"
echo "Options:"
echo " -c, --compile-only Only compile, don't run"
echo " -o, --output FILE Output binary name"
echo " -d, --debug Compile with debug symbols (-g)"
echo " -v, --verbose Verbose output"
echo " -h, --help Show this help"
exit 1
}
# Parse arguments
COMPILE_ONLY=false
OUTPUT_FILE=""
DEBUG=false
VERBOSE=false
SOURCES=()
RUNTIME_ARGS=()
PASS_TO_COMPILER=()
args=("$@")
i=0
while i < ${#args[@]}; do
case "${args[$i]}" in
-c|--compile-only)
COMPILE_ONLY=true
;;
-o|--output)
i=$((i+1))
OUTPUT_FILE="${args[$i]}"
;;
-d|--debug)
DEBUG=true
;;
-v|--verbose)
VERBOSE=true
;;
-h|--help)
usage
;;
--)
# Remaining args are runtime args
i=$((i+1))
while i < ${#args[@]}; do
RUNTIME_ARGS+=("${args[$i]}")
i=$((i+1))
done
break
;;
-*)
PASS_TO_COMPILER+=("${args[$i]}")
;;
*)
SOURCES+=("${args[$i]}")
;;
esac
i=$((i+1))
done
# Validate inputs
if [ ${#SOURCES[@]} -eq 0 ]; then
echo -e "${RED}Error: No source files provided${NC}"
usage
fi
# Build compiler command
COMPILE_CMD=($CXX $STD)
# Add debug flags if requested
if [ "$DEBUG" = true ]; then
COMPILE_CMD+=("-g")
OPT="Og" # Better debug experience
fi
# Add optimization and warnings
COMPILE_CMD+=($OPT $WARNINGS)
# Add any extra compiler flags
[ ${#PASS_TO_COMPILER[@]} -gt 0 ] && COMPILE_CMD+=("${PASS_TO_COMPILER[@]}")
# Determine output file
if [ -n "$OUTPUT_FILE" ]; then
COMPILE_CMD+=("-o" "$OUTPUT_FILE")
else
COMPILE_CMD+=("-o" "$BINARY")
fi
# Add source files
COMPILE_CMD+=("${SOURCES[@]}")
# Show compilation command if verbose
if [ "$VERBOSE" = true ]; then
echo -e "${YELLOW}Compiling:${NC} ${COMPILE_CMD[@]}"
fi
# Compile
if "${COMPILE_CMD[@]}"; then
echo -e "${GREEN}Compilation successful!${NC}"
# Run if not compile-only
if [ "$COMPILE_ONLY" = false ]; then
if [ -n "$OUTPUT_FILE" ]; then
"./$OUTPUT_FILE" "${RUNTIME_ARGS[@]}"
else
"$BINARY" "${RUNTIME_ARGS[@]}"
fi
fi
else
echo -e "${RED}Compilation failed!${NC}"
exit 1
fi
Script Features Explained
- The script uses
g++to compile the provided C++ file(s) with C++17 standard to a temporary executable/tmp/a.out. - If compilation succeeds (
&&), it runs the executable. - Using
/tmp/a.outavoids cluttering the current directory with temporary files. - The
"$@"allows passing multiple files or additional arguments tog++. -std=c++17enables modern C++ features-Wall -Wextraenables comprehensive warnings-O2enables optimization
Important Notes
- Ensure
g++is installed:sudo apt install g++(on Ubuntu/Debian) orsudo yum install gcc-c++(on CentOS). - The script assumes the file has a
mainfunction. - For projects with multiple files or dependencies, consider using Makefiles or build systems like CMake.
- If you need to pass runtime arguments to the program, use the enhanced version above.
- Security: Running executables from
/tmpis fine for personal use, but be cautious with untrusted code.
Extended Script: Watch Mode
For development, add automatic recompilation:
#!/bin/bash
FILE="$1"
BINARY="/tmp/a.out"
# One-time compilation and run
compile_and_run() {
g++ -std=c++17 -O2 -Wall -Wextra -o "$BINARY" "$FILE"
if [ $? -eq 0 ]; then
"$BINARY" "${@:2}"
fi
}
# Initial run
compile_and_run "$@"
# Watch mode
echo "Watching for changes... Press Ctrl+C to exit."
while true; do
sleep 1
if [ "$FILE" -nt "$BINARY" ]; then
echo "Recompiling..."
compile_and_run "$@"
fi
done
Modern C++ Build Tools (2026)
While shell scripts are great for quick prototyping, modern C++ development benefits from proper build tools:
1. Bazel
Google’s build system:
# Install
brew install bazel # macOS
# or
sudo apt install bazel # Ubuntu
# WORKSPACE file
workspace(name = "my_project")
# BUILD file
cc_binary(
name = "hello",
srcs = ["hello.cc"],
)
# Build and run
bazel run //:hello
2. CMake with CTest
# CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(MyApp)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_executable(myapp main.cpp)
enable_testing()
add_test(NAME myapp_test COMMAND myapp)
# Build
mkdir build && cd build
cmake ..
make
ctest
3. Modern CMake with FetchContent
// CMakeLists.txt with dependencies
cmake_minimum_required(VERSION 3.24)
project(MyApp LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
include(FetchContent)
FetchContent_Declare(
json
GIT_REPOSITORY https://github.com/nlohmann/json.git
GIT_TAG v3.11.0
)
FetchContent_MakeAvailable(json)
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE nlohmann_json::nlohmann_json)
4. C++ Runner Tools
For quick execution without scripts:
# cpp-run (Rust-based)
cargo install cpp-run
cpp-run main.cpp
# clang -run (Clang with -run flag)
clang++ -std=c++20 -stdlib=libc++ -run main.cpp
# Online compilers (for quick testing)
# - Compiler Explorer (godbolt.org)
# - Replit
# - JDoodle
5. Visual Studio Code Tasks
// .vscode/tasks.json
{
"version": "2.0.0",
"tasks": [
{
"label": "Build and Run C++",
"type": "shell",
"command": "g++",
"args": [
"${file}",
"-o",
"${fileDirname}/${fileBasenameNoExtension}",
"-std=c++20",
"-Wall",
"-Wextra"
],
"options": {
"cwd": "${fileDirname}"
},
"problemMatcher": ["$gcc"],
"group": {
"kind": "build",
"isDefault": true
}
}
]
}
Example Usage
Create main.cpp:
#include <iostream>
#include <vector>
#include <algorithm>
#include <ranges> // C++20 ranges
#include <numeric> // std::accumulate
int main(int argc, char* argv[]) {
std::vector<int> nums = {5, 2, 8, 1, 9};
std::cout << "Original: ";
for (auto n : nums) std::cout << n << " ";
std::cout << std::endl;
// Using C++20 ranges (if available)
auto sorted = nums | std::views::transform([](int x) { return x * 2; });
std::sort(nums.begin(), nums.end());
std::cout << "Sorted: ";
for (auto n : nums) std::cout << n << " ";
std::cout << std::endl;
// Sum using accumulate
int sum = std::accumulate(nums.begin(), nums.end(), 0);
std::cout << "Sum: " << sum << std::endl;
std::cout << "Arguments: ";
for (int i = 1; i < argc; i++) {
std::cout << argv[i] << " ";
}
std::cout << std::endl;
return 0;
}
Run:
cpprun main.cpp hello world
Output:
Original: 5 2 8 1 9
Sorted: 1 2 5 8 9
Sum: 25
Arguments: hello world
C++ Standards Progression
| Standard | Year | Key Features |
|---|---|---|
| C++11 | 2011 | Smart pointers, lambda, move semantics |
| C++14 | 2014 | Generic lambdas, constexpr |
| C++17 | 2017 | std::optional, std::variant, filesystem |
| C++20 | 2020 | Concepts, ranges, coroutines, modules |
| C++23 | 2023 | std::print, ranges improvements, deducing this |
| C++26 | 2026 | Expected, constexpr everything |
Best Practices
- Use Modern Standards: C++17 or C++20 for new projects
- Enable Warnings: Always compile with
-Wall -Wextra -Werror - Use Build Systems: CMake, Bazel, or Meson for larger projects
- Enable Sanitizers: Catch bugs early with
-fsanitize=address,undefined - Use Linters: clang-tidy for static analysis
# Compile with sanitizers
g++ -std=c++20 -fsanitize=address,undefined -g main.cpp -o main
Comments