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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,21 @@ jobs:
- name: Test
working-directory: ${{ steps.strings.outputs.build-output-dir }}
run: |
examples=(
oxide_match_example
oxide_functional_example
oxide_covariant_dispatch_example
oxide_vector_example
oxide_database_example
)
if [ "${{ runner.os }}" == "Windows" ]; then
./${{ matrix.build_type }}/oxide_examples.exe
for ex in "${examples[@]}"; do
./${{ matrix.build_type }}/$ex.exe || exit 1
done
else
./oxide_examples
for ex in "${examples[@]}"; do
./$ex || exit 1
done
fi
shell: bash

Expand All @@ -117,7 +128,7 @@ jobs:
- name: Create Release and Upload oxide.hpp
uses: softprops/action-gh-release@v2
with:
files: oxide.hpp
files: include/oxide.hpp
name: RELEASE ${{ github.ref_name }}
draft: false
prerelease: false
Expand Down
49 changes: 38 additions & 11 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,36 +19,63 @@
# DEALINGS IN THE SOFTWARE.

cmake_minimum_required(VERSION 3.21)
project(oxide VERSION 1.0.1 LANGUAGES CXX)
project(oxide VERSION 1.0.2 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_library(oxide INTERFACE)

target_include_directories(oxide INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)

target_compile_features(oxide INTERFACE cxx_std_23)

if(MSVC)
target_compile_options(oxide INTERFACE /EHsc)
endif()

target_compile_definitions(oxide INTERFACE
OXIDE_VERSION_MAJOR=${PROJECT_VERSION_MAJOR}
OXIDE_VERSION_MINOR=${PROJECT_VERSION_MINOR}
OXIDE_VERSION_PATCH=${PROJECT_VERSION_PATCH}
OXIDE_VERSION="${PROJECT_VERSION}"
)

# Define installation directory for headers
set(INCLUDE_INSTALL_DIR include)

# Create examples executable and link it to oxide
add_executable(oxide_examples examples.cpp)
target_link_libraries(oxide_examples oxide)
# Match example
add_executable(oxide_match_example examples/match.cpp)
target_link_libraries(oxide_match_example oxide)

# Functional example
add_executable(oxide_functional_example examples/functional.cpp)
target_link_libraries(oxide_functional_example oxide)

# Covariant dispatch example
add_executable(oxide_covariant_dispatch_example examples/covariant_dispatch.cpp)
target_link_libraries(oxide_covariant_dispatch_example oxide)

# Vector example
add_executable(oxide_vector_example examples/vector.cpp)
target_link_libraries(oxide_vector_example oxide)

# Database example
add_executable(oxide_database_example examples/database.cpp)
target_link_libraries(oxide_database_example oxide)

install(TARGETS oxide
EXPORT oxideTargets
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
INCLUDES DESTINATION include
EXPORT oxideTargets
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
INCLUDES DESTINATION include
)

install(FILES oxide.hpp DESTINATION include)
install(FILES include/oxide.hpp DESTINATION include)

install(EXPORT oxideTargets
FILE oxideTargets.cmake
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ refer to the `examples.cpp` file or explorer the fully working examples below.
* This resembles standard C++ operator overloading and is more intuitive.

```cpp
#include <oxide.hpp>
#include <functional>
#include <array>
#include <iostream>
#include <oxide.hpp>

struct Quit {};
struct Move { int x, y; };
Expand Down Expand Up @@ -71,10 +71,10 @@ int main() {

### Functional Example
```cpp
#include <oxide.hpp>
#include <functional>
#include <array>
#include <iostream>
#include <oxide.hpp>

struct Quit {};
struct Move { int x, y; };
Expand Down Expand Up @@ -135,10 +135,10 @@ oxide::Option<std::pair<int, int>> get_coordinates(const Message& msg) {
(with optional settings affecting processing)

```cpp
#include <oxide.hpp>
#include <functional>
#include <array>
#include <iostream>
#include <oxide.hpp>

struct Quit {};
struct Move { int x, y; };
Expand Down Expand Up @@ -203,8 +203,8 @@ int main() {

### Results, Errors and Chaining
```cpp
#include <iostream>
#include <oxide.hpp>
#include <iostream>

int main() {
using oxide::Result;
Expand Down Expand Up @@ -243,9 +243,9 @@ int main() {

### Database Example (Vec)
```cpp
#include <oxide.hpp>
#include <functional>
#include <iostream>
#include <oxide.hpp>

struct Insert { std::string key; int value; };
struct Update { std::string key; int new_value; };
Expand Down Expand Up @@ -369,8 +369,8 @@ int main() {

### Covariant Dispatch Example
```cpp
#include <iostream>
#include <oxide.hpp>
#include <iostream>

// Define shape types (no inheritance)
struct Circle {
Expand Down
73 changes: 73 additions & 0 deletions examples/covariant_dispatch.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright (C) 2025 Igal Alkon and ALKONTEK
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <oxide.hpp>

#include <iostream>

// Define shape types (no inheritance)
struct Circle {
double radius = 1.0;

[[nodiscard]] double area() const {
return 3.14159 * radius * radius;
}
};

struct Rectangle {
double width = 7.0, height = 14.0;

[[nodiscard]] double perimeter() const {
return 2 * (width + height);
}
};

// Create a discriminated union for shapes
using ShapeVariant = oxide::Union<Circle, Rectangle>;

// Clone function using pattern matching on the Union
ShapeVariant clone(const ShapeVariant& shape) {
return std::visit(oxide::overloaded{
[](const Circle& c) -> ShapeVariant { return Circle{c.radius}; },
[](const Rectangle& r) -> ShapeVariant { return Rectangle{r.width, r.height}; }
}, shape);
}

int main() {
constexpr ShapeVariant circle = Circle{};
auto cloned_circle = clone(circle);

constexpr ShapeVariant rectangle = Rectangle{};
auto cloned_rectangle = clone(rectangle);

// Use oxide's match and operator>> to polymorphically compute and print properties (akin to covariant dispatch)
cloned_circle >> oxide::match{
[](const Circle& c) { std::cout << "Cloned Circle area: " << c.area() << std::endl; },
[](const Rectangle& r) { std::cout << "Cloned Rectangle perimeter: " << r.perimeter() << std::endl; }
};

cloned_rectangle >> oxide::match{
[](const Circle& c) { std::cout << "Cloned Circle area: " << c.area() << std::endl; },
[](const Rectangle& r) { std::cout << "Cloned Rectangle perimeter: " << r.perimeter() << std::endl; }
};

return 0;
}
144 changes: 144 additions & 0 deletions examples/database.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*
* Copyright (C) 2025 Igal Alkon and ALKONTEK
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <oxide.hpp>

#include <functional>
#include <iostream>


struct Insert { std::string key; int value; };
struct Update { std::string key; int new_value; };
struct Delete { std::string key; };
struct Select { std::string key; std::function<void(int)> callback; };
struct Noop {};

// Your message types (can be one of these)
using Operation = oxide::Union<Insert, Update, Delete, Select, Noop>;

int main() {
oxide::Vec<std::pair<std::string, int>> db;

// Demonstrate push()
for (int i = 0; i < 100; ++i) {
db.push({"user" + std::to_string(i), i * 10});
}

// Demonstrate len()
std::cout << "Initial size: " << db.len() << "\n";

// Demonstrate capacity()
std::cout << "Capacity: " << db.capacity() << "\n";

// Demonstrate get() and mutable access
if (const auto record = db.get(0)) {
std::cout << "First record: " << record->get().first << " -> " << record->get().second << "\n";
record->get().second = 999; // Modify
}

// Demonstrate iter() const
std::cout << "First 5 records: ";
size_t count = 0;
for (const auto& [key, val] : db.iter()) {
if (count >= 5) break;
std::cout << key << ":" << val << " ";
++count;
}
std::cout << "\n";

// Demonstrate reserve() and shrink_to_fit()
db.reserve(10000);
std::cout << "After reserve(10000), capacity: " << db.capacity() << "\n";
db.shrink_to_fit();
std::cout << "After shrink_to_fit, capacity: " << db.capacity() << "\n";

// Message examples with pattern matching for database operations
const Operation op1 = Insert{"user100", 1000};
const Operation op2 = Update{"user50", 500};
const Operation op3 = Delete{"user25"};
const Operation op4 = Select{
"user75",
[](const int value){
std::cout << "Queried value: " << value << "\n";
}};
const Operation op5 = Noop{};

// Demonstrate handler using oxide::match{}
auto db_handler = [&](const Operation& msg) {
msg >> oxide::match{
[&](const Insert& ins) {
db.push({ins.key, ins.value});
std::cout << "Inserted: " << ins.key << " -> " << ins.value << "\n";
},
[&](const Update& upd) {
bool found = false;
for (auto& [key, val] : db.iter_mut()) {
if (key == upd.key) {
val = upd.new_value;
std::cout << "Updated: " << upd.key << "=" << upd.new_value << "\n";
found = true;
break;
}
}
if (!found) std::cout << "Update failed: key not found\n";
},
[&](const Delete& del) {
bool found = false;
for (size_t i = 0; i < db.len(); ++i) {
if (const auto rec = db.get(i); rec && rec->get().first == del.key) {
auto [key, val] = db.remove(i);
std::cout << "Deleted: " << key << "=" << val << "\n";
found = true;
break;
}
}
if (!found) std::cout << "Delete failed: key not found\n";
},
[&](const Select& sel) {
bool found = false;
for (const auto& [key, val] : db.iter()) {
if (key == sel.key) {
sel.callback(val);
found = true;
break;
}
}
if (!found) std::cout << "Select failed: key not found\n";
},
[&](const Noop&) {
std::cout << "No operation performed\n";
}
};
};

// Process operations
db_handler(op1);
db_handler(op2);
db_handler(op3);
db_handler(op4);
db_handler(op5);

// Final state
std::cout << "Final database size: " << db.len() << "\n";
std::cout << "Is empty: " << (db.is_empty() ? "true" : "false") << "\n";

return 0;
}
Loading