---
name: compute_pipeline
kind: function
lang: cpp
domain: core
version: "1.0.0"
purity: pure
signature: "data_table::StageOutput data_table::compute_pipeline(const char* const* in_cells, int in_rows, int in_cols, const std::vector<std::string>& in_headers, const std::vector<ColumnType>& in_types, const std::vector<Stage>& stages)"
description: "Chains N TQL stages sequentially: output of stage i becomes input of stage i+1. Returns the final StageOutput. Empty stages returns a passthrough. No ImGui, no I/O."
tags: [tables, pipeline, tql, chain, pure, cpp-tables]
uses_functions: [compute_stage_cpp_core]
uses_types: [data_table_types_cpp_core]
returns: []
returns_optional: false
error_type: ""
imports: []
tested: true
tests:
  - "compute_pipeline empty stages returns passthrough"
  - "compute_pipeline single stage equals compute_stage"
  - "compute_pipeline two stages chain filter then group"
  - "compute_pipeline three stage chain"
  - "compute_pipeline empty table"
test_file_path: "cpp/tests/test_compute_pipeline.cpp"
file_path: "cpp/functions/core/compute_pipeline.cpp"
params:
  - name: in_cells
    desc: "Row-major cell array (in_rows * in_cols pointers). May be null when in_rows == 0."
  - name: in_rows
    desc: "Number of input rows."
  - name: in_cols
    desc: "Number of columns per row. Must match in_headers.size()."
  - name: in_headers
    desc: "Column names for the raw input. Passed as-is to stage 0."
  - name: in_types
    desc: "Declared ColumnType per column for the raw input."
  - name: stages
    desc: "Ordered list of Stage descriptors. Each stage runs on the output of the previous one. Empty = passthrough."
output: "StageOutput produced by the last stage. Owns its cell_backing. If stages is empty, returns a no-op passthrough of in_cells."
---

## Ejemplo

```cpp
#include "core/compute_pipeline.h"
using namespace data_table;

const char* raw[] = {
    "EU","A","100", "US","A","200",
    "EU","B","300", "EU","A","50"
};
std::vector<std::string> hdrs  = {"region","type","revenue"};
std::vector<ColumnType>  types = {ColumnType::String, ColumnType::String, ColumnType::Float};

// Stage 0: keep EU rows only
Stage s0;
Filter f; f.col=0; f.op=Op::Eq; f.value="EU"; s0.filters.push_back(f);

// Stage 1: group by type, sum revenue, sort desc
Stage s1;
s1.breakouts = {"type"};
Aggregation agg; agg.fn=AggFn::Sum; agg.col="revenue"; s1.aggregations.push_back(agg);
SortClause sc; sc.col="sum_revenue"; sc.desc=true; s1.sorts.push_back(sc);

StageOutput out = compute_pipeline(raw, 4, 3, hdrs, types, {s0, s1});
// out.rows == 2 (B=300, A=150), sorted desc
// out.cells[0]=="B", out.cells[1]=="300"
// out.cells[2]=="A", out.cells[3]=="150"
```

## Cuando usarla

- Cuando tienes una secuencia de transformaciones TQL (filter -> group -> filter -> sort) y quieres ejecutarlas todas en un solo call.
- Para implementar el "active pipeline" de un State con multiples stages: `compute_pipeline(raw, rows, cols, hdrs, types, state.stages)`.
- En tests de integracion de pipelines multi-stage sin renderizar nada.

## Gotchas

- Cada stage crea un `StageOutput` intermedio con su propio `cell_backing`. Los intermedios se destruyen al final de la llamada — solo el resultado final sobrevive.
- Los punteros de `out.cells` del ultimo stage pueden apuntar a backing de ese stage o a cells del input del stage anterior (passthrough rows). El caller solo necesita mantener vivo el `StageOutput` devuelto — no el `in_cells` original si hubo algun stage que agrego rows al backing.
- Si `stages` es vacio, se comporta igual que `compute_stage` con un `Stage{}` vacio (passthrough, copia punteros de `in_cells` sin copiar datos).
