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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
*build
*private*
*private*
*.vscode
15 changes: 14 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,20 @@ cmake_minimum_required(VERSION 3.14)
# ----------------------------------------------------------------------------------------
project(cpp_lab_project # ${PROJECT_NAME}
VERSION 1.0.0
DESCRIPTION "The project with GoogleTest and CMake"
DESCRIPTION "A C/C++ project uses CMake, GoogleTest, gcc, g++, cppcheck, and lcov, integrated with Docker and GitHub Actions for CI/CD."
LANGUAGES CXX
)

# Build timestamp
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/include)
string(TIMESTAMP BUILD_TIME "%Y-%m-%d %H:%M:%S")

configure_file(
${CMAKE_SOURCE_DIR}/include/version.h.in
${CMAKE_BINARY_DIR}/generated/version.h
)


set(PROJECT_NAME_TEST ${PROJECT_NAME}_unit_test) # name for the unit-test executable

# ----------------------------------------------------------------------------------------
Expand Down Expand Up @@ -60,11 +70,14 @@ enable_testing()
# Header files directory
set(APP_HEADERS
"include"
"${CMAKE_BINARY_DIR}/generated" # cmake generated headers
)

# Core source files
set(APP_SOURCES
"src/DeleteMe.cpp"
"src/core/basics/InitializeVariable.cpp"
"src/core/basics/Operations.cpp"
)

# Test files
Expand Down
67 changes: 40 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,54 +1,67 @@
## OVERVIEW
- Structure:
includes/ → header files (.h, .hpp)
src/ → source files (.cpp)
tests/ → GoogleTest test cases
## 1. OVERVIEW
**Project Structure**
```
includes/ → Header files (.h, .hpp)
src/ → Source files (.cpp)
tests/ → GoogleTest test cases
```
## 2. DEPENDENCIES
Make sure the following tools are installed before building the project:
- **g++ / gcc**
- **CMake**
- **Git**
- **lcov** (for code coverage)
- **cppcheck** (for static analysis)

## DEPENDENCIES

## SETUP
## 3. SETUP
* Setup the Local Test Environment
* Using your own Ubuntu system
* 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++=4:5.3.1-1ubuntu1
$ sudo apt-get install lcov=1.12-2
$ sudo apt-get install cmake=3.5.1-1ubuntu3
$ sudo apt-get install git=1:2.7.4-0ubuntu1.6
$ sudo apt-get install libpthread-stubs0-dev=0.3-4

$ 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 the cpp check
* (Optional) Run static analysis
```
$ sudo apt-get install cppcheck
$ cppcheck "folder" / "file"
```
* Using **Docker**
* 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 → 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).
* 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).

## DOCUMENTATIONs
## 4. DOCUMENTATIONS
6 changes: 6 additions & 0 deletions include/version.h.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#pragma once

#define APP_NAME "@PROJECT_NAME@"
#define APP_VERSION "@PROJECT_VERSION@"
#define APP_DESCRIPTION "@PROJECT_DESCRIPTION@"
#define APP_BUILD_TIME "@BUILD_TIME@"
65 changes: 65 additions & 0 deletions src/core/basics/InitializeVariable.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#include <iostream>
using namespace std;

void initialize_variable();

// A struct that runs code when its object is created
struct InitializeVariable
{
InitializeVariable()
{
cout << "\n"
<< "\n"
<< "InitializeVariable\n";
initialize_variable();
}
};

// All global and static objects are constructed before main() begins.
static InitializeVariable autoRunInstance;

struct Foo
{
Foo()
{
cout << "Default constructor/ default init\n";
}

// explicit Foo(int)
explicit Foo(int)
{
cout << "Constructor called with int / copy init\n";
}

Foo(const Foo &other)
{
std::cout << "Copy constructor called\n";
}
};

