-
Notifications
You must be signed in to change notification settings - Fork 0
Description
This is a proposal for creating SWIG bindings to the aqnwb C++ API. The steps in this proposal were generated with AI. The goal of this Issue is mainly to initiate a discussion for potential integration with C# (e.g., used by Bonsai), and other languages. However, the actual implementation will need further iteration and should occur in a separate Git repo.
🎯 Goals
- Provide automated interop with AQNWB C++ classes
- Preserve macro-driven design (e.g.,
DEFINE_FIELD,DEFINE_REGISTERED_FIELD) - Avoid rewriting accessors manually
- Expose clean, language-specific API surfaces via SWIG
- Support C#, Python, and Java with consistent tooling
- Stay compatible with schema evolution via automation
🧰 Implementation Strategy
1. Preprocess Macro-Rich Headers
Use the compiler’s preprocessor (clang -E) to expand macro-generated functions. This produces intermediate headers (e.g., ElectricalSeries_expanded.hpp) with complete method declarations that SWIG can parse.
clang -E OpticalSeries.hpp -o OpticalSeries_expanded.hpp➡️ Exposes implicit methods to SWIG
➡️ Keeps macro logic untouched in upstream source
2. Auto-Extract Field Macro Lists
Instead of manually maintaining .def files, a script will parse expanded headers and extract field accessor declarations.
📄 Example output (OpticalSeriesFields.def):
X(data, BaseRecordingData)
X(timestamps, BaseRecordingData)
X(wavelength, BaseRecordingData)🔧 Script: scripts/generate_field_list.py
➡️ Centralizes field metadata
➡️ Eliminates duplication
➡️ Enables reuse across headers, implementations, and bindings
3. Auto-Generate SWIG Interface Files
Use the extracted field metadata to generate .i files per class and language target.
📄 Example: bindings/csharp/OpticalSeries.i
%module aqnwb_optical
%{
#include "generated/OpticalSeries_expanded.hpp"
%}
%include "generated/OpticalSeries_expanded.hpp"
%include <std_shared_ptr.i>
%shared_ptr(BaseRecordingData)➡️ Keeps bindings modular
➡️ Language-specific folders support expansion
4. Multi-Language CMake Build
Use swig_add_library() to generate bindings for C#, Python, and Java. Each target shares preprocessing and uses its own .i file.
📄 CMake example:
swig_add_library(aqnwb_optical_csharp LANGUAGE csharp SOURCES bindings/csharp/OpticalSeries.i)
swig_add_library(aqnwb_optical_python LANGUAGE python SOURCES bindings/python/OpticalSeries.i)
add_dependencies(aqnwb_optical_csharp OpticalSeriesPP)
add_dependencies(aqnwb_optical_python OpticalSeriesPP)➡️ Shared source, multi-language output
➡️ One build script to rule them all
5. Typemaps and Smart Pointer Support
SWIG typemaps ensure pointer-based APIs behave idiomatically in the target language.
%include <std_shared_ptr.i>
%shared_ptr(BaseRecordingData)
%shared_ptr(Types::LightSourceInfo)➡️ Provides managed access to C++ heap data
➡️ Avoids raw pointer leakage
6. Usage and Extensibility Docs
Create a comprehensive README.md:
- How to build for each language
- How to import generated modules (e.g.,
aqnwb.dll,aqnwb.so) - How to regenerate bindings from schema
🧬 Proposed Repo Structure
swig-bindings/
├── CMakeLists.txt
├── README.md
├── generated/
│ └── *.hpp (expanded headers)
├── scripts/
│ └── generate_field_list.py
├── bindings/
│ ├── csharp/
│ │ └── *.i
│ ├── python/
│ │ └── *.i
│ └── java/
│ └── *.i
└── swig_helpers/
└── typemaps.i
🔮 Future Enhancements
- Auto-generate bindings across all schema classes from a JSON/YAML schema
- Add GitHub Actions CI for multi-language builds and packaging
- Provide NuGet packages (C#), PyPI wheels (Python), and JARs (Java)
- Add test coverage per binding surface
- Auto-generate documentation from field metadata