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
37 changes: 37 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,21 @@ add_executable(${PROJECT_NAME}
set_target_properties(${PROJECT_NAME} PROPERTIES LINKER_LANGUAGE CXX)
target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_20)

# C Interface shared library
add_library(${PROJECT_NAME}_c SHARED
src/notifly_c.cpp
)

set_target_properties(${PROJECT_NAME}_c PROPERTIES
LINKER_LANGUAGE CXX
OUTPUT_NAME "notifly_c"
VERSION 1.0.0
SOVERSION 1
)

target_compile_features(${PROJECT_NAME}_c PUBLIC cxx_std_20)
target_include_directories(${PROJECT_NAME}_c PUBLIC include)

include(FetchContent)

#
Expand Down Expand Up @@ -53,6 +68,28 @@ set_target_properties(${UNIT_TEST}
MSVC_RUNTIME_LIBRARY MultiThreaded$<$<CONFIG:Debug>:Debug>
)

# C Interface test
set(C_TEST notifly_c_test)
add_executable(${C_TEST} test/test_c_interface.c)
target_link_libraries(${C_TEST} PRIVATE ${PROJECT_NAME}_c)
target_include_directories(${C_TEST} PRIVATE include)
set_target_properties(${C_TEST}
PROPERTIES
OUTPUT_NAME ${C_TEST}
LINKER_LANGUAGE C
)

# C Interface example
set(C_EXAMPLE notifly_c_example)
add_executable(${C_EXAMPLE} example/c_example.c)
target_link_libraries(${C_EXAMPLE} PRIVATE ${PROJECT_NAME}_c)
target_include_directories(${C_EXAMPLE} PRIVATE include)
set_target_properties(${C_EXAMPLE}
PROPERTIES
OUTPUT_NAME ${C_EXAMPLE}
LINKER_LANGUAGE C
)

# Function to copy a file only if it does not exist or is different
function(move_file source_file destination_dir)
# Check if the source file exists
Expand Down
32 changes: 31 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,37 @@ This project was originally forked from https://github.com/Geenz/CPP-Notificatio

A C++ API inspired by Cocoa's NSNotificationCenter API.

## Usage
## C Interface

Notifly now includes a **C interface** that provides access to the notification center functionality from C programs through a shared library (DLL/SO). This allows you to use Notifly from C projects while maintaining the performance and features of the C++ implementation.

**Key features of the C interface:**
- Shared library (`libnotifly_c.so`/`notifly_c.dll`) for easy integration
- Handle-based API for type safety
- Function pointer callbacks
- Synchronous and asynchronous notification posting
- Full compatibility with the C++ API functionality

See [docs/C_INTERFACE.md](docs/C_INTERFACE.md) for complete documentation and examples.

**Quick C example:**
```c
#include "notifly_c.h"

void my_callback(int notification_id, void* data, void* user_data) {
printf("Received notification %d\n", notification_id);
}

int main() {
notifly_handle notifly = notifly_default();
int observer_id = notifly_add_observer(notifly, 1001, my_callback, NULL);
notifly_post_notification(notifly, 1001, NULL);
notifly_remove_observer(notifly, observer_id);
return 0;
}
```

## C++ API Usage

Using `notifly` is simple. In order to use the default center, simply use the static
method `notifly::default_notifly()` like so:
Expand Down
186 changes: 186 additions & 0 deletions docs/C_INTERFACE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
# Notifly C Interface Documentation

This document describes the C interface for the Notifly notification center library.

## Overview

The C interface provides access to Notifly functionality from C programs through a shared library (`libnotifly_c.so` on Linux, `notifly_c.dll` on Windows). The interface wraps the C++ API with a C-compatible API using:

- Opaque handle-based design for type safety
- Function pointer callbacks instead of C++ std::function
- Simple void pointer data payloads
- Standard C error codes

## API Reference

### Types

```c
typedef struct notifly_instance* notifly_handle;
typedef void (*notifly_callback)(int notification_id, void* data, void* user_data);

typedef enum {
NOTIFLY_SUCCESS = 0,
NOTIFLY_OBSERVER_NOT_FOUND = -1,
NOTIFLY_NOTIFICATION_NOT_FOUND = -2,
NOTIFLY_PAYLOAD_TYPE_NOT_MATCH = -3,
NOTIFLY_NO_MORE_OBSERVER_IDS = -4,
NOTIFLY_INVALID_HANDLE = -5
} notifly_result_t;
```

