├── Images ├── Vehicle_Platform_Dependency.png ├── three_little_musketeers.png └── three_little_pigs.png ├── LICENSE ├── README.md ├── docs ├── C++20 │ ├── Asynchronous programming.pdf │ ├── Concepts.pdf │ ├── Coroutines - part 2.pdf │ ├── Coroutines.pdf │ ├── Coroutines__producer-consumer.pdf │ ├── Locking policy.pdf │ └── Ranges.pdf ├── TDD │ └── Testing within ASPICE context with C++.pdf ├── android │ ├── JNI Interface.pdf │ ├── Kotlin │ │ ├── Kotlin - builder pattern.pdf │ │ ├── Kotlin - fold expressions.pdf │ │ └── Kotlin - functional programming.pdf │ ├── RxAndroid.pdf │ └── Thread__Attributes.pdf ├── basics │ ├── ABI - overview.pdf │ ├── Arrays.pdf │ ├── Bits.pdf │ ├── Callables__C++_vs_Java.pdf │ ├── Enums.pdf │ ├── Invariant, covariant and contravariant.pdf │ ├── Range-based for loop.pdf │ ├── Strings.pdf │ ├── [Callables][C++_vs_Java].md │ ├── vtable#2.pdf │ └── vtable.pdf ├── desing patterns │ ├── Bridge design pattern.pdf │ ├── Builder.pdf │ ├── Expression templates.pdf │ ├── Factory method.pdf │ ├── Observer.pdf │ ├── Singleton.pdf │ └── Visitor.pdf ├── index.md ├── memory │ ├── Local allocator.pdf │ ├── Lock-free programming, part 1.pdf │ ├── Lock-free_programming__MPSC_queue.pdf │ └── Memory alignment.pdf └── rss.xml ├── generate_rss.py └── src ├── AOT ├── AOThread.h ├── AOThread_v2.h ├── JobQueue.h └── java │ ├── AOThread.java │ ├── Looper.java │ └── TestAOT.java ├── Event ├── Event.cpp ├── Event.h ├── Event20.hpp ├── Event_with_future_and_promises.pdf ├── LockFreeEvent.cpp ├── Monitor.hpp └── java │ ├── AutoResetEvent.java │ ├── Event.java │ └── TestEvent.java ├── FileStreams ├── BinaryInputFileStream.h ├── BinaryOutputFileStream.h ├── CharInputFileStream.h ├── CharOutputFileStream.h ├── FileStream.cpp └── FileStream.h ├── Kotlin ├── .gitignore ├── build.gradle └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── practice_kotlin │ │ │ ├── MainActivity.kt │ │ │ ├── coroutines │ │ │ └── CoroutinesWrapper.kt │ │ │ ├── design_patterns │ │ │ └── builder │ │ │ │ ├── Builder.kt │ │ │ │ ├── Builder2.kt │ │ │ │ └── Builder3.kt │ │ │ ├── helpers │ │ │ └── ElapsedTime.kt │ │ │ └── ui │ │ │ └── theme │ │ │ ├── Color.kt │ │ │ ├── Theme.kt │ │ │ └── Type.kt │ └── res │ │ ├── drawable │ │ ├── ic_launcher_background.xml │ │ └── ic_launcher_foreground.xml │ │ ├── mipmap-anydpi │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ └── data_extraction_rules.xml │ └── test │ └── java │ └── com │ └── example │ └── practice_kotlin │ ├── coroutines │ └── TestCoroutines.kt │ └── design_patterns │ └── builder │ └── TestBuilderPattern.kt ├── Lock ├── ClassLevelMutex.h └── LockingPolicies_c++20.cpp ├── Logging ├── ConsoleLogger.h ├── console │ ├── ConsoleLogger.h │ ├── CoutLogger.h │ ├── Logger.h │ ├── LoggerWrapper.h │ ├── LoggingHelper.h │ ├── LoggingMerge.h │ └── LoggingTag.h └── file │ ├── DataLogger.h │ ├── FileLogger.cpp │ └── FileLogger.h ├── Thread └── ThreadWrapper.h ├── Tutorial 1.2 └── main.cpp ├── Tutorial 1 └── main.cpp ├── Tutorial 2 ├── Test1.cpp └── Test2.cpp ├── Tutorial 3 ├── Setter.hxx ├── TestTuples.cxx └── TestTuples.hxx ├── Tutorial 4 ├── TestFP.cxx └── TestFP.hxx ├── Tutorial 5 ├── ClientWithVariant.hxx ├── DIContainer.hxx ├── DIFactory.hxx ├── Factory.hxx ├── TestCases.hxx ├── TestDependencyInjection.cxx ├── TestDependencyInjection.hxx ├── TestDependencyWithVariant.cxx └── TestDependencyWithVariant.hxx ├── Tutorial 6 ├── locking │ ├── ClassLevelLock.h │ ├── NonLock.h │ ├── ObjecLevelLock.h │ ├── TestLockingPolicy.cpp │ └── TestLockingPolicy.h └── mixin │ ├── TestMixin.cpp │ └── TestMixin.h ├── Tutorial 7 ├── external_polymorphism │ ├── ConsoleLogger.hxx │ ├── Logger.hxx │ ├── TestLogger.cxx │ ├── TestLogger.hxx │ ├── Type1.hxx │ └── Type2.hxx └── type_erasure │ ├── Car.hxx │ ├── DrivingType.hxx │ ├── TestTypeErasure.cxx │ ├── TestTypeErasure.hxx │ ├── Truck.hxx │ └── Vehicle.hxx ├── commons └── Commons.h ├── coroutines ├── Classical_producer_consumer.cpp ├── Coroutine_producer_consumer_1.cpp └── Coroutine_producer_consumer_2.cpp ├── desing_patterns ├── Bridge.cpp ├── Builder.cpp ├── Expression_template_radian.cpp ├── Expressions_template_binary_expr.cpp ├── FactoryMethod_different_implementations.cpp ├── Factory_customer_allocation.cpp ├── Observer.cpp ├── Publisher.hpp ├── Subscriber.hpp └── main.cpp ├── directory ├── Directory.cpp ├── Directory.h ├── TestDirectoryImpl.cpp └── TestDirectoryImpl.h ├── measuring └── ElapsedTime.h └── ring buffer ├── MPMC_lock-free_queue.cpp ├── MPSC_lock-free_queue.cpp └── RingBuffer_c++20.cpp /Images/Vehicle_Platform_Dependency.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/Images/Vehicle_Platform_Dependency.png -------------------------------------------------------------------------------- /Images/three_little_musketeers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/Images/three_little_musketeers.png -------------------------------------------------------------------------------- /Images/three_little_pigs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/Images/three_little_pigs.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Damir Ljubic 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/C++20/Asynchronous programming.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/C++20/Asynchronous programming.pdf -------------------------------------------------------------------------------- /docs/C++20/Concepts.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/C++20/Concepts.pdf -------------------------------------------------------------------------------- /docs/C++20/Coroutines - part 2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/C++20/Coroutines - part 2.pdf -------------------------------------------------------------------------------- /docs/C++20/Coroutines.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/C++20/Coroutines.pdf -------------------------------------------------------------------------------- /docs/C++20/Coroutines__producer-consumer.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/C++20/Coroutines__producer-consumer.pdf -------------------------------------------------------------------------------- /docs/C++20/Locking policy.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/C++20/Locking policy.pdf -------------------------------------------------------------------------------- /docs/C++20/Ranges.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/C++20/Ranges.pdf -------------------------------------------------------------------------------- /docs/TDD/Testing within ASPICE context with C++.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/TDD/Testing within ASPICE context with C++.pdf -------------------------------------------------------------------------------- /docs/android/JNI Interface.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/android/JNI Interface.pdf -------------------------------------------------------------------------------- /docs/android/Kotlin/Kotlin - builder pattern.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/android/Kotlin/Kotlin - builder pattern.pdf -------------------------------------------------------------------------------- /docs/android/Kotlin/Kotlin - fold expressions.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/android/Kotlin/Kotlin - fold expressions.pdf -------------------------------------------------------------------------------- /docs/android/Kotlin/Kotlin - functional programming.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/android/Kotlin/Kotlin - functional programming.pdf -------------------------------------------------------------------------------- /docs/android/RxAndroid.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/android/RxAndroid.pdf -------------------------------------------------------------------------------- /docs/android/Thread__Attributes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/android/Thread__Attributes.pdf -------------------------------------------------------------------------------- /docs/basics/ABI - overview.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/basics/ABI - overview.pdf -------------------------------------------------------------------------------- /docs/basics/Arrays.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/basics/Arrays.pdf -------------------------------------------------------------------------------- /docs/basics/Bits.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/basics/Bits.pdf -------------------------------------------------------------------------------- /docs/basics/Callables__C++_vs_Java.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/basics/Callables__C++_vs_Java.pdf -------------------------------------------------------------------------------- /docs/basics/Enums.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/basics/Enums.pdf -------------------------------------------------------------------------------- /docs/basics/Invariant, covariant and contravariant.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/basics/Invariant, covariant and contravariant.pdf -------------------------------------------------------------------------------- /docs/basics/Range-based for loop.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/basics/Range-based for loop.pdf -------------------------------------------------------------------------------- /docs/basics/Strings.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/basics/Strings.pdf -------------------------------------------------------------------------------- /docs/basics/vtable#2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/basics/vtable#2.pdf -------------------------------------------------------------------------------- /docs/basics/vtable.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/basics/vtable.pdf -------------------------------------------------------------------------------- /docs/desing patterns/Bridge design pattern.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/desing patterns/Bridge design pattern.pdf -------------------------------------------------------------------------------- /docs/desing patterns/Builder.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/desing patterns/Builder.pdf -------------------------------------------------------------------------------- /docs/desing patterns/Expression templates.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/desing patterns/Expression templates.pdf -------------------------------------------------------------------------------- /docs/desing patterns/Factory method.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/desing patterns/Factory method.pdf -------------------------------------------------------------------------------- /docs/desing patterns/Observer.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/desing patterns/Observer.pdf -------------------------------------------------------------------------------- /docs/desing patterns/Singleton.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/desing patterns/Singleton.pdf -------------------------------------------------------------------------------- /docs/desing patterns/Visitor.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/desing patterns/Visitor.pdf -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | 2 | # Welcome to Modern C++ Tutorials 3 | 4 | Check out the latest articles in [RSS feed](rss.xml). 5 | -------------------------------------------------------------------------------- /docs/memory/Local allocator.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/memory/Local allocator.pdf -------------------------------------------------------------------------------- /docs/memory/Lock-free programming, part 1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/memory/Lock-free programming, part 1.pdf -------------------------------------------------------------------------------- /docs/memory/Lock-free_programming__MPSC_queue.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/memory/Lock-free_programming__MPSC_queue.pdf -------------------------------------------------------------------------------- /docs/memory/Memory alignment.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/docs/memory/Memory alignment.pdf -------------------------------------------------------------------------------- /generate_rss.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | import xml.etree.ElementTree as ET 4 | from xml.dom import minidom 5 | from datetime import datetime 6 | 7 | # Constants 8 | docs_folder = "docs" 9 | rss_file = os.path.join(docs_folder, "rss.xml") 10 | 11 | def get_git_last_modified_time(file_path): 12 | try: 13 | output = subprocess.check_output( 14 | ["git", "log", "-1", "--format=%ct", "--", file_path], 15 | text=True 16 | ).strip() 17 | return int(output) 18 | except Exception: 19 | return 0 20 | 21 | def get_effective_mod_time(file_path): 22 | git_time = get_git_last_modified_time(file_path) 23 | return git_time if git_time != 0 else int(os.path.getmtime(file_path)) 24 | 25 | def format_rss_date(unix_timestamp): 26 | return datetime.utcfromtimestamp(unix_timestamp).strftime("%a, %d %b %Y %H:%M:%S GMT") 27 | 28 | def create_empty_rss_file(file_path): 29 | root = ET.Element("rss", version="2.0") 30 | channel = ET.SubElement(root, "channel") 31 | ET.SubElement(channel, "title").text = "Modern C++ Tutorials" 32 | ET.SubElement(channel, "link").text = "https://github.com/damirlj/modern_cpp_tutorials" 33 | ET.SubElement(channel, "description").text = "RSS feed for Modern C++ Tutorials" 34 | tree = ET.ElementTree(root) 35 | tree.write(file_path, encoding="utf-8", xml_declaration=True) 36 | print(f"Created new RSS file: {file_path}") 37 | 38 | def is_file_updated(mod_time, existing_pub_date): 39 | try: 40 | existing_time = datetime.strptime(existing_pub_date, "%a, %d %b %Y %H:%M:%S GMT").timestamp() 41 | return mod_time > existing_time 42 | except Exception as e: 43 | print(f"[Error] Failed to parse pubDate '{existing_pub_date}': {e}") 44 | return True 45 | 46 | def write_rss_feed(file_path, tree, channel): 47 | # Sort items by pubDate timestamp descending 48 | def item_timestamp(item): 49 | pub_date = item.find("pubDate").text 50 | try: 51 | return datetime.strptime(pub_date, "%a, %d %b %Y %H:%M:%S GMT").timestamp() 52 | except: 53 | return 0 54 | 55 | sorted_items = sorted(channel.findall("item"), key=item_timestamp, reverse=True) 56 | channel.clear() 57 | for item in sorted_items: 58 | channel.append(item) 59 | 60 | xml_str = ET.tostring(tree.getroot(), encoding="utf-8", method="xml").decode("utf-8") 61 | pretty_xml = minidom.parseString(xml_str).toprettyxml(indent=" ") 62 | xml_lines = [line for line in pretty_xml.splitlines() if line.strip()] 63 | with open(file_path, 'w', encoding="utf-8") as f: 64 | f.write('\n'.join(xml_lines)) 65 | 66 | print("RSS feed updated.") 67 | 68 | # Ensure RSS file exists 69 | if not os.path.exists(rss_file): 70 | create_empty_rss_file(rss_file) 71 | 72 | # Load existing RSS tree 73 | try: 74 | tree = ET.parse(rss_file) 75 | root = tree.getroot() 76 | channel = root.find("channel") 77 | existing_items = { 78 | item.find("link").text: item 79 | for item in channel.findall("item") 80 | } 81 | except ET.ParseError: 82 | print("Failed to parse existing RSS file.") 83 | exit(1) 84 | 85 | # Collect files 86 | docu_files = [ 87 | os.path.join(root, file) 88 | for root, _, files in os.walk(docs_folder) 89 | for file in files if file.endswith((".pdf", ".md")) 90 | ] 91 | 92 | changes_made = False 93 | 94 | for docu in docu_files: 95 | relative_path = os.path.relpath(docu, start=docs_folder) 96 | relative_path = relative_path.replace(os.sep, "/") 97 | link = f"https://github.com/damirlj/modern_cpp_tutorials/blob/main/docs/{relative_path}" 98 | title = os.path.splitext(os.path.basename(docu))[0].replace("_", " ") 99 | mod_time = get_effective_mod_time(docu) 100 | pub_date = format_rss_date(mod_time) 101 | 102 | if link in existing_items: 103 | item = existing_items[link] 104 | if is_file_updated(mod_time, item.find("pubDate").text): 105 | item.find("pubDate").text = pub_date 106 | print(f"Updated: {title}") 107 | changes_made = True 108 | else: 109 | item = ET.SubElement(channel, "item") 110 | ET.SubElement(item, "title").text = title 111 | ET.SubElement(item, "link").text = link 112 | ET.SubElement(item, "pubDate").text = pub_date 113 | print(f"Added: {title}") 114 | changes_made = True 115 | 116 | if changes_made: 117 | write_rss_feed(rss_file, tree, channel) 118 | else: 119 | print("No changes detected.") 120 | -------------------------------------------------------------------------------- /src/AOT/AOThread.h: -------------------------------------------------------------------------------- 1 | /* 2 | * AOThread.h 3 | * 4 | * Created on: Dec 30, 2020 5 | * Author: Damir Ljubic 6 | */ 7 | 8 | #ifndef AOT_AOTHREAD_H_ 9 | #define AOT_AOTHREAD_H_ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include "JobQueue.h" 16 | #include "Commons.h" 17 | #include "ThreadWrapper.h" 18 | 19 | 20 | 21 | namespace utils::aot 22 | { 23 | 24 | /** 25 | * AOT - Active Object Thread design pattern. 26 | * 27 | * Provides the thread context for executing asynchronous tasks. 28 | * The interaction with the client code is through restrictive interface which exposes single 29 | * method for tasks to be serialized - stored into the jobs queue. 30 | * Thread drains the queue providing the background context in which tasks will be executed 31 | * sequentially, one-by-one. 32 | * 33 | * @tparam R Return value type of task 34 | * 35 | */ 36 | template 37 | class AOThread final 38 | { 39 | 40 | public: 41 | 42 | using task_queue_t = JobQueue; 43 | 44 | AOThread(std::string name 45 | , utils::thread::ThreadWrapper::schedule_t policy 46 | , utils::thread::ThreadWrapper::priority_t priority): 47 | m_pJobQueue (std::make_unique()), 48 | m_pJobThread(utils::thread::make_thread_ptr(&AOThread::threadFunc, this)) 49 | 50 | { 51 | start(name, policy, priority); 52 | } 53 | 54 | ~AOThread()//user-defined c-tor: prevents generating default (memberwise) move-operations 55 | { 56 | stop(); 57 | } 58 | 59 | // Copy-operations explicit forbidden 60 | 61 | AOThread(const AOThread&) = delete; 62 | AOThread& operator = (const AOThread&) = delete; 63 | 64 | /** 65 | * Enqueue the task 66 | * 67 | * @note If the job should have an additional arguments, 68 | * use std::bind - to bind the task with the arguments. 69 | * 70 | * @param job The task to be enqueued 71 | * @return The future, for waiting on result, if any 72 | */ 73 | auto enqueue(utils::aot::job_t&& job) noexcept 74 | { 75 | return m_pJobQueue->enqueue(std::move(job));//thread-safe task queue 76 | } 77 | 78 | private: 79 | 80 | void start(std::string name 81 | , utils::thread::ThreadWrapper::schedule_t policy 82 | , int priority) noexcept 83 | { 84 | if (m_pJobThread) 85 | { 86 | (void)m_pJobThread->setName(name); 87 | (void)m_pJobThread->setPriority(policy, priority); 88 | } 89 | } 90 | 91 | void stop() 92 | { 93 | m_pJobQueue->stop();//stop dequeuing: signal thread exit 94 | 95 | if (m_pJobThread) 96 | { 97 | m_pJobThread.reset(nullptr);//wait on jobs thread to join 98 | } 99 | } 100 | 101 | /** 102 | * Serialized all tasks to be executed sequentially, 103 | * within the single background thread 104 | */ 105 | void threadFunc() noexcept; 106 | 107 | 108 | private: 109 | 110 | std::unique_ptr m_pJobQueue = nullptr; 111 | utils::thread::thread_ptr_t m_pJobThread = nullptr; 112 | }; 113 | 114 | template 115 | void AOThread::threadFunc() noexcept 116 | { 117 | using namespace std; 118 | 119 | for(;;) 120 | { 121 | 122 | // Suspend thread, until the queue is empty or exit is not signaled 123 | auto job = m_pJobQueue->dequeue(); 124 | 125 | if (!job) //exit signaled 126 | { 127 | break; 128 | } 129 | 130 | 131 | try 132 | { 133 | (*job)(); 134 | } 135 | catch(const std::bad_function_call& e) 136 | { 137 | //todo: add logging policy 138 | break; 139 | } 140 | 141 | }//for(;;) 142 | } 143 | 144 | 145 | } /* namespace utils::aot */ 146 | 147 | #endif /* AOT_AOTHREAD_H_ */ 148 | -------------------------------------------------------------------------------- /src/AOT/JobQueue.h: -------------------------------------------------------------------------------- 1 | /* 2 | * JobQueue.h 3 | * 4 | * Created on: Dec 29, 2020 5 | * Author: Damir Ljubic 6 | */ 7 | 8 | #ifndef AOT_JOBQUEUE_H_ 9 | #define AOT_JOBQUEUE_H_ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace utils::aot 19 | { 20 | 21 | template 22 | using job_t = std::packaged_task;//universal job signature 23 | 24 | template 25 | using job_queue_t = std::queue>;//universal queue of jobs-callable objects 26 | 27 | /** 28 | * Specialization of the std::queue. 29 | * 30 | * The queue is designed to hold the jobs - callable objects (tasks). 31 | * Helper class for implementing the AOT (Active Object Thread) design pattern. 32 | * The queue is implemented in a thread-safe manner. 33 | * 34 | * @see AOThread 35 | * 36 | * @tparam R Callable object return type 37 | */ 38 | 39 | template 40 | class JobQueue final: public job_queue_t 41 | { 42 | public: 43 | 44 | using value_type = job_t; 45 | 46 | using super = job_queue_t; 47 | using super::super;//using base class (std::queue) c-tors 48 | 49 | ~JobQueue() = default; //user-defined destructor will prevent generating default - memberwise move operations 50 | 51 | // Copy operations discarded 52 | 53 | JobQueue(const JobQueue& ) = delete; 54 | JobQueue& operator = (const JobQueue&) = delete; 55 | 56 | 57 | 58 | /** 59 | * Enqueue the task as rvalue reference, since 60 | * std::packaged_task is movable only type. 61 | * 62 | * @note If the task requires additional arguments (Args...), 63 | * use std::bind - to bind the callable object with arguments. 64 | * 65 | * 66 | * @param job The callable object to enqueue 67 | * @return The result of the task, if any (std::future). 68 | * The return value is in that case used only for synchronization 69 | */ 70 | template 71 | std::future enqueue(job_t&& job, Args&&...args) noexcept 72 | { 73 | std::future result; 74 | { 75 | std::lock_guard lock {m_mutex}; 76 | 77 | result = job.get_future(); 78 | value_type task { std::bind(std::move(job), std::forward(args)...)}; 79 | this->push(std::move(task)); 80 | } 81 | 82 | m_condition.notify_one(); 83 | 84 | return result; 85 | } 86 | 87 | std::future enqueue(value_type&& job) noexcept 88 | { 89 | std::future result; 90 | { 91 | std::lock_guard lock {m_mutex}; 92 | 93 | result = job.get_future(); 94 | this->push(std::move(job)); 95 | } 96 | 97 | m_condition.notify_one(); 98 | 99 | return result; 100 | } 101 | 102 | /** 103 | * Dequeue the job from the queue. 104 | * This is concurrent operation to enqueue - it will block 105 | * until either the queue is empty, or stop dequeuing is not signaled 106 | * 107 | * @return The task to be executed, or none-value in case that stop is signaled 108 | * 109 | */ 110 | std::optional dequeue() noexcept 111 | { 112 | std::unique_lock lock {m_mutex}; 113 | 114 | m_condition.wait(lock, [this]{return !this->empty() || m_stopDequeuing;}); 115 | 116 | if (m_stopDequeuing) return {}; 117 | 118 | auto job = std::move(this->front()); 119 | this->pop(); 120 | 121 | return job; 122 | } 123 | 124 | /** 125 | * Force stopping dequeuing 126 | */ 127 | void stop() noexcept 128 | { 129 | m_stopDequeuing = true; 130 | 131 | m_condition.notify_one(); 132 | } 133 | 134 | 135 | private: 136 | 137 | bool m_stopDequeuing = false; 138 | 139 | std::mutex m_mutex {};//neither copyable, nor movable 140 | std::condition_variable m_condition {};//neither copyable, nor movable 141 | 142 | }; 143 | 144 | } 145 | 146 | 147 | #endif /* AOT_JOBQUEUE_H_ */ 148 | -------------------------------------------------------------------------------- /src/AOT/java/AOThread.java: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: Damir Ljubic 3 | * @email: damirlj@yahoo.com 4 | *

