C++ API Documentation

A Modern C++17 Library for NumPy Arrays

A standalone library for reading and writing NumPy .npy files. It offers native file I/O for NumPy arrays, with seamless integration into existing C++ codebases.

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 ↗ designed for reading and writing NumPy .npy files.

It gives C++ applications a clean and efficient way to exchange numerical arrays with Python-based workflows, while remaining easy to integrate into existing native codebases. The library is released under the 3-Clause BSD License, and the source code is hosted on GitHub ↗.

Production Usage

syNumpy is used internally by FACEIO ↗ for facial features extraction workflows and by PixLab production systems behind the DocScan Mobile App, PixLab internal visual-search and document-processing workflows exposed through the PixLab API Endpoints, and the DOCSCAN API endpoint for scanning passports, driver licenses, visas, residence permits, and other identity documents issued around the world.


Core Features

syNumpy focuses on robust .npy support for scalar numeric and complex dtypes. The core parser entry point is syNumpy::loadNpyBuffer(), the file APIs are thin wrappers on top of that parser, and malformed headers or truncated payloads are rejected explicitly.

NpyArray Container

Stores shape dimensions, dtype descriptors, Fortran-order flags, and the raw payload bytes efficiently in memory.

Flexible Loading

Use syNumpy::loadNpy() to read from disk, or syNumpy::loadNpyBuffer() to parse an in-memory NumPy dataset directly.

Typed Saving

syNumpy::saveNpyRaw() allows explicit dtype descriptors, while saveNpy() overloads handle common native C++ workflows automatically.

Validation & Append Mode

Set mode = "a" to extend axis 0. It rigorously validates headers, endianness, and trailing dims before appending.


Build & Integration

The simplest integration path is to copy synumpy.hpp and synumpy.cpp into your source tree and compile 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 describes 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 covers direct integration, save/load round-trips, explicit shapes, buffer parsing, and append mode combined 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;
}