Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions .clang-tidy
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Apply to ALL headers (empty = no restriction)
HeaderFilterRegex: '.*'

# Treat warnings as errors (optional but recommended for CI)
# WarningsAsErrors: '*'
WarningsAsErrors: ''

Checks: >
-*,
clang-diagnostic-*,
modernize-*,
-modernize-use-trailing-return-type,
-modernize-use-auto,
cppcoreguidelines-*,
-cppcoreguidelines-owning-memory,
-cppcoreguidelines-pro-type-vararg,
-cppcoreguidelines-avoid-magic-numbers,
bugprone-*,
performance-*,
readability-*,
-readability-magic-numbers,
-readability-identifier-length,
misc-*,
-misc-unused-parameters

CheckOptions:
- key: readability-identifier-naming.NamespaceCase
value: lower_case

- key: readability-identifier-naming.ClassCase
value: CamelCase

- key: readability-identifier-naming.StructCase
value: CamelCase

- key: readability-identifier-naming.FunctionCase
value: lower_case

- key: readability-identifier-naming.VariableCase
value: lower_case

- key: readability-identifier-naming.MemberCase
value: lower_case

- key: modernize-use-nullptr.NullMacros
value: 'NULL'
21 changes: 20 additions & 1 deletion .github/workflows/cpp-build-test-coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ jobs:
--inline-suppr \
--error-exitcode=1 \
./src ./include

# -------------------------------------------------------
# Step 3: Configure and build the project
# -------------------------------------------------------
Expand Down Expand Up @@ -97,3 +97,22 @@ jobs:
cd build
echo "## Test Coverage Summary" >> $GITHUB_STEP_SUMMARY
lcov --summary coverageFiltered.info >> $GITHUB_STEP_SUMMARY

# -------------------------------------------------------
# Step 7: Run Clang-Tidy (experimenting , not exit)
# - Modern C++ static analysis
# - Enforces best practices
# - Add `--warnings-as-errors=*` to exit code 1
# -------------------------------------------------------
- name: Run clang-tidy
# run: |
# echo "Running clang-tidy..."
# clang-tidy \
# -checks='clang-analyzer-*,modernize-*,performance-*,readability-*' \
# -p build \
# $(find ./src -name '*.cpp')
run: |
echo "Running clang-tidy using .clang-tidy options"
clang-tidy \
-p build \
-header-filter='^src/.*' $(find src -name "*.cpp")
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ set(PROJECT_NAME_TEST ${PROJECT_NAME}_unit_test) # name for the unit-test execut
# Require at least C++17 for GoogleTest and modern C++ features
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Enable output of compile commands for clang-tidy

