Skip to content

Proposal: Create SWIG Bindings Repository for AQNWB C++ API #195

@oruebel

Description

@oruebel

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    category: proposalproposed enhancements or new features

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions