C++ Quickstart

C++ components are the right choice when latency or throughput matters. They link against pipeml, the C++ runtime that ships with Pipelogic — geometry, image, tensor, detection mixins, Triton inference client.

Prerequisites

You don't need Docker, you don't need to install the SDK. ppl release ships your sources to the platform; the platform builds the container image remotely with the 2-ml build system and links pipeml in.

1. Scaffold the component

mkdir threshold-image && cd threshold-image
mkdir src
touch component.yml src/main.cpp

ppl init will set up the workspace metadata for you in step 5.

2. Write component.yml

name: "Threshold image"
language: cpp
platform: linux/amd64
build_system: 2-ml
tags: ["latest", "default"]
worker:
  input_type: Image
  output_type: Image
  config_schema:
    threshold:
      type: UInt64
      default: 128
      description: "Pixel intensity threshold (0–255)."

build_system: 2-ml is the standard C++ ML runtime. It gives you OpenCV 4, FFmpeg, GStreamer, gRPC, the Triton client, and pipeml — no extra setup.

3. Write the worker

src/main.cpp:

#include <opencv2/imgproc.hpp>

#include "pipelogic/cv/image.hpp"
#include "pipelogic/pipelogic.hpp"

using namespace ppl;

struct Params {
  uint64_t threshold;

  Params() {
    auto cfg = parse_config();
    threshold = read_config(cfg, "threshold").as<UInt64>();
  }
};

PIPELOGIC_MAIN() {
  Params params;

  run([&params](ocv::Image img) -> ocv::Image {
    img.convert(ocv::ColorSpace::GRAY);
    cv::Mat& m = img.mutable_mat();
    cv::threshold(m, m, params.threshold, 255, cv::THRESH_BINARY);
    return img;
  });
}

What's happening:

  • ocv::Image is pipeml's high-level image wrapper. It carries the color-space tag and gives you direct access to the backing cv::Mat.
  • img.convert(ColorSpace::GRAY) is in-place and a no-op when the image is already GRAY — no need to branch on color_space().
  • img.mutable_mat() returns a mutable reference to the underlying cv::Mat, so cv::threshold(m, m, …) writes back into the same buffer — no per-tick allocation, no extra Image construction.
  • parse_config() and read_config(...) pull config_schema values out of component.yml.
  • PIPELOGIC_MAIN() is the entry-point macro. It expands into the appropriate int main(...) plus runtime init.
  • run(worker) blocks until the backend closes.

You never construct pipelang values by hand — the typed wrapper classes (ocv::Image, ocv::BoundingBox, ocv::AudioFrame, etc.) do it for you.

4. Optional: extra build flags

For most components nothing else is needed. If your component has additional system dependencies, list them in component.yml:

xmake_packages:
  - eigen
  - sentencepiece

If you need apt packages baked into the runtime image, add a Dockerfile-base:

RUN apt-get update && apt-get install -y \
    libtesseract-dev \
    && rm -rf /var/lib/apt/lists/*

If you need apt packages only at build time, use Dockerfile-build. Avoid build_system: custom unless you really need to override the entire build — both Dockerfile-base and Dockerfile-build work alongside build_system: 2-ml.

5. Initialize and release

ppl login
ppl init
ppl release

ppl release ships your sources; the platform compiles main.cpp with C++23, links pipeml, packages the resulting binary into a runtime image, and registers the new version in your workspace. Build time is typically 1–3 minutes.

6. Use it in a backend

In app.pipelogic.ai, wire up:

  1. Input image file → your Threshold imageOutput browser video.

Click Run. The web UI shows a live preview and lets you adjust the threshold parameter on the fly via config.sync.

What's next

Was this page helpful?