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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 102 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,109 @@
# CalQ
![alt text](CalQ.png)
# AtomicFlagGuard - RAII for Atomic Boolean Management

## Introduction
This is a simple calculator that I wrote when I first started with Qt and QML. it calculates the answer using javascipt's eval function and it has some predefined themes that you can enjoy
A modern C++17 RAII (Resource Acquisition Is Initialization) class for automatically managing atomic boolean flags.

## Overview

The `AtomicFlagGuard` class automatically sets an atomic boolean to `true` when entering a function scope and resets it to `false` when leaving the scope. This ensures proper cleanup even in the presence of exceptions or early returns.

## Features

- **Exception Safe**: Guarantees flag reset even if exceptions are thrown
- **Thread Safe**: Uses proper memory ordering for atomic operations
- **Modern C++**: Follows C++17 best practices
- **Zero Overhead**: Minimal runtime cost
- **Easy Integration**: Drop-in solution for existing code

## Basic Usage

```cpp
#include "atomic_flag_guard.h"

class MyClass {
private:
std::atomic_bool m_active = false;

public:
void my_function() {
// Method 1: Direct usage
AtomicFlagGuard guard(m_active);

// m_active is now true
// Do your work here...

// m_active automatically becomes false when function exits
}

void another_function() {
// Method 2: Using convenience macro
ATOMIC_FLAG_GUARD(m_active);

// m_active is now true
// Your code here...

// Automatic cleanup on scope exit
}
};
```

## Key Benefits

1. **Automatic Cleanup**: No need to manually reset the flag
2. **Exception Safety**: Flag is always reset, even if exceptions occur
3. **Early Return Safe**: Works correctly with multiple return points
4. **Thread Safe**: Proper atomic memory ordering
5. **Copy/Move Protected**: Prevents accidental copying or moving

## Memory Ordering

The class uses:
- `std::memory_order_release` for stores (ensuring visibility to other threads)
- `std::memory_order_acquire` for loads (ensuring we see the latest value)

## Thread Safety

While the guard itself is thread-safe for the atomic operations, note that:
- Multiple threads can each have their own guard instances
- The atomic flag can be safely accessed from multiple threads
- The "last writer wins" principle applies when multiple guards operate on the same flag

## Files

- `atomic_flag_guard.h` - The main RAII guard class
- `example_usage.cpp` - Comprehensive examples and test cases
- `simple_usage_example.cpp` - Basic integration example
- `Makefile` - Build configuration

## Building
You can just download and run the releases. for that go to Releases section. if you want to build it your self just load the project using the .pro file into QtCreator and run it

## Releases
Here are the releases for Linux, Android and Windows platforms:
https://github.com/ShahriarSS/CalQ/releases
```bash
# Basic build
make

# Run examples
make run

# Debug build
make debug

# Strict compilation with sanitizers
make strict

# Clean up
make clean
```

## Requirements

- C++17 or later
- Compiler with atomic support (GCC 7+, Clang 5+, MSVC 2017+)

## Integration

