Skip to content

Commit 8a13e2b

Browse files
committed
Add C++17 bindings
1 parent 936160f commit 8a13e2b

821 files changed

Lines changed: 115803 additions & 307 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/release.yml

Lines changed: 93 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -271,15 +271,11 @@ jobs:
271271
echo "WHEEL_PATH=${WHEEL_PATH}" >> "$GITHUB_ENV"
272272
273273
- name: Install built wheel
274-
if:
275-
matrix.name == 'linux-x86_64-manylinux2014' || !startsWith(matrix.os,
276-
'ubuntu')
274+
if: matrix.name == 'linux-x86_64-manylinux2014' || !startsWith(matrix.os, 'ubuntu')
277275
run: "$PYTHON_PATH" -m pip install --force-reinstall --no-deps "${{ env.WHEEL_PATH }}"
278276

279277
- name: Verify wheel import and version
280-
if:
281-
matrix.name == 'linux-x86_64-manylinux2014' || !startsWith(matrix.os,
282-
'ubuntu')
278+
if: matrix.name == 'linux-x86_64-manylinux2014' || !startsWith(matrix.os, 'ubuntu')
283279
shell: bash
284280
env:
285281
BASE_VERSION: ${{ needs.verify-tag.outputs.base_version }}
@@ -520,11 +516,19 @@ jobs:
520516
$src = "target/${{ matrix.target }}/release/${{ matrix.lib_name }}"
521517
$dst = "dist/openpit-ffi--${{ matrix.goos }}-${{ matrix.goarch }}-${{ matrix.lib_name }}"
522518
Copy-Item $src $dst -Force
519+
$implibSrc = "$src.lib"
520+
if (!(Test-Path $implibSrc)) {
521+
throw "MSVC import library not found at $implibSrc"
522+
}
523+
$implibDst = "$dst.lib"
524+
Copy-Item $implibSrc $implibDst -Force
523525
Copy-Item bindings/c/openpit.h dist/openpit.h -Force
524526
Copy-Item LICENSE dist/LICENSE -Force
525527
Copy-Item OWNERS dist/OWNERS -Force
526528
$hash = (Get-FileHash -Path $dst -Algorithm SHA256).Hash.ToLower()
527529
Set-Content -Path "$dst.sha256" -Value $hash
530+
$implibHash = (Get-FileHash -Path $implibDst -Algorithm SHA256).Hash.ToLower()
531+
Set-Content -Path "$implibDst.sha256" -Value $implibHash
528532
529533
- name: Upload runtime artifacts
530534
if: ${{ needs.verify-tag.outputs.release_mode == 'production' }}
@@ -543,12 +547,82 @@ jobs:
543547
if-no-files-found: error
544548
retention-days: 1
545549

550+
build-cpp-distributable:
551+
name: Build C++ distributable
552+
needs:
553+
- verify
554+
- verify-tag
555+
runs-on: ubuntu-22.04
556+
steps:
557+
- name: Checkout
558+
uses: actions/checkout@v4
559+
560+
- name: Load CI versions
561+
shell: bash
562+
run: cat "$GITHUB_WORKSPACE/.github/ci-versions.env" >> "$GITHUB_ENV"
563+
564+
- name: Setup Rust
565+
uses: dtolnay/rust-toolchain@stable
566+
with:
567+
toolchain: ${{ env.CI_RUST }}
568+
569+
- name: Cache Rust build
570+
uses: Swatinem/rust-cache@v2
571+
572+
- name: Setup CMake
573+
uses: jwlawson/actions-setup-cmake@v2
574+
with:
575+
cmake-version: "3.28"
576+
577+
- name: Setup just
578+
uses: extractions/setup-just@v3
579+
580+
- name: Build C++ binding
581+
shell: bash
582+
run: just build-cpp
583+
584+
- name: Install C++ binding into staging prefix
585+
shell: bash
586+
env:
587+
VERSION: ${{ needs.verify-tag.outputs.version }}
588+
run: |
589+
set -euo pipefail
590+
cmake --install bindings/cpp/build \
591+
--prefix /tmp/openpit-cpp-install \
592+
--config Release
593+
cp LICENSE /tmp/openpit-cpp-install/LICENSE
594+
cp OWNERS /tmp/openpit-cpp-install/OWNERS
595+
596+
- name: Package C++ distributable
597+
shell: bash
598+
env:
599+
VERSION: ${{ needs.verify-tag.outputs.version }}
600+
run: just --justfile "$JUSTFILE" package-cpp /tmp/openpit-cpp-install
601+
602+
- name: Upload C++ artifact
603+
if: ${{ needs.verify-tag.outputs.release_mode == 'production' }}
604+
uses: actions/upload-artifact@v4
605+
with:
606+
name: cpp-distributable
607+
path: dist/openpit-cpp--*.tar.gz*
608+
if-no-files-found: error
609+
610+
- name: Upload C++ artifact (dry-run)
611+
if: ${{ needs.verify-tag.outputs.release_mode == 'dry-run' }}
612+
uses: actions/upload-artifact@v4
613+
with:
614+
name: cpp-distributable
615+
path: dist/openpit-cpp--*.tar.gz*
616+
if-no-files-found: error
617+
retention-days: 1
618+
546619
publish-go-sources:
547620
name: Publish pit-go sources
548621
needs:
549622
- publish-rust
550623
- publish-sdist
551624
- build-runtime-artifacts
625+
- build-cpp-distributable
552626
- verify-tag
553627
runs-on: ubuntu-22.04
554628
permissions:
@@ -708,6 +782,7 @@ jobs:
708782
- publish-rust
709783
- publish-sdist
710784
- build-runtime-artifacts
785+
- build-cpp-distributable
711786
- verify-tag
712787
if: ${{ needs.verify-tag.outputs.release_mode == 'production' }}
713788
runs-on: ubuntu-22.04
@@ -724,6 +799,12 @@ jobs:
724799
path: release-assets
725800
merge-multiple: true
726801

