diff --git a/CMakeLists.txt b/CMakeLists.txt index 729cad4..871e64f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,6 +79,14 @@ set(APP_SOURCES "src/core/basics/InitializeVariable.cpp" "src/core/basics/Operations.cpp" "src/core/basics/ControlFlow.cpp" + "src/core/datatypes/Fundamental.cpp" + "src/core/datatypes/CArray.cpp" + "src/core/datatypes/CReferences.cpp" + "src/core/datatypes/CPointers.cpp" + "src/core/datatypes/CEnum.cpp" + "src/core/datatypes/CStruct.cpp" + "src/core/datatypes/CUnion.cpp" + "src/core/datatypes/TypeConVersions.cpp" ) # Test files diff --git a/src/core/datatypes/CArray.cpp b/src/core/datatypes/CArray.cpp new file mode 100644 index 0000000..66ec736 --- /dev/null +++ b/src/core/datatypes/CArray.cpp @@ -0,0 +1,28 @@ +#include + +void arrayExamples() +{ + std::cout << "\n--- Array Examples ---\n"; + + const int arr[5] = {1, 2, 3, 4, 5}; + for (int i = 0; i < 5; ++i) + { + std::cout << "arr[" << i << "] = " << arr[i] << "\n"; + } + + double matrix[2][3] = {{1.1, 2.2, 3.3}, {4.4, 5.5, 6.6}}; + for (int i = 0; i < 2; ++i) + { + for (int j = 0; j < 3; ++j) + { + std::cout << "matrix[" << i << "][" << j << "] = " << matrix[i][j] << "\n"; + } + } +} + +struct CArray +{ + CArray() { arrayExamples(); } +}; + +static CArray autoRunArray; diff --git a/src/core/datatypes/CEnum.cpp b/src/core/datatypes/CEnum.cpp new file mode 100644 index 0000000..0e138cd --- /dev/null +++ b/src/core/datatypes/CEnum.cpp @@ -0,0 +1,73 @@ +#include +#include // for uint8_t + +using namespace std; +// A type defined in terms of other types + +// *0. Define some enums +// Unscope enum +enum BasicEnum +{ + enumratorA, + enumratorB, + enumratorC +}; + +// Scoped enum - enumrators are inside enum's scope +enum class ScopeEnumClass +{ + enumratorA, + enumratorB, + enumratorC +}; + +// Scoped enum inside a namespace +namespace EnumNameSpace +{ + enum class ScopeEnumClass + { + enumratorA, + enumratorB, + enumratorC + }; +} + +// Scoped enum with explicit base type +enum class ScopeEnumClassB : uint8_t +{ + enumratorA = 0, + enumratorB = 1, + enumratorC = 2 +}; + +void enums() +{ + cout << "\n--- Enum Type Examples ---\n"; + // *1. Using unscope enum + [[maybe_unused]] BasicEnum unscope_e = enumratorA; + + // *2. Using scoped enum + [[maybe_unused]] ScopeEnumClass scope_e_c = ScopeEnumClass::enumratorA; + + // *3. Using scoped enum namespace + [[maybe_unused]] EnumNameSpace::ScopeEnumClass scope_e_c_n = EnumNameSpace::ScopeEnumClass::enumratorA; + + // *4. Using scoped enum with base type + [[maybe_unused]] ScopeEnumClassB st = ScopeEnumClassB::enumratorA; +} + +// A struct that runs code when its object is created +struct CEnum +{ + CEnum() + { + cout << "\n" + << "\n" + << "Compound type: Enum\n"; + + enums(); + } +}; + +// All global and static objects are constructed before main() begins. +static CEnum autoRunInstance; diff --git a/src/core/datatypes/CPointers.cpp b/src/core/datatypes/CPointers.cpp new file mode 100644 index 0000000..a54be6c --- /dev/null +++ b/src/core/datatypes/CPointers.cpp @@ -0,0 +1,125 @@ +#include +using namespace std; +// A type defined in terms of other types + +// Simple function to demonstrate function pointers +int foo(int x) +{ + cout << "x = " << x << endl; + return x; +} + +void byPtr(int *x) +{ + (*x)++; + cout << "x = " << *x << endl; +} + +void byConstPtr(const int *const x) +{ + // *x++; // error + cout << "x = " << *x << endl; +} + +void byPtrConst(const int *x) +{ + // *x++; // error + cout << "x = " << *x << endl; +} + +void byConstPtrConst(const int *const x) +{ + // *x++; // error + cout << "x = " << *x << endl; +} + +static int global = 42; +int *returnPtr() +{ + return &global; // the object must outlive the reference. +} + +void pointers() +{ + cout << "\n--- Pointers Type Examples ---\n"; + int a = 10; + cout << "a = " << a << "\n"; + + // * 1. Pointer basics + const int *null_ptr = nullptr; + cout << "Address of nullptr (null_ptr): " << null_ptr << "\n"; + + // '&' gives the address of a variable + int *ptr_a = &a; + cout << "Address of a (&a): " << &a << "\n"; + cout << "Value stored in pointer (ptr_a): " << ptr_a << "\n"; + + // '*' dereferences a pointer (accesses the value at that address) + cout << "Value of a via *ptr_a: " << *ptr_a << "\n"; + + // Change value of a through its pointer + *ptr_a = 2; + cout << "Value of a after *ptr_a = 2: " << a << "\n"; + + // * 2. Pointer to const + [[maybe_unused]] const int const_var = 100; + const int *ptr_const_var = &const_var; + // *ptr_const_var = 10; // cannot modify the value through pointer + cout << "Value of ptr_const_var " << *ptr_const_var << "\n"; + ptr_const_var = &a; // can point somewhere else + cout << "Value of ptr_const_var " << *ptr_const_var << "\n"; + + // * 3. Const pointer + int *const const_ptr_a = &a; + *const_ptr_a = 10; // can change value + // const_ptr_a = nullptr; //cannot point to another variable + + // * 4. Const pointer to const + [[maybe_unused]] const int *const const_ptr_const_var = &a; + // *const_ptr_const_var = 10; // cannot modify the value through pointer + // const_ptr_const_var = nullptr; //cannot point to another variable + + // * 5. Pointer to pointer + int **ptr_ptr_a = new int *[10]; // dynamically allocate an array of 10 int* + ptr_ptr_a[0] = &a; + cout << "Value via pointer-to-pointer (*ptr_ptr_a[0]): " << *ptr_ptr_a[0] << "\n"; + delete[] ptr_ptr_a; // always free heap memory + + // * 6. Void pointer (generic pointer) + // void *void_ptr = static_cast(&a); + void *void_ptr = &a; // C-style pointer casting [cstyleCast] + + cout << "Value via void pointer (after casting): " << *static_cast(void_ptr) << "\n"; + + // * 7. Function pointer + int (*fcn_ptr)(int) = &foo; + (*fcn_ptr)(5); // call via dereference + fcn_ptr(10); // call directly + + // 8. Passing pointers + int in = 10; + byPtr(&in); + byConstPtr(&in); + byPtrConst(&in); + byConstPtrConst(&in); + + // * 9. Return by ptr(address) + const int *b_ptr = returnPtr(); + std::cout << "By ptr: " << *b_ptr << '\n'; +} + +// A struct that runs code when its object is created +struct CPointers +{ + CPointers() + { + cout << "\n" + << "\n" + << "Compound type: Pointers\n"; + + pointers(); + } +}; + +// All global and static objects are constructed before main() begins. +static CPointers autoRunInstance; diff --git a/src/core/datatypes/CReferences.cpp b/src/core/datatypes/CReferences.cpp new file mode 100644 index 0000000..533fea5 --- /dev/null +++ b/src/core/datatypes/CReferences.cpp @@ -0,0 +1,65 @@ +#include +using namespace std; +// A type defined in terms of other types + +void increment(int &x) +{ + x += 1; +} + +void incrementConst(const int &x) +{ + // s cannot be modified here + // x += 1; +} + +static int global = 42; +int &returnRef() +{ + return global; // the object must outlive the reference. +} + +void references() +{ + cout << "\n--- References Type Examples ---\n"; + int a = 10; + cout << "a = " << a << "\n"; + + // *1. Reference basics + // int &ref_error; // reference must be initialized + int &ref_a = a; + + ref_a = 20; // modifies 'a', since ref is just an alias + cout << "ref_a =20, a = " << a << "\n"; // prints 20 + + // Cannot reseat: once 'ref' is bound to 'a', it cannot be bound to another variable + // int b = 30; + // ref = &b; invalid, would assign value instead of rebinding + + // *2. Pass by reference + increment(a); // avoids making a copy + std::cout << "a after increment = " << a << "\n"; // prints 21 + + // *3. Pass by const reference + incrementConst(a); + + // *4. Return by reference + int const &b = returnRef(); + std::cout << "By reference: " << b << '\n'; +} + +// A struct that runs code when its object is created +struct CReferences +{ + CReferences() + { + cout << "\n" + << "\n" + << "Compound type: References\n"; + + references(); + } +}; + +// All global and static objects are constructed before main() begins. +static CReferences autoRunInstance; diff --git a/src/core/datatypes/CStruct.cpp b/src/core/datatypes/CStruct.cpp new file mode 100644 index 0000000..eedb43b --- /dev/null +++ b/src/core/datatypes/CStruct.cpp @@ -0,0 +1,86 @@ +#include +#include + +using namespace std; +// A type defined in terms of other types + +// *0. Define a struct: +// structure alignment, padding, decreasing order of size +// public access +// In C, structs can only hold data members, not functions. +struct StructDataType +{ + /* data */ + double voltage{0.0}; // Default initialization + int id{0}; + char status{'N'}; + string label{"Unknown"}; + + /* function */ + void print() const + { + cout << "Size of struct = " << sizeof(StructDataType) << " bytes" << endl; + cout << "Sensor " << id + << " [" << label << "] " + << "Voltage: " << voltage + << " Status: " << status << endl; + } +}; + +void updateVoltageRef(StructDataType &data, double newV) +{ + data.voltage = newV; +} + +void updateVoltagePtr(StructDataType *data, double newV) +{ + // (*data).voltage = newV; + if (data) + data->voltage = newV; +} + +StructDataType makeSensor(int id, double v, const string &label) +{ + return {v, id, 'N', label}; +} + +void structs() +{ + cout << "\n--- Struct Type Examples ---\n"; + // *1. Using struct + [[maybe_unused]] StructDataType data0; // default init + StructDataType data1{3.3, 1, 'N', "Temp1"}; + StructDataType data2 = {6.3, 2, 'N', "Temp2"}; + + // *2. Access members + data1.print(); + const StructDataType *ptr_data1 = &data1; + ptr_data1->print(); + + // *3. Passing by reference + updateVoltageRef(data2, 100); + data2.print(); + // *4. Passing by pointer + updateVoltagePtr(&data2, 200); + data2.print(); + + // *5. Return a temporary struct + StructDataType s2 = makeSensor(2, 5.0, "Pressure"); + s2.print(); +} + +// A struct that runs code when its object is created +struct CStruct +{ + CStruct() + { + cout << "\n" + << "\n" + << "Compound type: Struct\n"; + + structs(); + } +}; + +// All global and static objects are constructed before main() begins. +static CStruct autoRunInstance; diff --git a/src/core/datatypes/CUnion.cpp b/src/core/datatypes/CUnion.cpp new file mode 100644 index 0000000..4a5a4d2 --- /dev/null +++ b/src/core/datatypes/CUnion.cpp @@ -0,0 +1,54 @@ +#include +#include + +// --- Union Definition --- +// All members share the same memory +union UnionDataType +{ + int intValue; + float floatValue; + char charValue; + + void printAll() const + { + std::cout << "intValue = " << intValue + << ", floatValue = " << floatValue + << ", charValue = " << charValue << "\n"; + } +}; + +// --- Demonstrate unique property --- +void unionDemo() +{ + std::cout << "\n--- Union Unique Behavior ---\n"; + + UnionDataType u; + + u.intValue = 65; + std::cout << "After assigning intValue = 65:\n"; + u.printAll(); // Only intValue is meaningful; others show overwritten memory + + u.floatValue = 3.14f; + std::cout << "After assigning floatValue = 3.14:\n"; + u.printAll(); // Writing floatValue overwrites intValue + + u.charValue = 'A'; + std::cout << "After assigning charValue = 'A':\n"; + u.printAll(); // Writing charValue overwrites both intValue and floatValue + + std::cout << "Size of union = " << sizeof(UnionDataType) << " bytes\n"; + std::cout << "Notice: Only one value is valid at a time.\n"; +} + +// --- Auto-run struct --- +struct CUnion +{ + CUnion() + { + std::cout << "\nCompound type: Union\n"; + unionDemo(); + } +}; + +// Constructed before main() +static CUnion autoRunUnionInstance; \ No newline at end of file diff --git a/src/core/datatypes/Fundamental.cpp b/src/core/datatypes/Fundamental.cpp new file mode 100644 index 0000000..55316ba --- /dev/null +++ b/src/core/datatypes/Fundamental.cpp @@ -0,0 +1,68 @@ +#include +using namespace std; + +// A basic type built into the core C++ language +void primative() +{ + cout << "\n--- Primative Type Examples ---\n"; + // Boolean + bool isReady = true; + cout << "bool: " << isReady << "\n"; + + // Character + char c = 'A'; + unsigned char uc = 200; + wchar_t wc = L'Ω'; // Greek Omega + // char8_t c8 = u8'A'; // UTF-8 + char16_t c16 = u'ß'; // UTF-16 + char32_t c32 = U'中'; // UTF-32 + cout << "char: " << c << "\n"; + cout << "unsigned char: " << static_cast(uc) << "\n"; + wcout << L"wchar_t: " << wc << L"\n"; + // cout << "char8_t: " << static_cast(c8) << "\n"; + cout << "char16_t: (UTF-16 code) " << static_cast(c16) << "\n"; + cout << "char32_t: (UTF-32 code) " << static_cast(c32) << "\n"; + + // Integer + short s = -10; + int i = 42; + unsigned int ui = 100; + long l = 123456L; + long long ll = 9876543210LL; + cout << "short: " << s << "\n"; + cout << "int: " << i << "\n"; + cout << "unsigned int: " << ui << "\n"; + cout << "long: " << l << "\n"; + cout << "long long: " << ll << "\n"; + + // Floating Point + float f = 3.14f; + double d = 2.718281828; + long double ld = 1.6180339887L; + cout << "float: " << f << "\n"; + cout << "double: " << d << "\n"; + cout << "long double: " << ld << "\n"; + + // Void + cout << "void: (no data type, used for functions)\n"; + + // pointer + int const *ptr = nullptr; + cout << "nullptr_t: " << ptr << "\n"; +} + +// A struct that runs code when its object is created +struct Fundamental +{ + Fundamental() + { + cout << "\n" + << "\n" + << "Fundamental\n"; + + primative(); + } +}; + +// All global and static objects are constructed before main() begins. +static Fundamental autoRunInstance; diff --git a/src/core/datatypes/TypeConVersions.cpp b/src/core/datatypes/TypeConVersions.cpp new file mode 100644 index 0000000..49eeb0c --- /dev/null +++ b/src/core/datatypes/TypeConVersions.cpp @@ -0,0 +1,153 @@ +#include +using namespace std; + +class Base +{ +public: + virtual void show() { cout << "Base class\n"; } + virtual ~Base() = default; +}; + +class Derived : public Base +{ +public: + void show() override { cout << "Derived class\n"; } +}; + +void implicitConversion() +{ + cout << "\n--- Implicit Type Conversion ---\n"; + // *1. Numeric promotion (safe, no data loss) + char c = 'A'; + int i = c; // char → int + float f = 3.5f; + double d = f; // float → double + bool b = true; + int b_to_int = b; // bool → int (true=1) + + cout << "char to int: " << i << "\n"; + cout << "float to double: " << d << "\n"; + cout << "bool to int: " << b_to_int << "\n"; + + // *2. Numeric conversion + float pi = 3.14159; + double pi_double = pi; // Widening conversions + int pi_int = pi; // narrowing, may lose fractional part + cout << "float to double (didening): " << pi_double << "\n"; + cout << "float to int (narrowing): " << pi_int << "\n"; +} + +void explicitConversion() +{ + cout << "\n--- Explicit Type Conversion ---\n"; + + double pi = 3.14159; + + // *1. C-style cast + int pi_c = (int)pi; + cout << "C-style cast: " << pi_c << "\n"; + + // *2. static_cast - compile-time type checking + int pi_static = static_cast(pi); + cout << "static_cast: " << pi_static << "\n"; + + // object -> object + Derived derived{}; + [[maybe_unused]] Base baseObj = static_cast(derived); + // object -> reference + const Base &baseRef = static_cast(derived); + // object -> ptr + [[maybe_unused]] const Base *base_ptr = static_cast(&derived); + + // *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(pConst); // remove const + cout << "const_cast: " << *pNonConst << " (removed const)\n"; + + // *4. reinterpret_cast: reinterpret memory (unsafe) + const void *pVoid = reinterpret_cast(&pi); + cout << "reinterpret_cast: address of pi = " << pVoid << "\n"; + + // *5. dynamic_cast: safe cast between related classes (runtime checked) + Base *basePtr = new Derived(); + const Derived *derivedPtr = dynamic_cast(basePtr); + if (derivedPtr) + cout << "dynamic_cast: Success (Base* -> Derived*)\n"; + else + cout << "dynamic_cast: Failed\n"; + + Base *anotherBase = new Base(); + const Derived *wrongCast = dynamic_cast(anotherBase); + if (!wrongCast) + cout << "dynamic_cast: nullptr (invalid downcast)\n"; + + delete basePtr; + delete anotherBase; +} + +void typeAliases() +{ + cout << "\n--- Type Aliases ---\n"; + + // *1. using - preferred + using MyDouble = double; + [[maybe_unused]] const MyDouble a = 3.14; + + // *2. typedef - old style + typedef double OldDouble; + [[maybe_unused]] OldDouble b = 2.718; + + // *3. Function pointer alias + using FuncType = int (*)(double, char); + auto func = [](double x, char c) + { + return static_cast(x) + c; + }; + FuncType fptr = func; + cout << "Function pointer alias result: " << fptr(2.5, 'A') << "\n"; +} + +int add(int x, int y) +{ + return (x + y); +} + +auto add_auto(int a, int b) -> int +{ + return a + b; +} + +void typeDeduction() +{ + cout << "\n--- Type Deduction ---\n"; + + // *1. auto deduces type + auto x = 42; // int + auto y = 3.14; // double + const auto z = x + y; // double, const ignored in type deduction + + cout << "auto x: " << x << ", y: " << y << ", z: " << z << "\n"; + + // *2. Trailing return type + cout << "add(3,4) = " << add(3, 4) << "\n"; + cout << "add_auto(3,4) = " << add_auto(3, 4) << "\n"; + + // *3. std::common_type + std::common_type_t val = x + y; + cout << "common_type: " << val << "\n"; +} + +struct CTypeConversion +{ + CTypeConversion() + { + implicitConversion(); + explicitConversion(); + + typeAliases(); + typeDeduction(); + } +}; + +static CTypeConversion autoRunTypes; \ No newline at end of file