### Instance Management

```c
// Create a new notifly instance
notifly_handle notifly_create(void);

// Destroy a notifly instance (only for instances created with notifly_create)
void notifly_destroy(notifly_handle handle);

// Get the default global notifly instance
notifly_handle notifly_default(void);
```

### Observer Management

```c
// Add an observer for a specific notification
int notifly_add_observer(notifly_handle handle, int notification_id,
notifly_callback callback, void* user_data);

// Remove a specific observer by ID
int notifly_remove_observer(notifly_handle handle, int observer_id);

// Remove all observers for a notification
int notifly_remove_all_observers(notifly_handle handle, int notification_id);
```

### Notification Posting

```c
// Post a notification synchronously
int notifly_post_notification(notifly_handle handle, int notification_id, void* data);

// Post a notification asynchronously
int notifly_post_notification_async(notifly_handle handle, int notification_id, void* data);
```

### Utility Functions

```c
// Convert error code to human-readable string
const char* notifly_result_to_string(int result);
```

## Usage Example

```c
#include "notifly_c.h"
#include <stdio.h>

// Callback function
void my_callback(int notification_id, void* data, void* user_data) {
printf("Received notification %d\n", notification_id);
if (data) {
int* value = (int*)data;
printf("Data: %d\n", *value);
}
}

int main() {
// Get default instance
notifly_handle notifly = notifly_default();

// Add observer
int observer_id = notifly_add_observer(notifly, 1001, my_callback, NULL);
if (observer_id <= 0) {
printf("Failed to add observer: %s\n", notifly_result_to_string(observer_id));
return 1;
}

// Post notification
int data = 42;
int result = notifly_post_notification(notifly, 1001, &data);
if (result <= 0) {
printf("Failed to post notification: %s\n", notifly_result_to_string(result));
return 1;
}

printf("Notification sent to %d observers\n", result);

// Cleanup
notifly_remove_observer(notifly, observer_id);

return 0;
}
```

## Building

The shared library is built using CMake:

```bash
mkdir build && cd build
cmake ..
make
```

This creates:
- `libnotifly_c.so` (Linux) / `notifly_c.dll` (Windows) - the shared library
- `notifly_c_test` - C interface unit tests
- `notifly_c_example` - C interface usage example

## Linking

To use the C interface in your project:

### CMake
```cmake
find_library(NOTIFLY_C_LIBRARY notifly_c)
target_link_libraries(your_target ${NOTIFLY_C_LIBRARY})
target_include_directories(your_target PRIVATE /path/to/notifly/include)
```

### Direct compilation
```bash
gcc -o my_program my_program.c -lnotifly_c -I/path/to/notifly/include
```

## Memory Management

- **Handles**: The default handle (`notifly_default()`) should never be destroyed. Only destroy handles created with `notifly_create()`.
- **Data**: The library does not take ownership of data passed to `notifly_post_notification()`. Ensure data remains valid during synchronous calls.
- **User data**: User data passed to `notifly_add_observer()` must remain valid until the observer is removed.

## Thread Safety

The C interface inherits the thread safety characteristics of the underlying C++ implementation:
- Multiple threads can safely add/remove observers and post notifications
- Callbacks may be invoked from different threads when using async notifications
- No additional locking is required in user code

## Error Handling

All functions return integer results:
- Positive values: Success (usually count of affected observers)
- Zero: Success with no side effects
- Negative values: Error codes (see `notifly_result_t` enum)

Use `notifly_result_to_string()` to get human-readable error descriptions.

## Limitations

1. **Type Safety**: Unlike the C++ API, the C interface uses void pointers for data, sacrificing compile-time type checking for simplicity.
2. **Templates**: The C++ template-based type validation is not available in C.
3. **Complex Data**: Only simple data structures should be passed through the void pointer interface.

## Migration from C++ API

| C++ API | C API |
|---------|-------|
| `notifly::default_notifly()` | `notifly_default()` |
| `add_observer(id, callback)` | `notifly_add_observer(handle, id, callback, user_data)` |
| `remove_observer(id)` | `notifly_remove_observer(handle, id)` |
| `post_notification(id, args...)` | `notifly_post_notification(handle, id, &data)` |
| `post_notification_async(id, args...)` | `notifly_post_notification_async(handle, id, &data)` |
Loading
Loading