void initialize_variable()
{
cout << "\n--- Variable Initialization Examples ---\n";
// There are there common ways to intialize a variable
// * Default
[[maybe_unused]] int initDefaultVar;
[[maybe_unused]] Foo initDefaultObj;

// * Traditional initialization
// * Copy-init: Type var = value;
// 1. Compiler tries to convert the value to a temporary Foo.
// 2. If the constructor is explicit, implicit conversion is blocked -> compilation error.
// 3. Otherwise, a temporary Foo is created and then copied/moved into the variable.
[[maybe_unused]] Foo copyInitObj = (Foo)2.3; // explicit cast: double 2.3 -> int 2 (implicit narrowing) -> Foo(int) -> temporary Foo -> copied/moved into copyInitObj
// Foo copyInitObjError = 2.3; // ERROR: implicit conversion blocked by explicit constructor
// We can explicitly prevent certain conversions using = delete or using {}

// * direct-init: Type var(value);
[[maybe_unused]] Foo directInitObj(4); // call Foo(int)
[[maybe_unused]] Foo directInitObj2(4.3); // look for constructor -> implicit 4.3(float) -> 4(int) -> call Foo(int) ->

// * Brace init
// calls the constructor directly without allowing implicit conversions.
[[maybe_unused]] Foo braceInit{3};
// Foo braceInit2{3.3}; // ERORR => Prefer this way
}
129 changes: 129 additions & 0 deletions src/core/basics/Operations.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
#include <iostream>
using namespace std;

void arithmeticOperator();
void logicalOperator();
void bitWiseOperator();

struct Operations
{
Operations()
{
cout << "\n"
<< "\n"
<< "Operation\n";
arithmeticOperator();
logicalOperator();
bitWiseOperator();
}
};

static Operations autoRunInstance;

void arithmeticOperator()
{
cout << "\n--- ArithmeticOperator Examples ---\n";
int a{100}, b{200};

// Addition
cout << "a = " << a << ", b = " << b << "\n";
int sum = a + b;
cout << "sum = " << sum << "\n";

// Subtraction
cout << "a = " << a << ", b = " << b << "\n";
int different = a - b;
cout << "different = " << different << "\n";

// Multiplication
cout << "a = " << a << ", b = " << b << "\n";
int product = a * b;
cout << "product = " << product << "\n";

// Division
cout << "a = " << a << ", b = " << b << "\n";
int quotient = a / b;
cout << "quotient = " << quotient << "\n";

// Modulus
cout << "a = " << a << ", b = " << b << "\n";
int remainder = a % b;
cout << "remainder = " << remainder << "\n";

// Increment
cout << "a = " << a << "\n";
int preIn = ++a; // increase a, return copy
cout << "preIn = " << preIn << "\n";

cout << "a = " << a << "\n";
int postIn = a++; // copy a to a copy, then increase a, return copy
cout << "postIn = " << postIn << "\n";

// Decrement
cout << "b = " << b << "\n";
int preDe = --b;
cout << "preDe = " << preDe << "\n";

cout << "b = " << b << "\n";
int postDe = b--;
cout << "postDe = " << postDe << "\n";

// Comma:
int value = (a++, b); // a is incremented, then b is returned
cout << "a = " << a << ", b = " << b << "\n";
cout << "comma(a++, b) = " << value << "\n";
}

void logicalOperator()
{
cout << "\n--- LogicalOperator Examples ---\n";
bool a = true;
bool b = false;
bool c = true;

cout << boolalpha; // show true/false instead of 1/0
cout << "a = " << a << ", b = " << b << ", c = " << c << "\n\n";

// AND (&&)
cout << "[AND] a && b = " << (a && b) << "\n";

// OR (||)
cout << "[OR ] a || b = " << (a || b) << "\n";

// NOT (!)
cout << "[NOT] !c = " << (!c) << "\n";
}

#include <bitset>
void bitWiseOperator()
{
cout << "\n--- BitWiseOperator Examples ---\n";
bitset<8> bitsA{0b1111'1111};
bitset<8> bitsB{0b1111'0000};

cout << "bitA = " << bitsA << ", bitB = " << bitsB << "\n";

// AND
bitset<8> result = bitsA & bitsB;
cout << "bitA && bitB= " << result << "\n";

// OR
result = bitsA | bitsB;
cout << "bitA | bitB= " << result << "\n";

// XOR
result = bitsA ^ bitsB;
cout << "bitA ^ bitB= " << result << "\n";

// NOT
result = ~bitsA;
cout << "~bitA = " << result << "\n";

// LEFT SHIFT
result = bitsA << 1;
cout << "bitA << 1 = " << result << "\n";

// RIGHT SHIFT
result = bitsA >> 1;
cout << "bitA >> 1 = " << result << "\n";
}
4 changes: 3 additions & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#include <iostream>
#include "version.h"

int main(int argc, char *argv[])
{
std::cout << "Hello World" << std::endl;
std::cout << APP_NAME << " v" << APP_VERSION << std::endl;
std::cout << APP_DESCRIPTION << std::endl;
return 0;
}