802+
- name: Download C++ distributable artifact
803+
uses: actions/download-artifact@v4
804+
with:
805+
name: cpp-distributable
806+
path: release-assets
807+
727808
- name: Create GitHub Release with runtime assets
728809
shell: bash
729810
run: |
@@ -736,8 +817,12 @@ jobs:
736817
- crates.io: https://crates.io/crates/openpit
737818
- PyPI: https://pypi.org/project/openpit/${{ needs.verify-tag.outputs.version }}/
738819
739-
Runtime artifacts for Go SDK delivery are attached to this release
740-
(`.so`, `.dylib`, `.dll` and `.sha256` files per target).
820+
Runtime artifacts for SDK delivery are attached to this release
821+
(\`.so\`, \`.dylib\`, \`.dll\`, Windows \`.dll.lib\`, and
822+
\`.sha256\` files per target).
823+
824+
The C++ distributable (\`openpit-cpp--${{ needs.verify-tag.outputs.version }}.tar.gz\`)
825+
contains the public headers and CMake config-file package.
741826
EOF
742827
743828
mapfile -t ASSETS < <(find release-assets -type f | sort)

.github/workflows/verify.yml

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,98 @@ jobs:
128128
- name: Check C
129129
run: just --justfile "$JUSTFILE" test-c-examples
130130

131+
cpp:
132+
name: C++ checks (${{ matrix.name }})
133+
runs-on: ${{ matrix.os }}
134+
strategy:
135+
fail-fast: false
136+
matrix:
137+
include:
138+
- name: linux-gcc
139+
os: ubuntu-22.04
140+
cc: gcc
141+
cxx: g++
142+
- name: linux-clang
143+
os: ubuntu-22.04
144+
cc: clang
145+
cxx: clang++
146+
- name: macos-appleclang
147+
os: macos-14
148+
cc: clang
149+
cxx: clang++
150+
- name: windows-msvc
151+
os: windows-2022
152+
steps:
153+
- name: Checkout
154+
uses: actions/checkout@v4
155+
156+
- name: Load CI versions
157+
shell: bash
158+
run: cat "$GITHUB_WORKSPACE/.github/ci-versions.env" >> "$GITHUB_ENV"
159+
160+
- name: Setup Rust
161+
uses: dtolnay/rust-toolchain@stable
162+
with:
163+
toolchain: ${{ env.CI_RUST }}
164+
165+
- name: Cache Rust build
166+
uses: Swatinem/rust-cache@v2
167+
168+
- name: Setup CMake
169+
uses: jwlawson/actions-setup-cmake@v2
170+
with:
171+
cmake-version: "3.28"
172+
173+
- name: Setup just
174+
uses: extractions/setup-just@v3
175+
176+
- name: Install C++ tooling (Linux)
177+
if: startsWith(matrix.os, 'ubuntu')
178+
run: |
179+
sudo apt-get update
180+
sudo apt-get install --yes clang-format clang-tidy doxygen
181+
182+
- name: Install C++ tooling (macOS)
183+
if: startsWith(matrix.os, 'macos')
184+
run: brew install clang-format doxygen
185+
186+
- name: Build C++ binding
187+
shell: bash
188+
run: |
189+
if [[ -n "${{ matrix.cc }}" ]]; then
190+
export CC="${{ matrix.cc }}"
191+
export CXX="${{ matrix.cxx }}"
192+
fi
193+
just build-cpp
194+
195+
- name: Test C++ binding
196+
shell: bash
197+
run: just test-cpp
198+
199+
- name: Build and test C++ examples
200+
shell: bash
201+
run: |
202+
if [[ -n "${{ matrix.cc }}" ]]; then
203+
export CC="${{ matrix.cc }}"
204+
export CXX="${{ matrix.cxx }}"
205+
fi
206+
just test-examples-cpp
207+
208+
- name: Check C++ formatting
209+
if: ${{ matrix.os != 'windows-2022' }}
210+
shell: bash
211+
run: just fmt-check-cpp
212+
213+
- name: Generate C++ API reference
214+
if: ${{ matrix.os != 'windows-2022' }}
215+
shell: bash
216+
run: just gen-docs-cpp
217+
218+
- name: Lint C++ sources
219+
if: startsWith(matrix.os, 'ubuntu')
220+
shell: bash
221+
run: just lint-cpp
222+
131223
go:
132224
name: Go checks
133225
runs-on: ubuntu-22.04

README.md

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
[![Verify](https://github.com/openpitkit/pit/actions/workflows/verify.yml/badge.svg)](https://github.com/openpitkit/pit/actions/workflows/verify.yml) [![Release](https://github.com/openpitkit/pit/actions/workflows/release.yml/badge.svg)](https://github.com/openpitkit/pit/actions/workflows/release.yml) [![License](https://img.shields.io/badge/license-Apache%202.0-blue)](LICENSE)
66
[![Go version](https://img.shields.io/badge/go-1.22%2B-00ADD8)](https://pkg.go.dev/go.openpit.dev/openpit) [![Module](https://img.shields.io/badge/module-go.openpit.dev%2Fopenpit-00ADD8)](https://pkg.go.dev/go.openpit.dev/openpit)
77
[![Python versions](https://img.shields.io/pypi/pyversions/openpit)](https://pypi.org/project/openpit/) [![PyPI](https://img.shields.io/pypi/v/openpit)](https://pypi.org/project/openpit/)
8+
[![C++17](https://img.shields.io/badge/C%2B%2B-17-00599C)](bindings/cpp/README.md)
89
[![Rust](https://img.shields.io/badge/rust-1.75+-orange)](https://crates.io/crates/openpit) [![crates.io](https://img.shields.io/crates/v/openpit)](https://crates.io/crates/openpit)
9-
[![C API](https://img.shields.io/badge/C%20API-header%20%2B%20docs-4b5563)](docs/c-api/index.md)
10+
[![C API](https://img.shields.io/badge/C%20API-blue)](bindings/c/README.md)
1011
<!-- markdownlint-enable MD013 -->
1112

1213
OpenPit is a workspace for embeddable pre-trade risk components. Before an
@@ -67,6 +68,7 @@ policy APIs:
6768

6869
- [Go custom policies](https://github.com/openpitkit/pit/wiki/Policy-API#go-interface)
6970
- [Python custom policies](https://github.com/openpitkit/pit/wiki/Policy-API#python-interface)
71+
- [C++ custom policies](https://github.com/openpitkit/pit/wiki/Policy-API#c-interface)
7072
- [Rust custom policies](https://github.com/openpitkit/pit/wiki/Policy-API#rust-interface)
7173

7274
## Versioning Policy (Pre‑1.0)
@@ -87,9 +89,10 @@ constraints that tolerate API evolution during the pre-stable phase.
8789
- [Go SDK README](bindings/go/README.md) - integrate OpenPit from Go.
8890
- [Python SDK README](bindings/python/README.md) - the `openpit` Python
8991
package.
92+
- [C++ SDK README](bindings/cpp/README.md) - C++17 CMake package.
9093
- [`openpit` crate README](crates/openpit/README.md) - Rust interface with a
9194
runnable example.
92-
- [C SDK README](bindings/c/README.md) - C ABI for environments that
95+
- [C SDK README](bindings/c/README.md) - C interface for environments that
9396
integrate through C.
9497
- [examples/](examples/) - end-to-end runnable scenarios.
9598
- [Wiki](https://github.com/openpitkit/pit/wiki) - conceptual pages and
@@ -103,6 +106,9 @@ constraints that tolerate API evolution during the pre-stable phase.
103106
- Python `>=3.10` if you build or test Python bindings
104107
- `maturin` and `pytest` if you build or test Python bindings
105108
- Go `1.22` if you build or test Go bindings
109+
- CMake `>=3.21`, a C++17 compiler, `clang-format`, `clang-tidy`, and
110+
`doxygen` if you build, test, format, lint, or generate C++ bindings,
111+
examples, and reference docs
106112

107113
```bash
108114
python3.10 -m venv .venv
@@ -159,6 +165,28 @@ Go tests then expect the path to that library through
159165
development inside the `pit` repository - consumers installing the SDK with
160166
`go get` do not need to set it.
161167

168+
#### C++
169+
170+
The C++ binding is a header-only C++17 SDK. In-repo builds link it against the
171+
locally built native runtime:
172+
173+
```bash
174+
just build-cpp
175+
just build-examples-cpp
176+
```
177+
178+
Manual:
179+
180+
```bash
181+
cargo build -p openpit-ffi --release --locked
182+
cmake -S bindings/cpp -B bindings/cpp/build \
183+
-DOPENPIT_RUNTIME_LIBRARY="$PWD/target/release/libopenpit_ffi.dylib"
184+
cmake --build bindings/cpp/build
185+
```
186+
187+
On Linux, use `libopenpit_ffi.so` instead of `libopenpit_ffi.dylib`. On
188+
Windows, use `openpit_ffi.dll`.
189+
162190
### Tests
163191

164192
With [Just](https://just.systems/):
@@ -178,6 +206,10 @@ just test-python-integration
178206
# Go:
179207
just test-go
180208
just test-go-race
209+
210+
# C++:
211+
just test-cpp
212+
just test-examples-cpp
181213
```
182214

183215
Manual:
@@ -205,4 +237,12 @@ export OPENPIT_RUNTIME_LIBRARY_PATH="$(pwd)/../../target/release/libopenpit_ffi.
205237
# macOS: use libopenpit_ffi.dylib instead.
206238
go test ./...
207239
go test -race ./...
240+
241+
# C++:
242+
cd ../..
243+
cargo build -p openpit-ffi --release --locked
244+
cmake -S bindings/cpp -B bindings/cpp/build \
245+
-DOPENPIT_RUNTIME_LIBRARY="$PWD/target/release/libopenpit_ffi.dylib"
246+
cmake --build bindings/cpp/build
247+
ctest --test-dir bindings/cpp/build --output-on-failure
208248
```

bindings/c/README.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# OpenPit (Pre-trade Integrity Toolkit) for C
22

33
<!-- markdownlint-disable MD013 -->
4-
[![Verify](https://github.com/openpitkit/pit/actions/workflows/verify.yml/badge.svg)](https://github.com/openpitkit/pit/actions/workflows/verify.yml) [![Release](https://github.com/openpitkit/pit/actions/workflows/release.yml/badge.svg)](https://github.com/openpitkit/pit/actions/workflows/release.yml) [![C API](https://img.shields.io/badge/C%20API-header%20%2B%20docs-4b5563)](../../docs/c-api/index.md) [![License](https://img.shields.io/badge/license-Apache%202.0-blue)](../../LICENSE)
4+
[![Verify](https://github.com/openpitkit/pit/actions/workflows/verify.yml/badge.svg)](https://github.com/openpitkit/pit/actions/workflows/verify.yml) [![Release](https://github.com/openpitkit/pit/actions/workflows/release.yml/badge.svg)](https://github.com/openpitkit/pit/actions/workflows/release.yml) [![C API](https://img.shields.io/badge/C%20API-blue)](../../docs/c-api/index.md) [![License](https://img.shields.io/badge/license-Apache%202.0-blue)](../../LICENSE)
55
<!-- markdownlint-enable MD013 -->
66

77
`openpit.h` is an embeddable pre-trade risk SDK entrypoint for integrating
@@ -112,7 +112,7 @@ Built-in policies:
112112
- plus your own via the [policy SDK](https://github.com/openpitkit/pit/wiki/Policy-API).
113113

114114
The primary integration model is to build project-specific policies against the
115-
public C API: [the C API docs](https://github.com/openpitkit/pit/blob/main/docs/c-api/index.md).
115+
public C API: [the C API docs](../../docs/c-api/index.md).
116116

117117
Two types of rejections are supported: a full kill switch for the account and a
118118
rejection of only the current request. Kill switches are intended for
@@ -493,7 +493,7 @@ Example flow:
493493
7. apply one execution report built as a POD payload
494494
8. inspect whether post-trade state triggered a kill switch
495495
496-
For the full type and ownership reference, use the generated C manual:
496+
For the full type and ownership reference, use the C manual:
497497
[docs/c-api/index.md](https://github.com/openpitkit/pit/blob/main/docs/c-api/index.md).
498498
499499
## Errors
@@ -510,8 +510,7 @@ Input validation errors and API misuse are reported through two channels:
510510
message with `openpit_shared_string_view()`, then release with
511511
`openpit_destroy_shared_string()`
512512
- read `OpenPitPretradeRejectCode` for stable machine-readable business reject categories
513-
- use the generated C docs for ownership and lifetime rules of every returned
514-
pointer
513+
- use the C docs for ownership and lifetime rules of every returned pointer
515514
516515
The example above uses both channels:
517516

0 commit comments

Comments
 (0)