# Optionally enforce warnings (good for learning/debugging)
add_compile_options(-Wall -Wextra -Wpedantic)
Expand Down Expand Up @@ -123,6 +124,8 @@ set(APP_SOURCES
"src/patterns/creational/Builder.cpp"
"src/patterns/creational/Prototype.cpp"
"src/core/datatypes/class/Relationship.cpp"
"src/core/datatypes/class/VirtualBase.cpp"
"src/core/datatypes/class/Binding.cpp"
## Exceptions
"src/core/exception/BasicHandle.cpp"
"src/core/exception/ThrowNoexcept.cpp"
Expand All @@ -131,6 +134,8 @@ set(APP_SOURCES
"src/core/filehandle/StringStream.cpp"
"src/core/filehandle/FileIO.cpp"
"src/core/filehandle/Directory.cpp"
"src/core/filehandle/OutputFormatting.cpp"
"src/core/filehandle/BinaryFileHandling.cpp"
## Container
"src/core/datatypes/container/sequence/Array.cpp"
"src/core/datatypes/container/sequence/Vector.cpp"
Expand Down
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ RUN \
apt-get install -y gcc g++ && \
# install cppcheck
apt-get install -y cppcheck && \
# install clang tidy
apt-get install -y clang-tidy && \
# install lcov
apt-get install -y lcov

Expand Down
134 changes: 79 additions & 55 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
## 1. OVERVIEW
## 1. Overview
**Project Structure**
```
includes/ → Header files (.h, .hpp)
src/ → Source files (.cpp)
tests/ → GoogleTest test cases
```
## 2. DEPENDENCIES
## 2. Dependencies
Make sure the following tools are installed before building the project:
- **g++ / gcc**
- **CMake**
Expand All @@ -14,68 +14,92 @@ Make sure the following tools are installed before building the project:
- **cppcheck** (for static analysis)
- **Optional:** Install `clang-format` to format C++ code.

Linux
**Linux**
```bash
sudo apt install clang-format
find . -regex '.*\.\(cpp\|h\|hpp\)' -exec clang-format -i {} \;
```
Windows
**Windows**
```bash
# Install clang-format via Chocolatey
choco install clang-format

# Apply clang-format recursively to .cpp, .h, .hpp files
Get-ChildItem -Recurse -Include *.cpp, *.h, *.hpp | ForEach-Object { clang-format -i $_.FullName }
```
## 3. SETUP
* Setup the Local Test Environment
* 1.Using your own Ubuntu system
* Install `gcc`, `cmake`, `git`, and `pthread` (Skip this step if you already install)
```
$ sudo apt-get update
$ sudo apt-get install g++
$ sudo apt-get install lcov
$ sudo apt-get install cmake
$ sudo apt-get install git
$ sudo apt-get install cppcheck
```
* Build the application and the tests
```
$ cd build
$ cmake ..
$ cmake --build .
```
* Run the application and the test
```
$ ./cpp_lab_project
$ ./cpp_lab_project_test
```
* (Optional) Run static analysis
```
$ sudo apt-get install cppcheck
$ cppcheck "folder" / "file"
```
* 2.Using **Docker**
* Build the Docker image
```
docker build --tag sample-ci-cpp .
```
* Run an interactive container
```
docker run -it sample-ci-cpp:latest /bin/bash
```
* Inspect the environment
```
printenv
```
* *Notes:*
* Use the -t or --tag flag to set the name of the image to be created. (the full name is actually sample-ci-cpp:latest, since latest is the default tag)
* Opening an interactive shell inside your Docker container to explore, test, or debug the environment built from your image.
* docker run to start a new container.
* -it → run it interactively:
* -i = keep STDIN open (so you can type commands)
* -t = allocate a terminal (TTY)
* sample-ci-cpp:latest → the image you built earlier.
* /bin/bash → the command to execute inside the container (opens a Bash shell).
## 3. Setup
### 3.1. Setup the Local Test Environment
- **Ubuntu system**
* Install `gcc`, `cmake`, `git`, and `pthread` (Skip this step if you already install)
```
$ sudo apt-get update
$ sudo apt-get install g++
$ sudo apt-get install lcov
$ sudo apt-get install cmake
$ sudo apt-get install git
$ sudo apt-get install cppcheck
```
* Build the application and the tests
```
$ cd build
$ cmake ..
$ cmake --build .
```
* Run the application and the test
```
$ ./cpp_lab_project
$ ./cpp_lab_project_test
```
* (Optional) Run static analysis - cppcheck
```
$ sudo apt-get install cppcheck
$ cppcheck "folder" / "file"
```
* (Optional) Run static analysis - clang-tidy
```
$ sudo apt-get install -y clang-tidy
$ clang-tidy -p build -header-filter='^src/.*' $(find src -name "*.cpp")
```
- **Docker**
* Update `Dockerfile`
* Build the Docker image
```
$ cd build
$ docker build --tag cpp-lab .
```
* Run an interactive container
```
$ docker run -it cpp-lab:latest /bin/bash
```
* Inspect the environment
```
$ printenv
```
* *Notes:*
* Use the `-t` or `--tag` flag to set the name of the image to be created. (the full name is actually `cpp-lab:latest`, since latest is the default tag)
* Opening an interactive shell inside your Docker container to explore, test, or debug the environment built from your image.
* docker run to start a new container.
* `-it`: run it interactively:
* `-i`: keep STDIN open (so you can type commands)
* `-t`: allocate a terminal (TTY)
* `cpp-lab:latest`: the image you built earlier.
* `/bin/bash`: the command to execute inside the container (opens a Bash shell).

## 4. DOCUMENTATIONS
## 5. Update Docker Image
```bash
# Navigate to the project that contain your Dockerfile
cd cpp-lab

# Build the project by running the following command, swapping out DOCKER_USERNAME with your username.
docker build -t DOCKER_USERNAME/cpp-lab .

# Verify the image exists locally
docker image ls

# To push the image
docker push DOCKER_USERNAME/cpp-lab
```

## 6. TROUBLESHOOTING
1. `push access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed`
=> docker login / Docker Desktop login
47 changes: 32 additions & 15 deletions src/core/datatypes/TypeConVersions.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// cppcheck-suppress-file [unreadVariable]
#include <iostream>
using namespace std;

Expand All @@ -12,6 +13,11 @@ class Derived : public Base {
void show() override { cout << "Derived class\n"; }
};

class DerivedX : public Base {
public:
void show() override { cout << "DerivedX class\n"; }
};

void implicitConversion() {
cout << "\n--- Implicit Type Conversion ---\n";
// *1. Numeric promotion (safe, no data loss)
Expand Down Expand Up @@ -39,48 +45,59 @@ void explicitConversion() {

double pi = 3.14159;

// *1. C-style cast
// *1. C-style cast - not safe
int pi_c = (int)pi;
cout << "C-style cast: " << pi_c << "\n";

// *2. static_cast - compile-time type checking
// **2. static_cast - type-safe relationship + compile-time type checking**
int pi_static = static_cast<int>(pi);
cout << "static_cast: " << pi_static << "\n";

// object -> object
// object -> object
Derived derived{};
[[maybe_unused]] Base baseObj = static_cast<Derived>(derived);
// object -> reference
Base baseObj = static_cast<Derived>(derived);
// DerivedX derivedx =static_cast<Derived>(derived); // ERROR

// object -> reference
const Base& baseRef = static_cast<Derived&>(derived);
// object -> ptr
[[maybe_unused]] const Base* base_ptr = static_cast<Derived*>(&derived);
const Base* base_ptr = static_cast<Derived*>(&derived);

// *3. const_cast: const_cast adds or removes the const qualifier
// **3. const_cast: const_cast adds or removes the const qualifier**
const double c_pi = 2.71828;
const double* pConst = &c_pi;
const double* pNonConst = const_cast<double*>(pConst); // remove const
cout << "const_cast: " << *pNonConst << " (removed const)\n";

// *4. reinterpret_cast: reinterpret memory (unsafe)
// **4. reinterpret_cast: reinterpret memory (unsafe)**
// It is used to convert a pointer of some data type into a pointer of another data type,
// even if the data types before and after conversion are different. (#static,dynamic)
// It does not check if the pointer type and data pointed by the pointer is same or not.
const void* pVoid = reinterpret_cast<const void*>(&pi);
cout << "reinterpret_cast: address of pi = " << pVoid << "\n";

// ** Use case: Memory-Mapped I/O **
// C
// ******************** Use case: Memory-Mapped I/O ******************************
// C ===============================================
// #define REG_ADDR 0x000fff01
// volatile uint8_t* reg = reinterpret_cast<volatile uint8_t*>(REG_ADDR);
// *reg = 0xF;
// *reg = 0x1;

// C++
// C++ ===============================================
// #include <cstdint>
// constexpr std::uintptr_t REG_ADDR = 0x000fff02;
// auto* const reg = reinterpret_cast<volatile uint8_t*>(REG_ADDR);
// *reg = 0xF;

// *reg = 0x1;
// ********************************************************************************

// **5. dynamic_cast: safe cast between related classes - type-safe relationship + run-time type checking**
// RTTI (RunTime Type Information) check
// #include <typeinfor>
// if(typeid(*basePtr) == typeid(Derived)){
// Derived* derivedPtr = dynamic_cast<Derived*>(basePtr)
// }

// *5. dynamic_cast: safe cast between related classes (runtime checked)
Base* basePtr = new Derived();
const Derived* derivedPtr = dynamic_cast<Derived*>(basePtr);
if (derivedPtr)
Expand All @@ -102,11 +119,11 @@ void typeAliases() {

// *1. using - preferred
using MyDouble = double;
[[maybe_unused]] const MyDouble a = 3.14;
const MyDouble a = 3.14;

// *2. typedef - old style
typedef double OldDouble;
[[maybe_unused]] OldDouble b = 2.718;
OldDouble b = 2.718;

// *3. Function pointer alias
using FuncType = int (*)(double, char);
Expand Down
Loading