## Built With
* [Qt](http://qt.io/) - The framework used to write the application
Simply include the header file in your project:

## License
This project is licensed under the GPL_V3 License - see the [LICENSE.md](LICENSE.md) file for details
```cpp
#include "atomic_flag_guard.h"
```

## Preview
![alt text](preview3.png)
No additional linking required - it's header-only!
72 changes: 72 additions & 0 deletions atomic_flag_guard.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#pragma once

#include <atomic>

/**
* @brief RAII guard for managing an atomic boolean flag
*
* This class follows the RAII (Resource Acquisition Is Initialization) pattern
* to automatically manage the state of an atomic boolean. The flag is set to
* true upon construction and reset to false upon destruction, ensuring proper
* cleanup even in the presence of exceptions.
*/
class AtomicFlagGuard {
public:
/**
* @brief Construct a new Atomic Flag Guard object
*
* @param flag Reference to the atomic boolean to manage
* @param set_value The value to set when entering scope (default: true)
*/
explicit AtomicFlagGuard(std::atomic_bool& flag, bool set_value = true)
: m_flag(flag), m_original_value(flag.load()), m_reset_value(!set_value) {
m_flag.store(set_value, std::memory_order_release);
}

/**
* @brief Destroy the Atomic Flag Guard object
*
* Automatically resets the flag to false (or the opposite of set_value)
*/
~AtomicFlagGuard() {
m_flag.store(m_reset_value, std::memory_order_release);
}

// Delete copy constructor and copy assignment operator
AtomicFlagGuard(const AtomicFlagGuard&) = delete;
AtomicFlagGuard& operator=(const AtomicFlagGuard&) = delete;

// Delete move constructor and move assignment operator
AtomicFlagGuard(AtomicFlagGuard&&) = delete;
AtomicFlagGuard& operator=(AtomicFlagGuard&&) = delete;

/**
* @brief Check if the flag is currently active
*
* @return true if the flag is set to the active value
*/
bool is_active() const {
return m_flag.load(std::memory_order_acquire);
}

/**
* @brief Get the original value of the flag before this guard was created
*
* @return The original value of the atomic flag
*/
bool original_value() const {
return m_original_value;
}

private:
std::atomic_bool& m_flag; ///< Reference to the managed atomic flag
const bool m_original_value; ///< Original value of the flag
const bool m_reset_value; ///< Value to reset to on destruction
};

/**
* @brief Convenience macro for creating an atomic flag guard
*
* Usage: ATOMIC_FLAG_GUARD(my_atomic_bool);
*/
#define ATOMIC_FLAG_GUARD(flag) AtomicFlagGuard guard_##flag(flag)
Binary file added example_usage
Binary file not shown.
133 changes: 133 additions & 0 deletions example_usage.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
#include "atomic_flag_guard.h"
#include <iostream>
#include <thread>
#include <chrono>

class MyClass {
private:
std::atomic_bool m_active = false;

public:
// Method 1: Using the RAII guard directly
void some_function() {
// Create the guard - m_active becomes true automatically
AtomicFlagGuard guard(m_active);

std::cout << "Entered some_function, m_active = " << m_active.load() << std::endl;

// Do some work...
std::this_thread::sleep_for(std::chrono::milliseconds(100));

// When this function exits (normally or via exception),
// the guard's destructor will automatically set m_active to false
}

// Method 2: Using the convenience macro
void another_function() {
ATOMIC_FLAG_GUARD(m_active); // Expands to: AtomicFlagGuard guard_m_active(m_active);

std::cout << "Entered another_function, m_active = " << m_active.load() << std::endl;

// Simulate some work that might throw an exception
if (false) { // Change to true to test exception safety
throw std::runtime_error("Something went wrong!");
}

std::this_thread::sleep_for(std::chrono::milliseconds(100));

// m_active will be automatically set to false when exiting
}

// Method 3: Using RAII with early return
void function_with_early_return(bool should_return_early) {
AtomicFlagGuard guard(m_active);

std::cout << "Entered function_with_early_return, m_active = " << m_active.load() << std::endl;

if (should_return_early) {
std::cout << "Returning early, but m_active will still be reset!" << std::endl;
return; // Guard destructor will still be called
}

// Normal processing...
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}

// Method 4: Nested function calls (multiple guards)
void outer_function() {
AtomicFlagGuard outer_guard(m_active);
std::cout << "In outer_function, m_active = " << m_active.load() << std::endl;

inner_function();

std::cout << "Back in outer_function, m_active = " << m_active.load() << std::endl;
}

void inner_function() {
// This will temporarily change m_active, but it will be restored
// when this guard is destroyed
std::cout << "In inner_function, m_active = " << m_active.load() << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}

// Utility function to check the current state
bool is_active() const {
return m_active.load();
}

// Thread-safe function using the guard
void thread_safe_function(int thread_id) {
AtomicFlagGuard guard(m_active);

std::cout << "Thread " << thread_id << " entered, m_active = "
<< m_active.load() << std::endl;

// Simulate some work
std::this_thread::sleep_for(std::chrono::milliseconds(200));

std::cout << "Thread " << thread_id << " finishing, m_active = "
<< m_active.load() << std::endl;
}
};

int main() {
MyClass obj;

std::cout << "=== Testing basic RAII functionality ===" << std::endl;
std::cout << "Initial state: m_active = " << obj.is_active() << std::endl;

obj.some_function();
std::cout << "After some_function: m_active = " << obj.is_active() << std::endl;

std::cout << "\n=== Testing with macro ===" << std::endl;
obj.another_function();
std::cout << "After another_function: m_active = " << obj.is_active() << std::endl;

std::cout << "\n=== Testing early return ===" << std::endl;
obj.function_with_early_return(true);
std::cout << "After early return: m_active = " << obj.is_active() << std::endl;

std::cout << "\n=== Testing nested calls ===" << std::endl;
obj.outer_function();
std::cout << "After nested calls: m_active = " << obj.is_active() << std::endl;

std::cout << "\n=== Testing exception safety ===" << std::endl;
try {
// You can modify another_function to throw an exception to test this
obj.another_function();
} catch (const std::exception& e) {
std::cout << "Caught exception: " << e.what() << std::endl;
}
std::cout << "After exception: m_active = " << obj.is_active() << std::endl;

std::cout << "\n=== Testing multi-threading ===" << std::endl;
std::thread t1(&MyClass::thread_safe_function, &obj, 1);
std::thread t2(&MyClass::thread_safe_function, &obj, 2);

t1.join();
t2.join();

std::cout << "Final state: m_active = " << obj.is_active() << std::endl;

return 0;
}
51 changes: 51 additions & 0 deletions simple_usage_example.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#include "atomic_flag_guard.h"
#include <iostream>
#include <atomic>

// Example: Your existing class with the atomic boolean
class YourClass {
private:
std::atomic_bool m_active = false; // Your existing member variable

public:
// Your functions where you want to manage the flag
void critical_function() {
// Simply create the guard at the beginning of the function
AtomicFlagGuard guard(m_active);

// Now m_active is automatically true
std::cout << "Inside critical_function, active: " << m_active.load() << std::endl;

// Do your work here...
// The flag will automatically be set to false when the function exits
// (whether normally, via return, or via exception)
}

void another_critical_function() {
// Alternative: use the convenience macro
ATOMIC_FLAG_GUARD(m_active);

std::cout << "Inside another_critical_function, active: " << m_active.load() << std::endl;

// Your code here...
}

// Check current state
bool is_active() const {
return m_active.load();
}
};

int main() {
YourClass obj;

std::cout << "Before: " << obj.is_active() << std::endl;

obj.critical_function();
std::cout << "After critical_function: " << obj.is_active() << std::endl;

obj.another_critical_function();
std::cout << "After another_critical_function: " << obj.is_active() << std::endl;

return 0;
}