C++ API Documentation

Standalone C++17 Library for NumPy .npy Files

Read and write NumPy .npy files directly from modern C++17. syNumpy gives native applications a lightweight way to exchange array data with Python and NumPy-based pipelines.

minimal_example.cpp
C++17
#include "synumpy.hpp"
#include <vector>

int main() {
    // Save an array to disk directly
    std::vector<float> values = {1.0f, 2.0f, 3.0f};
    syNumpy::saveNpy("floats.npy", values);

    // Load an array from disk
    syNumpy::NpyArray arr = syNumpy::loadNpy("floats.npy");
    std::vector<float> roundtrip = arr.asVector<float>();

    return roundtrip.size() == 3 ? 0 : 1;
}
PixLab logo

Version 1.9.7 Source Code ↗

What is syNumpy?

syNumpy is a standalone C++17 library from Symisc Systems ↗ for reading and writing NumPy .npy files.

It gives native C++ applications a straightforward way to exchange array data with Python-based workflows without pulling in a large runtime dependency chain. The library is released under the 3-Clause BSD License, and the source code is available on GitHub ↗.

Production Usage

syNumpy is used internally by FACEIO ↗ for facial feature extraction workflows and by PixLab systems behind the DocScan mobile app, internal visual search and document processing workflows, the PixLab API endpoints, and the DOCSCAN API for passports, driver licenses, visas, residence permits, and other identity documents.


Core Features

syNumpy focuses on reliable .npy support for scalar numeric and complex dtypes. The main parser entry point is syNumpy::loadNpyBuffer(), the file APIs are thin wrappers around that parser, and malformed headers or truncated payloads fail explicitly.

NpyArray Container

Stores shape metadata, dtype descriptors, Fortran-order flags, and raw payload bytes in a compact in-memory container.

Flexible Loading

Use syNumpy::loadNpy() for file input, or syNumpy::loadNpyBuffer() to parse an in-memory NumPy payload directly.

Typed Saving

syNumpy::saveNpyRaw() exposes explicit dtype control, while saveNpy() overloads cover common native C++ workflows with less setup.

Validation & Append Mode

Set mode = "a" to extend axis 0. Headers, endianness, and trailing dimensions are validated before append operations proceed.


Build & Integration

The simplest integration path is to copy synumpy.hpp and synumpy.cpp into your source tree and build them with your existing C++17 target.

  • Public files synumpy.hpp, synumpy.cpp
  • Language requirement C++17
  • Runtime dependencies None
  • Alternative build files CMakeLists.txt, Makefile

Minimal Example

example.cpp
C++17
#include "synumpy.hpp"
#include <vector>

int main() {
    // Save an array to disk directly
    std::vector<float> values = {1.0f, 2.0f, 3.0f};
    syNumpy::saveNpy("floats.npy", values);

    // Load an array from disk
    syNumpy::NpyArray arr = syNumpy::loadNpy("floats.npy");
    std::vector<float> roundtrip = arr.asVector<float>();

    return roundtrip.size() == 3 ? 0 : 1;
}

C++17 API Reference Guide

The following reference covers the current public interface exported by synumpy.hpp.

Version Constants

Symbols
syNumpy::kVersionMajor syNumpy::kVersionMinor syNumpy::kVersionPatch syNumpy::kVersionString
Returns
Compile-time constants that identify the library version.

class syNumpy::Error

Signature
explicit Error(const std::string& message)
Description
Exception type thrown by syNumpy when parsing, validation, dtype checks, or file I/O fails.
Params
message - The exception text.
Returns
A std::runtime_error-derived exception object used for parse, validation, and I/O failures.

class syNumpy::NpyArray

Constructors
NpyArray() = default NpyArray(std::vector<std::size_t> shape, std::string dtype_descr, bool fortran_order)
Description
Owns a fully materialized .npy array in memory together with its shape, dtype metadata, Fortran-order flag, and payload bytes.
Params
shape, dtype_descr, and fortran_order for the materializing constructor.
Returns
An in-memory NumPy array object containing metadata and payload bytes.
Methods
  • shape(), dtypeDescr(), wordSize(), fortranOrder(), numValues(), and numBytes() expose array metadata.
  • bytes() returns raw payload bytes.
  • is<T>(), data<T>(), and asVector<T>() provide typed access and throw syNumpy::Error on dtype mismatch.