All rights reserved! 5 | */ 6 | package ; 7 | 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | import java.util.Optional; 11 | import java.util.concurrent.Callable; 12 | import java.util.concurrent.CompletableFuture; 13 | import java.util.concurrent.ExecutorService; 14 | import java.util.concurrent.Executors; 15 | import java.util.concurrent.TimeUnit; 16 | 17 | public final class AOThread { 18 | 19 | // Single-thread execution context 20 | private final ExecutorService executionContext = Executors.newSingleThreadExecutor(); 21 | private final Looper looper = new Looper(); 22 | 23 | /** Start the AOT: Attach the Looper with the execution context */ 24 | public void start() { 25 | executionContext.submit(looper.getLooper()); 26 | } 27 | 28 | @FunctionalInterface 29 | private interface IStopThread { 30 | void stop(@NotNull ExecutorService executor); 31 | } 32 | 33 | /** Stop thread which drains the message queue */ 34 | private void stop(@NotNull IStopThread callback) throws InterruptedException { 35 | looper.stop(); // set the exit flag 36 | callback.stop(executionContext); 37 | } 38 | 39 | /** 40 | * Try to stop the background AOT gracefully, by waiting on the last task completion 41 | * 42 | * @param timeout The timeout to wait for 43 | * @param unit The time unit for the timeout to be expressed with 44 | */ 45 | public void stop(long timeout, TimeUnit unit) throws InterruptedException { 46 | stop( 47 | executor -> { 48 | executor.shutdown(); // initiate shutdown 49 | try { 50 | if (!executor.awaitTermination(timeout, unit)) { 51 | executor.shutdownNow(); 52 | } 53 | } catch (InterruptedException e) { 54 | executor.shutdownNow(); 55 | Thread.currentThread().interrupt(); 56 | } 57 | }); 58 | } 59 | 60 | /** 61 | * Stop the AOT background thread immediately, without waiting on the task completion 62 | * 63 | * @note There is no guarantees that the last running task will be successfully interrupted 64 | */ 65 | public void stopNow() throws InterruptedException { 66 | stop(ExecutorService::shutdownNow); 67 | } 68 | 69 | private void submit(@NotNull Runnable task) throws InterruptedException { 70 | looper.submit(task); 71 | } 72 | 73 | /** 74 | * Submit the task to the Looper (job-queue).
75 | * The client will be synchronized on the result of the execution. 76 | * 77 | * @param job The task to be enqueued 78 | * @return The future object - for synchronizing on the execution outcome 79 | * @param The result type 80 | */ 81 | @NotNull 82 | public Optional> enqueueWithResult(@NotNull Callable job) { 83 | CompletableFuture f = new CompletableFuture<>(); 84 | 85 | // Wrap the callable into parameterless runnable task 86 | Runnable task = 87 | () -> { 88 | try { 89 | R result = job.call(); 90 | f.complete(result); // signal the result to client 91 | } catch (Exception e) { 92 | f.completeExceptionally(e); // or signal the exception to client 93 | } 94 | }; 95 | 96 | try { 97 | submit(task); 98 | } catch (InterruptedException e) { 99 | Thread.currentThread().interrupt(); 100 | return Optional.empty(); 101 | } 102 | 103 | return Optional.of(f); 104 | } 105 | 106 | @FunctionalInterface 107 | public interface IJob { 108 | void execute() throws Exception; 109 | } 110 | 111 | /** 112 | * Submit the job to the Looper.
113 | * Fire-and-forget: client will not wait on the outcome of execution, if any. 114 | * 115 | * @param job The task to be executed 116 | */ 117 | public void enqueue(@NotNull IJob job) { 118 | Runnable task = ()->{ 119 | try { 120 | job.execute(); 121 | } catch(Exception e) { 122 | e.printStackTrace(); // your own logging 123 | } 124 | }; 125 | 126 | try { 127 | submit(task); 128 | } catch (InterruptedException e) { 129 | Thread.currentThread().interrupt(); 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/AOT/java/Looper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: Damir Ljubic 3 | * @email: damirlj@yahoo.com 4 | *

All rights reserved! 5 | */ 6 | package ; 7 | 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | import java.util.concurrent.BlockingQueue; 11 | import java.util.concurrent.LinkedBlockingQueue; 12 | 13 | public final class Looper { 14 | // Thread-safe tasks queue 15 | private final BlockingQueue queue = new LinkedBlockingQueue<>(); 16 | 17 | private static final Runnable STOPPING_TASK = () -> {}; 18 | 19 | /** For stopping the Looper */ 20 | public void stop() throws InterruptedException { 21 | queue.put(STOPPING_TASK); // to unblock the queue and terminate gracefully 22 | } 23 | 24 | /** Looper drains the queue, and executes the tasks in order of reception (FIFO) */ 25 | private final Runnable looper = 26 | () -> { 27 | for (; ;) { 28 | try { 29 | Runnable task = queue.take(); // blocking call 30 | if (task == STOPPING_TASK) break; 31 | task.run(); 32 | } catch (InterruptedException e) { 33 | Thread.currentThread().interrupt(); // set the interrupt flag to "true" 34 | } 35 | } 36 | System.out.println(" Looper"); // your own logging 37 | }; 38 | 39 | @NotNull 40 | public Runnable getLooper() { 41 | return looper; 42 | } 43 | 44 | public void submit(@NotNull Runnable task) throws InterruptedException { 45 | queue.put(task); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/AOT/java/TestAOT.java: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: Damir Ljubic 3 | * @email: damirlj@yahoo.com 4 | *

All rights reserved! 5 | */ 6 | package ; 7 | 8 | import androidx.annotation.NonNull; 9 | 10 | import org.jetbrains.annotations.NotNull; 11 | import org.junit.Test; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | import java.util.Optional; 16 | import java.util.concurrent.Callable; 17 | import java.util.concurrent.CompletableFuture; 18 | import java.util.concurrent.ExecutionException; 19 | import java.util.concurrent.Future; 20 | import java.util.concurrent.TimeUnit; 21 | import java.util.function.Consumer; 22 | 23 | public class TestAOT { 24 | 25 | @NonNull 26 | private List> submitTasks_withResults( 27 | @NotNull List> callables, @NotNull AOThread aot) { 28 | List> results = new ArrayList<>(callables.size()); 29 | for (Callable callable : callables) { 30 | aot.enqueueWithResult(callable).map(results::add); 31 | } 32 | return results; 33 | } 34 | 35 | private void fetchResultOrException( 36 | @NonNull Future result, @NotNull Consumer callback) { 37 | try { 38 | callback.accept(result.get()); // blocking call: wait on result being set 39 | } catch (ExecutionException | InterruptedException e) { // or exception being thrown 40 | e.printStackTrace(); 41 | } 42 | } 43 | 44 | private void syncOnResults(@NotNull List> results, @NotNull Consumer callback) { 45 | for (Future result : results) { 46 | fetchResultOrException(result, callback); 47 | } 48 | } 49 | 50 | @NotNull 51 | private Callable createCallable(long timeout) { 52 | return () -> { 53 | Thread.sleep(timeout); // simulate some work 54 | return String.format( // it's always hosted by the same thread - execution context 55 | "(thread: %s): Sleeping for %d[ms]", Thread.currentThread().getName(), timeout); 56 | }; 57 | } 58 | 59 | @Test 60 | public void test_AOTWaitingOnResults() throws InterruptedException { 61 | final AOThread aot = new AOThread(); 62 | aot.start(); 63 | 64 | Thread client = 65 | new Thread( 66 | () -> { 67 | final List> callables = 68 | List.of(createCallable(200L), createCallable(100L), createCallable(300L)); 69 | final List> results = submitTasks_withResults(callables, aot); 70 | // Client: synchronized on the result 71 | syncOnResults(results, System.out::println); 72 | }, 73 | "t_client"); 74 | 75 | client.start(); 76 | client.join(); 77 | 78 | aot.stopNow(); 79 | } 80 | 81 | @NotNull 82 | private AOThread.IJob createJob(long timeout) { 83 | return () -> { 84 | Thread.sleep(timeout); // simulate some work 85 | System.out.printf( 86 | "Executing within thread: \"%s\", after %d[ms]\n", 87 | Thread.currentThread().getName(), timeout); 88 | }; 89 | } 90 | 91 | @Test 92 | public void test_AOTWithoutWaitingOnResults() throws InterruptedException { 93 | 94 | final AOThread aot = new AOThread(); 95 | aot.start(); 96 | 97 | Thread client = 98 | new Thread( 99 | () -> { 100 | final List callables = List.of(createJob(200L), createJob(100L)); 101 | for (AOThread.IJob callable : callables) { 102 | aot.enqueue(callable); 103 | try { 104 | Thread.sleep(1000); 105 | } catch (InterruptedException e) { 106 | e.printStackTrace(); 107 | } 108 | } 109 | }, 110 | "t_client"); 111 | 112 | client.start(); 113 | client.join(); 114 | 115 | aot.stop(1, TimeUnit.SECONDS); 116 | } 117 | 118 | @Test 119 | public void test_bothOfThem() throws InterruptedException { 120 | final AOThread aot = new AOThread(); 121 | aot.start(); 122 | 123 | Thread client = 124 | new Thread( 125 | () -> { 126 | // Submit, without waiting 127 | aot.enqueue(createJob(100L)); 128 | aot.enqueue(() -> {}); // this will not brake the looper 129 | 130 | // Wait on result, or exception being set 131 | Optional> opFut = 132 | aot.enqueueWithResult(createCallable(200L)); 133 | opFut.ifPresent( 134 | fut -> 135 | fetchResultOrException( 136 | fut, 137 | res -> 138 | System.out.printf( 139 | "(Thread: \"%s\") Receiving result: %s\n", 140 | Thread.currentThread().getName(), res))); 141 | }, 142 | "t_client"); 143 | 144 | client.start(); 145 | client.join(); 146 | 147 | aot.stopNow(); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/Event/Event.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // damirlj@yahoo.com 3 | // Copyright (c) 2023. All rights reserved! 4 | // 5 | 6 | #include "Event.h" 7 | 8 | Event::Event(bool autoReset) noexcept 9 | : m_autoReset(autoReset) 10 | {} 11 | 12 | Event::~Event() = default; 13 | 14 | namespace 15 | { 16 | inline void updateWaitingThreads(Event::waiting_threads_t& waitingThreads) 17 | { 18 | waitingThreads.erase( 19 | std::remove(waitingThreads.begin(), waitingThreads.end(), std::this_thread::get_id()), 20 | waitingThreads.end()); 21 | } 22 | } // namespace 23 | 24 | Event::event_wait_t Event::wait_for(std::chrono::milliseconds timeout) 25 | { 26 | std::unique_lock lock{m_lock}; 27 | 28 | event_wait_t outcome = event_wait_t::signaled; 29 | 30 | if (not m_predicate) 31 | { 32 | m_waitingThreads.push_back(std::this_thread::get_id()); 33 | 34 | const bool signaled = m_event.wait_for(lock, timeout, [this] { return m_predicate; }); 35 | outcome = signaled ? event_wait_t::signaled : event_wait_t::timeout; 36 | 37 | updateWaitingThreads(m_waitingThreads); 38 | } 39 | 40 | // Auto reset 41 | if (m_autoReset && m_waitingThreads.empty()) m_predicate = false; 42 | 43 | return outcome; 44 | } 45 | 46 | void Event::wait() 47 | { 48 | std::unique_lock lock{m_lock}; 49 | 50 | if (!m_predicate) 51 | { 52 | m_waitingThreads.push_back(std::this_thread::get_id()); 53 | 54 | m_event.wait(lock, [this] { return m_predicate; }); 55 | 56 | updateWaitingThreads(m_waitingThreads); 57 | } 58 | 59 | // Auto reset 60 | if (m_autoReset && m_waitingThreads.empty()) m_predicate = false; 61 | } 62 | 63 | void Event::notify() 64 | { 65 | setEvent(&std::condition_variable::notify_one); 66 | } 67 | 68 | void Event::broadcast() 69 | { 70 | setEvent(&std::condition_variable::notify_all); 71 | } 72 | 73 | [[maybe_unused]] void Event::reset() 74 | { 75 | std::lock_guard lock{m_lock}; 76 | m_predicate = false; 77 | } -------------------------------------------------------------------------------- /src/Event/Event.h: -------------------------------------------------------------------------------- 1 | // 2 | // damirlj@yahoo.com 3 | // Copyright (c) 2023. All rights reserved! 4 | // 5 | 6 | #ifndef EVENT_H 7 | #define EVENT_H 8 | 9 | 10 | // std library 11 | #include 12 | #include 13 | #include 14 | #include // std::invoke 15 | #include 16 | #include 17 | 18 | 19 | namespace utils 20 | { 21 | 22 | /** 23 | * Implementation of the event synchronization primitive. 24 | * Will synchronize threads, one producer: signaling the event to single, or 25 | * all consumer threads waiting on the same condition 26 | */ 27 | class Event final 28 | { 29 | public: 30 | 31 | using event_wait_t = enum class EEvent : std::uint8_t { timeout = 0, signaled }; 32 | using waiting_threads_t = std::vector; 33 | 34 | /** 35 | * C-tor 36 | * 37 | * @param autoReset In case that is set to true, will reset the event after being signaled 38 | * at consumer point, so that it can wait - block on the same event on the next recall 39 | */ 40 | explicit Event(bool autoReset) noexcept; 41 | ~Event(); 42 | 43 | // Copy functions forbidden 44 | 45 | Event(const Event&) = delete; 46 | Event& operator=(const Event&) = delete; 47 | 48 | // Move operations forbidden 49 | 50 | Event(Event&&) = delete; 51 | Event& operator=(Event&&) = delete; 52 | 53 | /** 54 | * Wait on the event being signaled, or timeout expired 55 | * 56 | * @param timeout Timeout in milliseconds to wait 57 | * @return Indication of the operation outcome {@link Event#event_wait_t} 58 | */ 59 | event_wait_t wait_for(std::chrono::milliseconds timeout); 60 | 61 | /** 62 | * Wait infinitely on event being signaled 63 | */ 64 | void wait(); 65 | 66 | /** 67 | * Notify - wake up the single thread 68 | */ 69 | void notify(); 70 | 71 | /** 72 | * Notify - wake up the all waiting threads 73 | * 74 | * @note If the event is auto reset - this will notify only the first 75 | * woken thread 76 | */ 77 | void broadcast(); 78 | 79 | /** 80 | * Manually reset event - in case of the auto reset is false. 81 | */ 82 | [[maybe_unused]] void reset(); 83 | 84 | private: 85 | 86 | using notify_f = void (std::condition_variable::*)(void); 87 | void setEvent(notify_f notifier) 88 | { 89 | std::lock_guard lock{m_lock}; 90 | m_predicate = true; 91 | 92 | // There is at least one consumer-thread - waiting on the event to be signaled 93 | if (not m_waitingThreads.empty()) 94 | { 95 | std::invoke(notifier, m_event); 96 | } 97 | } 98 | 99 | private: 100 | std::condition_variable m_event; // not copyable nor movable 101 | std::mutex m_lock; // not copyable nor movable 102 | 103 | const bool m_autoReset; 104 | bool m_predicate = false; 105 | 106 | waiting_threads_t m_waitingThreads; 107 | 108 | }; 109 | } // namespace utils 110 | 111 | #endif // EVENT_H 112 | -------------------------------------------------------------------------------- /src/Event/Event20.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EVENT20_HPP 2 | #define EVENT20_HPP 3 | 4 | // std library 5 | #include 6 | #include 7 | #include 8 | 9 | // Application 10 | #include "Monitor.hpp" 11 | 12 | namespace utils 13 | { 14 | class Event final 15 | { 16 | using threads_ids_type = std::vector; 17 | 18 | public: 19 | explicit Event(bool autoReset) noexcept 20 | : autoReset_{autoReset} 21 | {} 22 | 23 | ~Event() = default; 24 | 25 | // Copy functions forbidden 26 | 27 | Event(const Event&) = delete; 28 | Event& operator=(const Event&) = delete; 29 | 30 | // Move operations forbidden 31 | 32 | Event(Event&&) = delete; 33 | Event& operator=(Event&&) = delete; 34 | 35 | inline void wait() noexcept 36 | { 37 | { 38 | auto lock = sync_.getLock(); 39 | if (flag_) return; // premature signalization 40 | waitingThreads_.push_back(std::this_thread::get_id()); 41 | } 42 | auto lock = sync_.wait([this] { return flag_; }); 43 | update_waiting_threads(waitingThreads_); 44 | if (autoReset_ && waitingThreads_.empty()) flag_ = false; 45 | } 46 | 47 | inline bool wait_for(std::chrono::milliseconds timeout) noexcept 48 | { 49 | { 50 | auto lock = sync_.getLock(); 51 | if (flag_) return true; // premature signalization 52 | waitingThreads_.push_back(std::this_thread::get_id()); 53 | } 54 | auto [result, lock] = sync_.wait_for(timeout, [this] { return flag_; }); 55 | update_waiting_threads(waitingThreads_); 56 | if (autoReset_ && waitingThreads_.empty()) 57 | flag_ = false; // for broadcast to work with auto reset flag set to true 58 | 59 | return result; 60 | } 61 | 62 | inline void signal() noexcept 63 | { 64 | sync_.notify_one([this] { flag_ = true; }); 65 | } 66 | 67 | inline void broadcast() noexcept 68 | { 69 | sync_.notify_all([this] { flag_ = true; }); 70 | } 71 | 72 | private: 73 | inline void update_waiting_threads(threads_ids_type& waitingThreads) noexcept 74 | { 75 | waitingThreads_.erase( 76 | std::remove(waitingThreads.begin(), waitingThreads.end(), std::this_thread::get_id()), 77 | waitingThreads.end()); 78 | } 79 | 80 | private: 81 | Monitor<> sync_{}; 82 | 83 | const bool autoReset_; 84 | bool flag_{false}; 85 | threads_ids_type waitingThreads_; 86 | }; 87 | } // namespace utils 88 | #endif // EVENT20_HPP 89 | -------------------------------------------------------------------------------- /src/Event/Event_with_future_and_promises.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/src/Event/Event_with_future_and_promises.pdf -------------------------------------------------------------------------------- /src/Event/Monitor.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | /** 9 | * @brief Implementation of the Monitor Object design pattern. 10 | * This will be used to monitor any client's method that requires inter-thread 11 | * synchronization - by accessing/modifying the client's internal state from 12 | * the different thread contexts. 13 | */ 14 | template 15 | class Monitor final 16 | { 17 | using lock_t = Lock; 18 | using condition_t = Condition; 19 | 20 | public: 21 | 22 | [[nodiscard]] std::unique_lock getLock() const 23 | { 24 | return std::unique_lock {lock_}; 25 | } 26 | 27 | // Inspired by: https://www.modernescpp.com/index.php/thread-safe-queue-two-serious-errors/ 28 | template 29 | [[nodiscard]] auto getLockAndNotifyWhenDone() const 30 | { 31 | struct UnLockWithNotify final 32 | { 33 | explicit UnLockWithNotify(const Monitor& monitor) noexcept: monitor_{monitor} 34 | {} 35 | // Mutex interface 36 | inline void lock() { monitor_.lock_.lock();} 37 | inline void unlock() 38 | { 39 | if constexpr (broadcast) monitor_.condition_.notify_all(); 40 | else monitor_.condition_.notify_one(); 41 | monitor_.lock_.unlock(); 42 | } 43 | private: 44 | const Monitor& monitor_; 45 | 46 | }; 47 | 48 | return UnLockWithNotify{*this}; 49 | } 50 | 51 | /** 52 | * Wait infinite on the predicate to be signaled, and return 53 | * afterwards the locking object to the client 54 | */ 55 | template 56 | requires std::is_same_v> 57 | [[nodiscard]] auto wait( 58 | Predicate&& predicate, 59 | Args&&... args) const 60 | { 61 | std::unique_lock lock{lock_}; 62 | 63 | condition_.wait( 64 | lock, 65 | [ p = std::forward(predicate), ...args = std::forward(args)]() mutable 66 | { 67 | return std::invoke(p, std::forward(args)...); 68 | }); 69 | 70 | return lock; 71 | } 72 | 73 | /** 74 | * 75 | * Wait on the predicate to be signaled, or timeout being expired: whatever comes first, 76 | * and return the tuple containing the result of the wait operation and 77 | * the locking object itself 78 | */ 79 | template 80 | requires std::is_same_v> 81 | [[nodiscard]] std::tuple> 82 | wait_for(std::chrono::milliseconds timeout, 83 | Predicate&& predicate, 84 | Args&&... args) const 85 | { 86 | std::unique_lock lock{lock_}; 87 | 88 | const bool result = condition_.wait_for( 89 | lock, 90 | timeout, 91 | [ p = std::forward(predicate), ...args = std::forward(args)]() mutable 92 | { 93 | return std::invoke(p, std::forward(args)...); 94 | }); 95 | 96 | return {result, std::move(lock)}; 97 | } 98 | 99 | template 100 | auto notify_one(Func&& func, Args&&...args) const 101 | { 102 | return notify(std::forward(func), std::forward(args)...); 103 | } 104 | 105 | template 106 | auto notify_all(Func&& func, Args&&...args) const 107 | { 108 | return notify(std::forward(func), std::forward(args)...); 109 | } 110 | 111 | private: 112 | 113 | /** 114 | * Execute the given function (callable) under the locking 115 | * protection, and notify completion. 116 | * Returns the result of the function call - if any 117 | */ 118 | template 119 | auto notify(Func&& func, Args&&...args) const 120 | { 121 | auto lockWithNotify = getLockAndNotifyWhenDone(); 122 | std::lock_guard lock {lockWithNotify}; 123 | 124 | if constexpr (not std::is_void_v>) 125 | { 126 | return std::invoke(std::forward(func), std::forward(args)...); 127 | } 128 | 129 | std::invoke(std::forward(func), std::forward(args)...); 130 | } 131 | 132 | 133 | private: 134 | mutable lock_t lock_; 135 | mutable condition_t condition_; 136 | }; 137 | -------------------------------------------------------------------------------- /src/Event/java/AutoResetEvent.java: -------------------------------------------------------------------------------- 1 | package ; 2 | 3 | public final class AutoResetEvent extends Event { 4 | public AutoResetEvent() { 5 | super(true); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Event/java/Event.java: -------------------------------------------------------------------------------- 1 | package ; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Date; 7 | import java.util.List; 8 | import java.util.concurrent.Callable; 9 | import java.util.concurrent.TimeUnit; 10 | import java.util.concurrent.locks.Condition; 11 | import java.util.concurrent.locks.Lock; 12 | import java.util.concurrent.locks.ReentrantLock; 13 | 14 | /** Helper class for event synchronization */ 15 | public class Event { 16 | private final Lock mLock; // mutex 17 | private final Condition mCondition; // condition variable 18 | private volatile boolean mEventSignaled = false; // predicate to prevent spurious wake ups 19 | 20 | private static final class ThreadList { 21 | private final List threads = new ArrayList<>(); 22 | 23 | public void add() { 24 | threads.add(Thread.currentThread().getId()); 25 | } 26 | 27 | public void remove() { 28 | threads.remove(Thread.currentThread().getId()); 29 | } 30 | 31 | public boolean empty() { 32 | return threads.isEmpty(); 33 | } 34 | } 35 | 36 | private final ThreadList waitingThreads = new ThreadList(); 37 | private final boolean autoReset; 38 | 39 | public Event(boolean autoReset) { 40 | mLock = new ReentrantLock(); 41 | mCondition = mLock.newCondition(); 42 | 43 | this.autoReset = autoReset; 44 | } 45 | 46 | @FunctionalInterface 47 | private interface ILockCallback { 48 | void apply() throws Exception; // signature relevant: methods of condition variable may throw 49 | } 50 | 51 | private void lockAndThen(@NonNull ILockCallback callback) { 52 | mLock.lock(); 53 | try { 54 | callback.apply(); // Call condition variable related methods, without handling exceptions 55 | } catch (Exception e) { 56 | e.printStackTrace(); 57 | } finally { 58 | mLock.unlock(); 59 | } 60 | } 61 | 62 | /** 63 | * Signal the event to wake up a single thread, among the all threads which are waiting on the 64 | * same event 65 | */ 66 | public void signal() { 67 | ILockCallback callback = 68 | () -> { 69 | mEventSignaled = true; 70 | if (!waitingThreads.empty()) mCondition.signal(); 71 | }; 72 | 73 | lockAndThen(callback); 74 | } 75 | 76 | /** Signal the event to the all threads that are waiting on the same event notification */ 77 | public void signalAll() { 78 | ILockCallback callback = 79 | () -> { 80 | mEventSignaled = true; 81 | if (!waitingThreads.empty()) mCondition.signalAll(); 82 | }; 83 | 84 | lockAndThen(callback); 85 | } 86 | 87 | private boolean waitOnCondition(Callable waitStrategy) throws Exception { 88 | waitingThreads.add(); 89 | try { 90 | while (!mEventSignaled) { 91 | if (!waitStrategy.call()) { 92 | return false; // Timeout or deadline reached 93 | } 94 | } 95 | return true; 96 | } finally { 97 | waitingThreads.remove(); 98 | if (autoReset && waitingThreads.empty()) { 99 | mEventSignaled = false; 100 | } 101 | } 102 | } 103 | 104 | /** 105 | * Wait on event signalization, or until thread which waits on event notification is interrupted 106 | */ 107 | public void waitEvent() { 108 | lockAndThen( 109 | () -> 110 | waitOnCondition( 111 | () -> { 112 | mCondition.await(); 113 | return true; 114 | })); 115 | } 116 | 117 | /** 118 | * Wait on event being signaled, waiting thread interrupted, or timeout is expired 119 | * 120 | * @param time The relative time interval to be waited for 121 | * @param unit Unit of time to wait for (nanoseconds, microseconds, milliseconds, etc.) 122 | * @return Indication of the wait outcome: false - timeout expired before event is notified. 123 | * Otherwise - true 124 | */ 125 | public boolean waitEventFor(final long time, final TimeUnit unit) { 126 | final boolean[] signaled = {false}; 127 | lockAndThen(() -> signaled[0] = waitOnCondition(() -> mCondition.await(time, unit))); 128 | return signaled[0]; 129 | } 130 | 131 | /** 132 | * Wait on the event being signaled, waiting thread interrupted, or the deadline reached 133 | * 134 | * @param deadline The absolute time to be waited on 135 | * @return Indication of the wait outcome: false - timeout expired before event is notified. 136 | * Otherwise - true 137 | */ 138 | public boolean waitEventUntil(final Date deadline) { 139 | final boolean[] signaled = {false}; 140 | lockAndThen(() -> signaled[0] = waitOnCondition(() -> mCondition.awaitUntil(deadline))); 141 | return signaled[0]; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/Event/java/TestEvent.java: -------------------------------------------------------------------------------- 1 | package ; 2 | 3 | import static junit.framework.Assert.assertTrue; 4 | 5 | import java.util.List; 6 | import java.util.Locale; 7 | import java.util.concurrent.TimeUnit; 8 | 9 | import org.jetbrains.annotations.NotNull; 10 | import org.junit.Test; 11 | 12 | public class TestEvent { 13 | 14 | @FunctionalInterface 15 | private interface ISignal { 16 | void invoke(TEvent e); 17 | } 18 | 19 | @NotNull 20 | private Thread signalThread( 21 | ISignal callback, final @NotNull AutoResetEvent event, final long timeout) { 22 | return new Thread( 23 | () -> { 24 | try { 25 | Thread.sleep(timeout); 26 | callback.invoke(event); 27 | } catch (InterruptedException e) { 28 | e.printStackTrace(); 29 | } 30 | }); 31 | } 32 | 33 | private Runnable waitTaskSignaled(@NotNull Event event, final long signaledAfterMs) { 34 | return () -> { 35 | long start = System.nanoTime(); 36 | event.waitEvent(); 37 | long elapsed = (System.nanoTime() - start) / 1_000_000; 38 | System.out.printf( 39 | Locale.ENGLISH, 40 | " (%s) received after: %d[ms]\n", 41 | Thread.currentThread().getName(), 42 | elapsed); 43 | assertTrue(elapsed >= signaledAfterMs); 44 | }; 45 | } 46 | 47 | @Test 48 | public void testWaitSignaled() throws InterruptedException { 49 | final AutoResetEvent event = new AutoResetEvent(); 50 | final long timeout = 1000L; 51 | 52 | Thread waiter = new Thread(waitTaskSignaled(event, timeout)); 53 | 54 | Thread signaler = signalThread(Event::signal, event, timeout); 55 | signaler.start(); 56 | 57 | waiter.start(); 58 | waiter.join(); 59 | } 60 | 61 | @Test 62 | public void testWaitSignaled_multipleThreads() throws InterruptedException { 63 | final AutoResetEvent event = new AutoResetEvent(); 64 | final long timeout = 1000L; 65 | 66 | Thread signaler = signalThread(Event::signalAll, event, timeout); 67 | signaler.start(); 68 | 69 | List threads = 70 | List.of( 71 | new Thread(waitTaskSignaled(event, timeout), "t_waiter#1"), 72 | new Thread(waitTaskSignaled(event, timeout), "t_waiter#2"), 73 | new Thread(waitTaskSignaled(event, timeout), "t_waiter#3")); 74 | for (Thread thread : threads) { 75 | thread.start(); 76 | } 77 | for (Thread thread : threads) { 78 | thread.join(); 79 | } 80 | } 81 | 82 | private Thread waitForTask(@NotNull Event event, final long timeout) { 83 | return new Thread( 84 | () -> { 85 | long start = System.nanoTime(); 86 | boolean signaled = event.waitEventFor(timeout, TimeUnit.MILLISECONDS); 87 | long elapsed = (System.nanoTime() - start) / 1_000_000; 88 | if (signaled) { 89 | System.out.printf(Locale.ENGLISH, " received after: %d[ms]\n", elapsed); 90 | assertTrue(elapsed < timeout); 91 | } else { 92 | System.out.printf(Locale.ENGLISH, " timeout expired: %d[ms]\n", elapsed); 93 | assertTrue(elapsed >= timeout); 94 | } 95 | }); 96 | } 97 | 98 | @Test 99 | public void testWaitForSignaled() throws InterruptedException { 100 | final AutoResetEvent event = new AutoResetEvent(); 101 | final long timeout = 1000L; 102 | 103 | Thread waiter = waitForTask(event, timeout * 2); 104 | 105 | Thread signaler = signalThread(Event::signal, event, timeout); 106 | signaler.start(); 107 | 108 | waiter.start(); 109 | waiter.join(); 110 | } 111 | 112 | @Test 113 | public void testWaitForSignaled_signal_first() throws InterruptedException { 114 | final AutoResetEvent event = new AutoResetEvent(); 115 | final long timeout = 1000L; 116 | 117 | event.signal(); 118 | Thread.sleep(100); 119 | 120 | Thread waiter = waitForTask(event, timeout); 121 | 122 | waiter.start(); 123 | waiter.join(); 124 | } 125 | 126 | @Test 127 | public void testWaitForSignaled_not_signaled() throws InterruptedException { 128 | final AutoResetEvent event = new AutoResetEvent(); 129 | final long timeout = 1000L; 130 | 131 | Thread waiter = waitForTask(event, timeout); 132 | 133 | waiter.start(); 134 | waiter.join(); 135 | } 136 | 137 | @Test 138 | public void testWaitFor_missedSignal() throws InterruptedException { 139 | final AutoResetEvent event = new AutoResetEvent(); 140 | final long timeout = 1000L; 141 | 142 | Thread waiter = waitForTask(event, timeout / 2); 143 | 144 | waiter.start(); 145 | 146 | Thread signaler = signalThread(Event::signal, event, timeout); 147 | signaler.start(); 148 | signaler.join(); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/FileStreams/BinaryInputFileStream.h: -------------------------------------------------------------------------------- 1 | // 2 | // damirlj@yahoo.com 3 | // Copyright (c) 2021. All rights reserved! 4 | // 5 | 6 | #ifndef INCLUDE_COMMON_FILE_BINARYINPUTFILESTREAM_HXX_ 7 | #define INCLUDE_COMMON_FILE_BINARYINPUTFILESTREAM_HXX_ 8 | 9 | #include "FileStream.h" 10 | 11 | namespace utils::file 12 | { 13 | // Strong (named) type - type alias 14 | using BinaryInputFileStream = InputFileStream; // std::byte 15 | } 16 | 17 | 18 | #endif /* INCLUDE_COMMON_FILE_BINARYINPUTFILESTREAM_HXX_ */ 19 | -------------------------------------------------------------------------------- /src/FileStreams/BinaryOutputFileStream.h: -------------------------------------------------------------------------------- 1 | // 2 | // damirlj@yahoo.com 3 | // Copyright (c) 2021. All rights reserved! 4 | // 5 | 6 | #ifndef FILE_BINARYOUTPUTSTREAM_H_ 7 | #define FILE_BINARYOUTPUTSTREAM_H_ 8 | 9 | 10 | #include "FileStream.h" 11 | 12 | namespace utils::file 13 | { 14 | // Strong (named) type - type alias 15 | using BinaryOutputFileStream = OutputFileStream; //std::byte 16 | } 17 | 18 | #endif /* FILE_BINARYOUTPUTSTREAM_H_ */ 19 | -------------------------------------------------------------------------------- /src/FileStreams/CharInputFileStream.h: -------------------------------------------------------------------------------- 1 | // 2 | // damirlj@yahoo.com 3 | // Copyright (c) 2021. All rights reserved! 4 | // 5 | 6 | #ifndef INCLUDE_COMMON_FILE_CHARINPUTFILESTREAM_HXX_ 7 | #define INCLUDE_COMMON_FILE_CHARINPUTFILESTREAM_HXX_ 8 | 9 | 10 | #include "FileStream.h" 11 | 12 | namespace utils::file 13 | { 14 | // Strong (named) type - type alias 15 | using CharInputFileStream = InputFileStream; 16 | } 17 | 18 | #endif /* INCLUDE_COMMON_FILE_CHARINPUTFILESTREAM_HXX_ */ 19 | -------------------------------------------------------------------------------- /src/FileStreams/CharOutputFileStream.h: -------------------------------------------------------------------------------- 1 | // 2 | // damirlj@yahoo.com 3 | // Copyright (c) 2021. All rights reserved! 4 | // 5 | 6 | #ifndef INCLUDE_COMMON_FILE_CHAROUTPUTFILESTREAM_H_ 7 | #define INCLUDE_COMMON_FILE_CHAROUTPUTFILESTREAM_H_ 8 | 9 | #include "FileStream.h" 10 | 11 | namespace utils::file 12 | { 13 | // Strong (named) type - type alias 14 | using CharOutputFileStream = OutputFileStream; 15 | } 16 | 17 | 18 | #endif /* INCLUDE_COMMON_FILE_CHAROUTPUTFILESTREAM_H_ */ 19 | -------------------------------------------------------------------------------- /src/FileStreams/FileStream.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // damirlj@yahoo.com 3 | // Copyright (c) 2021. All rights reserved! 4 | // 5 | 6 | #include "FileStream.h" 7 | 8 | using namespace utils::file; 9 | 10 | FileStream::FileStream(const std::filesystem::path& path, std::ios_base::openmode mode) noexcept 11 | { 12 | m_file.open(path, mode); 13 | } 14 | 15 | FileStream::~FileStream() 16 | { 17 | m_file.close(); 18 | } 19 | 20 | 21 | bool FileStream::isOpen() const 22 | { 23 | return m_file.is_open(); 24 | } 25 | 26 | std::size_t FileStream::size() const 27 | { 28 | const auto pos = m_file.tellg(); //current file position 29 | 30 | m_file.seekg(0, std::ios::end); 31 | const auto size = static_cast(m_file.tellg()); 32 | 33 | m_file.seekg(pos); 34 | 35 | return size; 36 | } 37 | -------------------------------------------------------------------------------- /src/FileStreams/FileStream.h: -------------------------------------------------------------------------------- 1 | // 2 | // damirlj@yahoo.com 3 | // Copyright (c) 2021. All rights reserved! 4 | // 5 | 6 | #ifndef FILE_STREAMS_H_ 7 | #define FILE_STREAMS_H_ 8 | 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | 19 | namespace utils::file 20 | { 21 | /** 22 | * RAII wrapper around the std::fstream 23 | */ 24 | class FileStream 25 | { 26 | public: 27 | 28 | 29 | /** 30 | * C-tor 31 | * Create the file stream. 32 | * Open the file. 33 | * 34 | * @param path The absolute file path 35 | * @param mode Open mode, which specify the type of the file stream and the 36 | * way how it should be created 37 | */ 38 | FileStream(const std::filesystem::path& path, std::ios_base::openmode mode) noexcept; 39 | 40 | // Copy operation forbidden 41 | 42 | FileStream(const FileStream& ) = delete; 43 | FileStream& operator = (const FileStream& ) = delete; 44 | 45 | bool isOpen() const; 46 | 47 | std::size_t size() const; 48 | 49 | /** 50 | * D-tor 51 | * 52 | * Close the file stream 53 | */ 54 | virtual ~FileStream(); 55 | 56 | protected: 57 | 58 | mutable std::fstream m_file; 59 | }; 60 | 61 | template 62 | class OutputFileStream : public FileStream 63 | { 64 | public: 65 | 66 | explicit OutputFileStream(const std::filesystem::path& path) noexcept : 67 | FileStream(path, std::ofstream::out) 68 | {} 69 | 70 | OutputFileStream(const std::filesystem::path& path, std::ios_base::openmode mode) noexcept : 71 | FileStream(path, std::ofstream::out | mode) 72 | {} 73 | 74 | virtual ~OutputFileStream() override = default; 75 | 76 | using data_t = T; 77 | using chunk_t = std::vector; 78 | 79 | virtual void write(const chunk_t& data) 80 | { 81 | writeData(data); 82 | } 83 | 84 | virtual void write(chunk_t&& data) 85 | { 86 | writeData(std::move(data)); 87 | } 88 | 89 | private: 90 | 91 | template 92 | static constexpr bool compatible = std::is_same_v, chunk_t> || 93 | std::is_constructible_v || 94 | std::is_convertible_v; 95 | 96 | template> 97 | void writeData(Data&& data) 98 | { 99 | auto&& rdata = std::forward(data);//universal reference 100 | m_file.write(reinterpret_cast(&rdata[0]), rdata.size()); 101 | } 102 | 103 | };//class OutputFileStream 104 | 105 | 106 | 107 | template 108 | class InputFileStream: public FileStream 109 | { 110 | public: 111 | 112 | explicit InputFileStream(const std::filesystem::path& path) noexcept: 113 | FileStream(path, std::ifstream::in) 114 | {} 115 | 116 | InputFileStream(const std::filesystem::path& path, std::ios_base::openmode mode) noexcept : 117 | FileStream(path, std::ifstream::in | mode) 118 | {} 119 | 120 | virtual ~InputFileStream() override = default; 121 | 122 | using data_t = T; 123 | using chunk_t = std::vector; 124 | 125 | /** 126 | * Read entire file into memory 127 | * @note It will throw std::runtime_error in case that operation on 128 | * the input file stream (std::ifstream) is somehow failed 129 | */ 130 | virtual chunk_t readAll(); 131 | 132 | };//class InputFileStream 133 | 134 | template 135 | inline typename InputFileStream::chunk_t 136 | InputFileStream::readAll() 137 | { 138 | const auto file_size = size(); 139 | 140 | chunk_t buffer(file_size); 141 | 142 | m_file.seekg(0);//rewind the position to the beginning of the stream 143 | if (!m_file.read(reinterpret_cast(&buffer[0]), file_size) ) 144 | throw std::runtime_error(strerror(errno)); 145 | 146 | return buffer; 147 | } 148 | }//namespace:utils::file 149 | 150 | #endif /*FILE_STREAMS_H_*/ 151 | -------------------------------------------------------------------------------- /src/Kotlin/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /src/Kotlin/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias(libs.plugins.androidApplication) 3 | alias(libs.plugins.jetbrainsKotlinAndroid) 4 | } 5 | 6 | android { 7 | namespace 'com.example.practice_kotlin' 8 | compileSdk 34 9 | 10 | defaultConfig { 11 | applicationId "com.example.practice_kotlin" 12 | minSdk 31 13 | targetSdk 34 14 | versionCode 1 15 | versionName "1.0" 16 | 17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 18 | vectorDrawables { 19 | useSupportLibrary true 20 | } 21 | } 22 | 23 | buildTypes { 24 | release { 25 | minifyEnabled false 26 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 27 | } 28 | } 29 | compileOptions { 30 | sourceCompatibility JavaVersion.VERSION_1_8 31 | targetCompatibility JavaVersion.VERSION_1_8 32 | } 33 | kotlinOptions { 34 | jvmTarget = '1.8' 35 | } 36 | buildFeatures { 37 | compose true 38 | } 39 | composeOptions { 40 | kotlinCompilerExtensionVersion '1.5.1' 41 | } 42 | packaging { 43 | resources { 44 | excludes += '/META-INF/{AL2.0,LGPL2.1}' 45 | } 46 | } 47 | } 48 | 49 | dependencies { 50 | 51 | implementation libs.androidx.core.ktx 52 | implementation libs.androidx.lifecycle.runtime.ktx 53 | implementation libs.androidx.activity.compose 54 | implementation platform(libs.androidx.compose.bom) 55 | implementation libs.androidx.ui 56 | implementation libs.androidx.ui.graphics 57 | implementation libs.androidx.ui.tooling.preview 58 | implementation libs.androidx.material3 59 | 60 | testImplementation libs.junit 61 | androidTestImplementation libs.androidx.junit 62 | androidTestImplementation libs.androidx.espresso.core 63 | androidTestImplementation platform(libs.androidx.compose.bom) 64 | androidTestImplementation libs.androidx.ui.test.junit4 65 | debugImplementation libs.androidx.ui.tooling 66 | debugImplementation libs.androidx.ui.test.manifest 67 | 68 | 69 | // For coroutines tests 70 | testImplementation libs.kotlinx.coroutines.test 71 | implementation libs.kotlin.reflect 72 | 73 | } -------------------------------------------------------------------------------- /src/Kotlin/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 15 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/Kotlin/src/main/java/com/example/practice_kotlin/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.practice_kotlin 2 | 3 | import android.os.Bundle 4 | import androidx.activity.ComponentActivity 5 | import androidx.activity.compose.setContent 6 | import androidx.compose.foundation.layout.fillMaxSize 7 | import androidx.compose.material3.MaterialTheme 8 | import androidx.compose.material3.Surface 9 | import androidx.compose.material3.Text 10 | import androidx.compose.runtime.Composable 11 | import androidx.compose.ui.Modifier 12 | import androidx.compose.ui.tooling.preview.Preview 13 | import com.example.practice_kotlin.helpers.func_elapsed 14 | import com.example.practice_kotlin.ui.theme.Practice_KotlinTheme 15 | 16 | 17 | class MainActivity : ComponentActivity() { 18 | override fun onCreate(savedInstanceState: Bundle?) { 19 | super.onCreate(savedInstanceState) 20 | setContent { 21 | Practice_KotlinTheme { 22 | // A surface container using the 'background' color from the theme 23 | Surface( 24 | modifier = Modifier.fillMaxSize(), 25 | color = MaterialTheme.colorScheme.background 26 | ) { 27 | Greeting() 28 | } 29 | } 30 | } 31 | } 32 | } 33 | 34 | private fun test() { 35 | for (i in 1..5) { 36 | Thread.sleep(10) 37 | } 38 | } 39 | 40 | @Composable 41 | fun Greeting(modifier: Modifier = Modifier) { 42 | val (_, elapsedTime) = func_elapsed(::test) 43 | Text( 44 | text = "Elapsed time: $elapsedTime", 45 | modifier = modifier 46 | ) 47 | 48 | } 49 | 50 | @Preview(showBackground = true) 51 | @Composable 52 | fun GreetingPreview() { 53 | 54 | Practice_KotlinTheme { 55 | Greeting() 56 | } 57 | } -------------------------------------------------------------------------------- /src/Kotlin/src/main/java/com/example/practice_kotlin/coroutines/CoroutinesWrapper.kt: -------------------------------------------------------------------------------- 1 | package com.example.practice_kotlin.coroutines 2 | 3 | import kotlinx.coroutines.CoroutineDispatcher 4 | import kotlinx.coroutines.CoroutineName 5 | import kotlinx.coroutines.CoroutineScope 6 | import kotlinx.coroutines.Job 7 | import kotlinx.coroutines.async 8 | import kotlinx.coroutines.coroutineScope 9 | 10 | 11 | class CoroutinesWrapper(dispatcher: CoroutineDispatcher) : 12 | CoroutineScope, AutoCloseable { 13 | 14 | // Job management - to ensure that all jobs launched within CoroutinesWrapper 15 | // will be properly closed - by calling job.cancel() 16 | private val job = Job() 17 | // Specifies the environment in which the coroutines will be executed (scheduler, name and other custom attributes) 18 | override val coroutineContext = dispatcher + job + CoroutineName("CoroutinesWrapper") 19 | 20 | /** 21 | * Turn the callable into (deferred) coroutine, that 22 | * will be executed within the specified CoroutineScope - within 23 | * the given thread/thread pool context specified by _dispatcher_ parameter of the c-tor, while 24 | * calling coroutine remains suspended 25 | */ 26 | private suspend fun func2coroutine(f: () -> R): R = coroutineScope { 27 | val coro = async { f() } // coroutine builder: factory method to start the coroutine within the given scope 28 | coro.await() 29 | } 30 | 31 | /** 32 | * Reference implementation {@link CoroutinesWrapper#func2coroutine} 33 | * @param f Regular function that is turned into deferred coroutine 34 | * @return Result of the regular function, of type R 35 | */ 36 | suspend fun await(f: () -> R): R { 37 | return func2coroutine(f) 38 | } 39 | 40 | /** 41 | * high-order function 42 | * Providing the thread/thread pool context (CoroutineScope) for the coroutine 43 | * that will be executed, while suspending the calling coroutine 44 | * 45 | * @param f Coroutine to be executed 46 | * @return The result of the coroutine, of type R 47 | */ 48 | suspend fun awaitCoroutine(f: suspend () -> R): R { 49 | return coroutineScope { f() } 50 | } 51 | 52 | override fun close() { 53 | job.cancel() 54 | } 55 | } -------------------------------------------------------------------------------- /src/Kotlin/src/main/java/com/example/practice_kotlin/design_patterns/builder/Builder.kt: -------------------------------------------------------------------------------- 1 | package com.example.practice_kotlin.design_patterns.builder 2 | 3 | class A private constructor( 4 | val id: Int? = null, 5 | val name: String? = null 6 | ) { 7 | 8 | companion object { 9 | class Builder() { 10 | private var id: Int? = null 11 | private var name: String? = null 12 | 13 | // For aggregated updates - initialize with the current one (copy-constructor) 14 | constructor (a: A) : this() { 15 | id = a.id 16 | name = a.name 17 | } 18 | 19 | // Setters 20 | 21 | fun setId(id: Int) = apply { this.id = id } 22 | 23 | fun setName(name: String) = apply { this.name = name } 24 | 25 | /** 26 | * For aggregated updates: 27 | * - create the builder with secondary c-tor, that takes the current value of outer class instance 28 | * - update the aggregate value with the new delta-update (not all properties are set). 29 | * This will be new aggregated value 30 | * 31 | * @param a The outer class instance, as aggregated value 32 | */ 33 | fun update(a: A) = apply { 34 | a.id?.let { id = it } 35 | a.name?.let { name = it } 36 | } 37 | 38 | fun build() = A(id, name) 39 | } 40 | 41 | // Helper method for parsing the dynamic updates as partial one (deltas) 42 | fun update(curr: A, new: A) = Builder(curr).update(new).build() 43 | } 44 | 45 | 46 | override fun toString(): String { 47 | return "[id=$id, name=$name]" 48 | } 49 | } -------------------------------------------------------------------------------- /src/Kotlin/src/main/java/com/example/practice_kotlin/design_patterns/builder/Builder2.kt: -------------------------------------------------------------------------------- 1 | package com.example.practice_kotlin.design_patterns.builder 2 | 3 | enum class Engine { ELECTRIC, HYBRID, DIESEL, PETROL, HYDROGEN, LNG } 4 | 5 | enum class Camera { FRONT, REAR, BOTH, NONE } 6 | enum class Smartphone { CarPlay, AndroidAuto, BOTH, NONE } 7 | enum class ADAS { AdaptiveControl, Copilot, BOTH, NONE } 8 | 9 | 10 | class CarConfiguration private constructor( 11 | // mandatory 12 | val id: Int, 13 | val brand: String, 14 | val engine: Engine, 15 | // optionals 16 | val camera: Camera?, 17 | val smartphone: Smartphone?, 18 | val adas: ADAS? 19 | ) { 20 | 21 | companion object { 22 | class Builder(var id: Int, var brand: String, var engine: Engine) { 23 | 24 | // optionals 25 | var camera: Camera? = null 26 | var smartphone: Smartphone? = null 27 | var adas: ADAS? = null 28 | 29 | // For aggregated updates - initialize the builder with previous configuration 30 | constructor (car: CarConfiguration) : this(car.id, car.brand, car.engine) { 31 | camera = car.camera 32 | smartphone = car.smartphone 33 | adas = car.adas 34 | } 35 | 36 | fun build() = CarConfiguration(id, brand, engine, camera, smartphone, adas) 37 | 38 | // For aggregated updates - the new (partial) configuration that will be applied on top of the current configuration 39 | fun update(car: CarConfiguration) = apply { 40 | id = car.id 41 | brand = car.brand 42 | engine = car.engine 43 | 44 | car.camera?.let { camera = it } 45 | car.smartphone?.let { smartphone = it } 46 | car.adas?.let { adas = it } 47 | } 48 | } 49 | 50 | /** 51 | * Helper methods, for building the outer class instance on the fly 52 | * 53 | * @param block This is lambda that extends Builder (placeholder for anonymous Extension Function) on the fly - Builder is receiver, 54 | * which means the callable can access any non-private (public) properties of the 55 | * receiver (all of them) through "this", in order to properly configure the outer class 56 | */ 57 | inline fun build(id: Int, brand: String, engine: Engine, block: Builder.() -> Unit) = 58 | Builder(id, brand, engine).apply(block).build() 59 | 60 | // For aggregated updates 61 | inline fun build(currConfig: CarConfiguration, block: Builder.() -> Unit) = 62 | Builder(currConfig).apply(block).build() 63 | 64 | fun build(currConfig: CarConfiguration, newConfig: CarConfiguration) = 65 | Builder(currConfig).update(newConfig).build() 66 | } 67 | 68 | override fun toString(): String { 69 | return "[id=$id, brand=$brand, engine=$engine, camera=$camera, smartphone=$smartphone, adas=$adas]" 70 | } 71 | } 72 | 73 | 74 | -------------------------------------------------------------------------------- /src/Kotlin/src/main/java/com/example/practice_kotlin/design_patterns/builder/Builder3.kt: -------------------------------------------------------------------------------- 1 | package com.example.practice_kotlin.design_patterns.builder 2 | 3 | 4 | class CarConfiguration2 ( 5 | // mandatory 6 | var id: Int, 7 | var brand: String, 8 | var engine: Engine, 9 | // optionals 10 | var camera: Camera? = null, 11 | var smartphone: Smartphone? = null, 12 | var adas: ADAS? = null 13 | ) { 14 | 15 | 16 | // For aggregated updates - initialize with previous configuration (copy-constructor ) 17 | constructor (car: CarConfiguration2) : this(car.id, car.brand, car.engine) { 18 | camera = car.camera 19 | smartphone = car.smartphone 20 | adas = car.adas 21 | } 22 | 23 | // For aggregated updates - the new (partial) configuration that will be applied on top of the current configuration 24 | fun update(car: CarConfiguration2) = apply { 25 | id = car.id 26 | brand = car.brand 27 | engine = car.engine 28 | 29 | car.camera?.let { camera = it } 30 | car.smartphone?.let { smartphone = it } 31 | car.adas?.let { adas = it } 32 | } 33 | 34 | 35 | companion object { 36 | /** 37 | * Helper method, for building the outer class, as wrapper around the apply() 38 | * We don't call it directly, since it's about communicating the intention clearly (naming) 39 | * 40 | * @param block This is lambda that extends the receiver - instance of the outer class, 41 | * which means the callable can access any non-private (public) properties of the 42 | * receiver (all of them), in order to properly configure the outer class 43 | */ 44 | 45 | inline fun build( 46 | id: Int, 47 | brand: String, 48 | engine: Engine, 49 | block: CarConfiguration2.() -> Unit 50 | ) = 51 | CarConfiguration2(id, brand, engine).apply(block) 52 | 53 | // For aggregated updates 54 | inline fun build(currConfig: CarConfiguration2, block: CarConfiguration2.() -> Unit) = 55 | CarConfiguration2(currConfig).apply(block) 56 | 57 | fun build(currConfig: CarConfiguration2, newConfig: CarConfiguration2) = 58 | CarConfiguration2(currConfig).update(newConfig) 59 | } 60 | 61 | 62 | override fun toString(): String { 63 | return "[id=$id, brand=$brand, engine=$engine, camera=$camera, smartphone=$smartphone, adas=$adas]" 64 | } 65 | } 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/Kotlin/src/main/java/com/example/practice_kotlin/helpers/ElapsedTime.kt: -------------------------------------------------------------------------------- 1 | package com.example.practice_kotlin.helpers 2 | 3 | import kotlinx.coroutines.runBlocking 4 | import kotlin.reflect.KFunction 5 | 6 | 7 | private suspend fun elapsed(f: suspend () -> R): Pair { 8 | val start = System.currentTimeMillis() 9 | val result = f() 10 | return Pair(result, System.currentTimeMillis() - start) 11 | } 12 | 13 | suspend fun elapsedAny(f: KFunction, vararg args : Any?): Pair { 14 | return elapsed { f.call(*args) } 15 | } 16 | 17 | /** 18 | * For measuring the elapsed time of a synchronous function. 19 | * It will be turned into blocking call - waiting on the function to be 20 | * executed within the coroutine context 21 | */ 22 | fun func_elapsed(f: () -> R): Pair { 23 | val result: Pair 24 | runBlocking { 25 | result = elapsed(f) // suspension point 26 | } 27 | return result 28 | } 29 | 30 | /** 31 | * For measuring elapsed time of a suspend call 32 | */ 33 | suspend fun coro_elapsed(f: suspend () -> R): Pair = elapsed(f) 34 | 35 | -------------------------------------------------------------------------------- /src/Kotlin/src/main/java/com/example/practice_kotlin/ui/theme/Color.kt: -------------------------------------------------------------------------------- 1 | package com.example.practice_kotlin.ui.theme 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | val Purple80 = Color(0xFFD0BCFF) 6 | val PurpleGrey80 = Color(0xFFCCC2DC) 7 | val Pink80 = Color(0xFFEFB8C8) 8 | 9 | val Purple40 = Color(0xFF6650a4) 10 | val PurpleGrey40 = Color(0xFF625b71) 11 | val Pink40 = Color(0xFF7D5260) -------------------------------------------------------------------------------- /src/Kotlin/src/main/java/com/example/practice_kotlin/ui/theme/Theme.kt: -------------------------------------------------------------------------------- 1 | package com.example.practice_kotlin.ui.theme 2 | 3 | import android.app.Activity 4 | import android.os.Build 5 | import androidx.compose.foundation.isSystemInDarkTheme 6 | import androidx.compose.material3.MaterialTheme 7 | import androidx.compose.material3.darkColorScheme 8 | import androidx.compose.material3.dynamicDarkColorScheme 9 | import androidx.compose.material3.dynamicLightColorScheme 10 | import androidx.compose.material3.lightColorScheme 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.runtime.SideEffect 13 | import androidx.compose.ui.graphics.toArgb 14 | import androidx.compose.ui.platform.LocalContext 15 | import androidx.compose.ui.platform.LocalView 16 | import androidx.core.view.WindowCompat 17 | 18 | private val DarkColorScheme = darkColorScheme( 19 | primary = Purple80, 20 | secondary = PurpleGrey80, 21 | tertiary = Pink80 22 | ) 23 | 24 | private val LightColorScheme = lightColorScheme( 25 | primary = Purple40, 26 | secondary = PurpleGrey40, 27 | tertiary = Pink40 28 | 29 | /* Other default colors to override 30 | background = Color(0xFFFFFBFE), 31 | surface = Color(0xFFFFFBFE), 32 | onPrimary = Color.White, 33 | onSecondary = Color.White, 34 | onTertiary = Color.White, 35 | onBackground = Color(0xFF1C1B1F), 36 | onSurface = Color(0xFF1C1B1F), 37 | */ 38 | ) 39 | 40 | @Composable 41 | fun Practice_KotlinTheme( 42 | darkTheme: Boolean = isSystemInDarkTheme(), 43 | // Dynamic color is available on Android 12+ 44 | dynamicColor: Boolean = true, 45 | content: @Composable () -> Unit 46 | ) { 47 | val colorScheme = when { 48 | dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { 49 | val context = LocalContext.current 50 | if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) 51 | } 52 | 53 | darkTheme -> DarkColorScheme 54 | else -> LightColorScheme 55 | } 56 | val view = LocalView.current 57 | if (!view.isInEditMode) { 58 | SideEffect { 59 | val window = (view.context as Activity).window 60 | window.statusBarColor = colorScheme.primary.toArgb() 61 | WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme 62 | } 63 | } 64 | 65 | MaterialTheme( 66 | colorScheme = colorScheme, 67 | typography = Typography, 68 | content = content 69 | ) 70 | } -------------------------------------------------------------------------------- /src/Kotlin/src/main/java/com/example/practice_kotlin/ui/theme/Type.kt: -------------------------------------------------------------------------------- 1 | package com.example.practice_kotlin.ui.theme 2 | 3 | import androidx.compose.material3.Typography 4 | import androidx.compose.ui.text.TextStyle 5 | import androidx.compose.ui.text.font.FontFamily 6 | import androidx.compose.ui.text.font.FontWeight 7 | import androidx.compose.ui.unit.sp 8 | 9 | // Set of Material typography styles to start with 10 | val Typography = Typography( 11 | bodyLarge = TextStyle( 12 | fontFamily = FontFamily.Default, 13 | fontWeight = FontWeight.Normal, 14 | fontSize = 16.sp, 15 | lineHeight = 24.sp, 16 | letterSpacing = 0.5.sp 17 | ) 18 | /* Other default text styles to override 19 | titleLarge = TextStyle( 20 | fontFamily = FontFamily.Default, 21 | fontWeight = FontWeight.Normal, 22 | fontSize = 22.sp, 23 | lineHeight = 28.sp, 24 | letterSpacing = 0.sp 25 | ), 26 | labelSmall = TextStyle( 27 | fontFamily = FontFamily.Default, 28 | fontWeight = FontWeight.Medium, 29 | fontSize = 11.sp, 30 | lineHeight = 16.sp, 31 | letterSpacing = 0.5.sp 32 | ) 33 | */ 34 | ) -------------------------------------------------------------------------------- /src/Kotlin/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /src/Kotlin/src/main/res/mipmap-anydpi/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/Kotlin/src/main/res/mipmap-anydpi/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/Kotlin/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/src/Kotlin/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /src/Kotlin/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/src/Kotlin/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /src/Kotlin/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/src/Kotlin/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /src/Kotlin/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/src/Kotlin/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /src/Kotlin/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/src/Kotlin/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /src/Kotlin/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/src/Kotlin/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /src/Kotlin/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/src/Kotlin/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /src/Kotlin/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/src/Kotlin/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /src/Kotlin/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/src/Kotlin/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /src/Kotlin/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/src/Kotlin/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /src/Kotlin/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /src/Kotlin/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Practice_Kotlin 3 | -------------------------------------------------------------------------------- /src/Kotlin/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |