Skip to main content

codegen-cpp

codegen-cpp is a C++ code generation utility written in Python.

It can be used to genereate C++ code from TOML specification files. Presently it supports generation Struct-of-Array data structures and code to read those tables to and from CSV and Parquet files using Apache Arrow.

Requirements

codegen-cpp itself needs Python 3.12 or later. The code it generates needs a C++23 compiler and Apache Arrow built with the CSV and Parquet support.

Installation

python -m venv .venv
.venv/bin/pip install .

Usage

Write a specification, for example spec.toml:

[[table]]
name = "Measurement"
columns = [
    { name = "station_id", type = "i64" },
    { name = "temperature", type = "f64" },
    { name = "note", type = "str" },
]

[[csv_reader]]
name = "MeasurementCsvReader"
table = "Measurement"
default_values = { note = "" }

[[parquet_writer]]
name = "MeasurementParquetWriter"
table = "Measurement"

Generate the headers:

codegen-cpp generate spec.toml --output-dir include

This writes one header per section, named after the section: include/Measurement.hpp, include/MeasurementCsvReader.hpp, and include/MeasurementParquetWriter.hpp.

Use them to convert a CSV file into a Parquet file:

#include "MeasurementCsvReader.hpp"
#include "MeasurementParquetWriter.hpp"

int main() {
    MeasurementCsvReader reader("measurements.csv.gz", 100000);
    MeasurementParquetWriter writer("measurements.parquet");

    while (reader.has_more_batches()) {
        const Measurement batch = reader.read_batch();
        writer.write_batch(batch);
    }

    writer.close();
    return 0;
}

examples/example1.toml is an exemplar specification that shows features of the specification format.. To see how a specification is parsed, without generating anything:

codegen-cpp debug parse-spec examples/example1.toml

The specification

A specification is a TOML document holding any number of sections of five kinds. Every section is an array of tables, written [[table]], [[csv_reader]], and so on.

Section What it generates
table the struct holding the rows
csv_reader a class reading the table from a CSV file
parquet_reader a class reading the table from a Parquet file
csv_writer a class writing the table to a CSV file
parquet_writer a class writing the table to a Parquet file

Every section has a name, which is used verbatim as the name of the generated class and of the header file holding it. The names of all sections share one namespace and have to be unique. Every reader and writer names the table it reads into or writes out.

A table declares its columns, each with a name used verbatim as a C++ member name, and one of the scalar types:

Type C++ type
i8, i16, i32, i64 std::int8_t ... std::int64_t
u8, u16, u32, u64 std::uint8_t ... std::uint64_t
f32, f64 float, double
bool bool
str std::string

Readers may declare default_values, a mapping of column names to the value stored when that column is null in the input file. A null in any other column is an error. The value has to fit the type of its column.

The generated code

For a table called Measurement, Measurement.hpp defines two structs. MeasurementRow holds a single row by value, and Measurement holds the rows column by column, one std::vector per column:

struct Measurement {
    std::vector<std::int64_t> station_id;
    std::vector<double> temperature;
    std::vector<std::string> note;

    std::size_t size() const noexcept;
    void clear() noexcept;
    void reserve(std::size_t n);
    void push_back(const MeasurementRow& row);
    void push_back(const std::int64_t& station_id_,
                   const double& temperature_,
                   const std::string& note_);
    MeasurementRow operator[](std::size_t i) const;
};

All the columns of a table have the same length, which is what size() reports. operator[] returns a copy of a row, because the rows are not stored as rows.

Readers hand out one batch of rows at a time, and writers take one batch at a time:

bool has_more_batches();       // readers
Table read_batch();            // readers, at most batch_size rows

void write_batch(const Table& table);  // writers
void close();                          // writers

A writer replaces the file it opens if it already exists. close() writes out what is left and releases the file; calling it twice is allowed. The destructor closes the file as well, but only close() reports a failure to write.

The constructors take the path of the file, and readers also take the number of rows per batch. The remaining arguments are optional:

Class Optional arguments
csv_reader use_threads (false), block_size (128 MB), compression (guessed)
parquet_reader none
csv_writer compression (guessed), compression_level (the codec's default)
parquet_writer compression (Zstandard), compression_level, row_group_length (128000)

For the CSV classes, the compression is guessed from the suffix of the file name, so .gz, .zst, .bz2 and .lz4 are compressed and everything else is plain text. Passing a codec explicitly overrides the guess. Parquet files carry their compression inside them, so the Parquet reader needs no such argument.

Development

Install the package and its development dependencies in a virtual environment, and run the tests:

python -m venv .venv
.venv/bin/pip install -e ".[dev]"
.venv/bin/pytest

The project is formatted with black, checked with pycodestyle and pyright.

Testing the generated C++ code

The tests under tests/cpp generate headers, write CSV and Parquet files with Arrow, and read them back with the generated readers. Arrow is installed with Conan; see conanfile.txt for the features it is built with. Note that Arrow needs C++20 or later, which the default Conan profile does not ask for.

conan install . --build=missing -of build -s compiler.cppstd=20
cmake -S tests/cpp -B build/cpp \
    -DCMAKE_TOOLCHAIN_FILE="$PWD/build/build/Release/generators/conan_toolchain.cmake" \
    -DCMAKE_BUILD_TYPE=Release
cmake --build build/cpp
ctest --test-dir build/cpp --output-on-failure

License

MIT, see LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

codegen_cpp-0.1.0.tar.gz (21.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

codegen_cpp-0.1.0-py3-none-any.whl (19.8 kB view details)

Uploaded Python 3

File details

Details for the file codegen_cpp-0.1.0.tar.gz.

File metadata

  • Download URL: codegen_cpp-0.1.0.tar.gz
  • Upload date:
  • Size: 21.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for codegen_cpp-0.1.0.tar.gz
Algorithm Hash digest
SHA256 bd66d5790ee54f15b81fc779d666d3628f4ee2e73cbc49859a77c23d4e326add
MD5 9a2efc401fd2812349520b26b212c37c
BLAKE2b-256 a7872a8cfc4357f0271567cab125d9ba2828f4faa60d392e64aefc88575f69ae

See more details on using hashes here.

File details

Details for the file codegen_cpp-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: codegen_cpp-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 19.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for codegen_cpp-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 67bbb718a4c40307e17b95939280df974f42c9edca732cfebe5625dab47c8f20
MD5 259c8aa35995bea2e238419a7359f2cb
BLAKE2b-256 d3d54a5d94caa089a52ff1bc6bf2a011c531945f5d8a4d60e203d62a91afe23f

See more details on using hashes here.

Release history Release notifications | RSS feed

0.5.1

2 files

0.5.0

2 files

0.4.0

2 files

0.3.0

2 files

0.2.0

2 files

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page