function syNumpy::loadNpyBuffer()

Signature
NpyArray loadNpyBuffer(const void* buffer, std::size_t size)
Description
Parses a complete NumPy .npy image that is already loaded in memory. This is the core parser entry point used by the file-loading path.
Params
buffer - A pointer to the first byte of a complete .npy image.
size - The total available byte count.
Returns
A fully populated syNumpy::NpyArray.
Throws
syNumpy::Error on malformed headers, truncated payloads, unsupported dtypes, or unsupported endianness.

function syNumpy::loadNpy()

Signature
NpyArray loadNpy(const std::filesystem::path& path)
Description
Opens a NumPy .npy file from disk, reads its contents, and returns the parsed array as a syNumpy::NpyArray.
Params
path - The filesystem path to a .npy file.
Returns
A fully populated syNumpy::NpyArray.

function syNumpy::saveNpyRaw()

Signature
void saveNpyRaw(const std::filesystem::path& path, const void* data, std::size_t byte_count, const std::vector<std::size_t>& shape, std::string_view dtype_descr, std::string_view mode = "w")
Description
Writes a .npy file from raw bytes and an explicit NumPy dtype descriptor. It is the low-level save path behind the typed overloads and also supports compatible append mode.
Params
path, data, byte_count, shape, and dtype_descr.
Returns
None
Notes
Use mode = "w" or "wb" to overwrite or create, and mode = "a" or "ab" to append along axis 0 when the existing file is compatible.

function syNumpy::saveNpy() Typed Overloads

Signatures
template <typename T> void saveNpy(const std::filesystem::path& path, const T* data, const std::vector<std::size_t>& shape, std::string_view mode = "w") template <typename T> void saveNpy(const std::filesystem::path& path, const std::vector<T>& data, const std::vector<std::size_t>& shape, std::string_view mode = "w") template <typename T> void saveNpy(const std::filesystem::path& path, const std::vector<T>& data, std::string_view mode = "w")
Description
Convenience overloads that infer the NumPy dtype from native C++ types and save pointer-backed, shaped, or one-dimensional vector data without requiring an explicit dtype descriptor.
Params
Typed payload data, a target path, and optionally an explicit shape.
Returns
None
Notes
The shaped vector overload requires data.size() to match the element count implied by the provided shape. The vector-only overload automatically infers a one-dimensional shape equal to {data.size()}.

Supported Native Types

Mappings
Supported mappings include bool, 8/16/32/64-bit signed and unsigned integers, float, double, long double, and std::complex<T> for the supported floating-point scalar families.

Full Usage Example

The following example combines direct integration, save and load round-trips, explicit shapes, buffer parsing, and append mode in one place.

full_example.cpp
C++17
#include "synumpy.hpp"

#include <cstdint>
#include <fstream>
#include <iterator>
#include <vector>

int main() {
    // 1. Save and Load flat vector
    std::vector<float> embeddings = {0.12f, 0.44f, 0.91f, 1.37f};
    syNumpy::saveNpy("embeddings.npy", embeddings);

    syNumpy::NpyArray loaded = syNumpy::loadNpy("embeddings.npy");
    std::vector<float> roundtrip = loaded.asVector<float>();

    // 2. Saving shaped matrices
    std::vector<std::int32_t> matrix = {
        10, 20, 30,
        40, 50, 60
    };
    syNumpy::saveNpy("matrix.npy", matrix, {2, 3});

    // 3. Loading from an in-memory buffer
    std::ifstream input("embeddings.npy", std::ios::binary);
    std::vector<std::uint8_t> bytes{
        std::istreambuf_iterator<char>(input),
        std::istreambuf_iterator<char>()
    };
    syNumpy::NpyArray from_buffer = syNumpy::loadNpyBuffer(bytes.data(), bytes.size());

    // 4. Append Mode overloads
    syNumpy::saveNpy("timeline.npy", std::vector<float>{1.0f, 2.0f, 3.0f});
    syNumpy::saveNpy("timeline.npy", std::vector<float>{4.0f, 5.0f}, "a");

    return roundtrip.size() == 4 && from_buffer.numValues() == 4 ? 0 : 1;
}