Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

Proposal: generate a layout test package for converted Go packages

Abierto
#1,030 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
5/5
Tiempo estimado
Más de una semana
Aptitud para principiantes
35/100
Tipo de issue
Nueva funcionalidad
Claridad
Bastante claro
Estado de actividad
Activo
Stack tecnológico
cpp, go

Línea de trabajo

Start by reading llcppg's conversion and type-layout code, the configuration handling for llcppg.cfg, and the end-to-end test suite; the issue does not name specific files. Prototype the first rollout step for non-template records, generating layout_test.go and layout.cpp, then run the generated package through llgo test. Done means mismatches report expected and actual values, failures return a non-zero exit code, and the staged rollout cases are covered.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

Architecture-Design feature to be confirm

1. Goal

llcppg converts C/C++ types into Go types that must have identical memory layouts (see the C/C++ to Go type mapping proposal). Today that guarantee rests on the conversion rules being right. Nothing checks the result.

This proposal makes llcppg generate a layout test package next to the converted package. Running go test (or llgo test) on it verifies, for every converted type:

  • sizeof matches.
  • alignof matches.
  • The offset of every field matches.
  • The size of every field matches.

A mismatch fails the test and names the type, the field, the expected value and the actual value.

2. Usage

After conversion, the output module contains a sibling test package:

mylib/            # converted Go package
mylib_layout/     # generated layout test package (do not edit)
    layout_test.go
    layout.cpp

Run it like any other test:

llgo test ./mylib_layout

llcppg generates it by default; -layout=false in the command line (or "layout": false in llcppg.cfg) turns it off. Regenerating the package overwrites it, so it is safe to wire into CI.

When a check fails, the output looks like:

--- FAIL: TestLayout/Baz
    layout_test.go:42: Baz.X offset: C++ = 4, Go = 0
    layout_test.go:43: Baz sizeof: C++ = 8, Go = 4

3. Where the expected values come from

There are two possible sources, and they catch different bugs.

  • Static values captured at generation time. llcppg already reads the record layout from Clang when it converts a type. Writing those numbers into the test is cheap and needs no C++ compiler at test time. But it only proves the Go type agrees with what llcppg believed; if llcppg misread the layout, the test agrees with the mistake.
  • Values computed by a real C++ compiler at test time. The test package includes a small C++ file that exports sizeof, alignof and offsetof for every converted type. The Go test calls it and compares. This checks against the compiler that actually builds the library, so it also catches ABI and header drift.

Decision: use the second source by default. Offer -layout=static for environments that cannot build C++ code in tests, which emits the first kind instead. The two modes share the same generated Go test structure; only the origin of the expected numbers differs.

3.1 The generated C++ helper

For each type the helper exposes plain functions, with names derived from the Go type name so they are stable across regeneration:

#include <cstddef>
#include "mylib.h"

extern "C" {
size_t llcppg_layout_Baz_size()    { return sizeof(baz); }
size_t llcppg_layout_Baz_align()   { return alignof(baz); }
size_t llcppg_layout_Baz_off_x()   { return __builtin_offsetof(baz, x); }
size_t llcppg_layout_Baz_sz_x()    { return sizeof(((baz*)0)->x); }
}

__builtin_offsetof is used because offsetof is only conditionally supported for non-standard-layout types, and types with [[no_unique_address]] or base classes are common here. The helper may need -Wno-invalid-offsetof.

3.2 The generated Go test
func TestLayout(t *testing.T) {
    var v mylib.Baz
    check(t, "Baz sizeof", cxxSize_Baz(), unsafe.Sizeof(v))
    check(t, "Baz alignof", cxxAlign_Baz(), unsafe.Alignof(v))
    check(t, "Baz.X offset", cxxOff_Baz_x(), unsafe.Offsetof(v.X))
    check(t, "Baz.X sizeof", cxxSz_Baz_x(), unsafe.Sizeof(v.X))
}

Each type gets its own subtest, so one failing type does not hide the others.

4. What is and is not checked

Checked

  • Every converted struct/class: size and alignment.
  • Every field that has a Go counterpart: offset and size.
  • Embedded base classes: offset and size, using the embedded field name.
  • Instantiated templates (see 4.2).

Skipped

  • Generated padding fields (_llcppg_paddingN). They exist to make the other offsets right; they have no C++ counterpart.
  • Bit-fields and union members. Their Go representation is defined by their own proposals; only the size and alignment of the enclosing type are checked. These skips are listed in the test output so they are visible.
  • Types llcppg skipped during conversion.
4.1 Standalone empty classes

By design (known limitation in the type mapping document), an empty C++ class has sizeof 1 and its Go counterpart has unsafe.Sizeof 0. The test records this as an expected exception rather than a failure: it asserts C++ size is 1 and Go size is 0. If either side changes, the test fails, so the exception cannot silently drift.

Types that merely contain an empty class are checked normally. That is the case the padding rules exist for.

4.2 Templates

llcppg cannot know template arguments, but the layout directives (//llgo:padding, //llgo:padding-if, //llgo:no-padding) only make sense when verified on concrete instantiations. The test package therefore checks a set of instantiations:

  • Instantiations that already appear in the converted headers (as field types, typedefs or base classes). These are collected automatically.
  • Extra instantiations listed in llcppg.cfg, so authors can cover the interesting combinations, in particular the empty-type ones.

Suggested config shape:

{
  "layout": {
    "instances": [
      "Object<Empty, Empty>",
      "Object<Empty, int>",
      "Object<int, Empty>"
    ]
  }
}

Each instance produces a C++ helper entry and a Go check on the corresponding instantiated Go type, for example mylib.Object[mylib.Empty, mylib.Empty].

The "same empty type twice" case needs explicit attention, since that is where the simple padding model breaks. llcppg should add <T, T> for every template that has more than one type parameter used as a [[no_unique_address]] member whenever T can be an empty class, even if no such instantiation appears in the headers.

5. Behavior and failure reporting

  • One failure per mismatched value, with type, member, expected (C++) and actual (Go).
  • A final summary line with the count of checked types, checked fields and skipped items.
  • Non-zero exit code on any mismatch, so CI fails.
  • Output order follows header declaration order, which keeps diffs stable.

6. Responsibilities

  • llcppg generates layout_test.go and layout.cpp from the same type information it used for conversion, and regenerates them whenever the package is regenerated.
  • LLGo builds the C++ helper together with the test and resolves the extern "C" calls.
  • Library authors only run llgo test, and may extend layout.instances for templates.

7. Rollout

  1. Non-template structs and classes: size, alignment, field offsets and sizes.
  2. Embedded bases, standalone empty-class exceptions, skipped-item reporting.
  3. Template instantiations from headers, then config-listed instantiations.
  4. -layout=static mode.
  5. Run the generated package for every package in the llcppg end-to-end test suite and in goplus/lib CI.

8. Open questions

  • Package naming and location: sibling directory (mylib_layout) versus an internal/layouttest subpackage of the converted module.
  • How to name helper functions for types in nested namespaces and for template instances so the names stay stable across regeneration.
  • Whether the helper should also verify enum underlying type sizes and function pointer fields, which are layout-relevant but are not records.
  • Handling of ABIs other than Itanium: offsets from a real compiler are correct by construction, but the expected-exception list in 4.1 is ABI-specific.
  • Whether a failing layout check should also be offered as a generation-time warning when the C++ compiler is available, instead of waiting for tests.
Lenguaje dominante
Go
Estrellas
39
Forks
13
Merge medio
1 h 28 min
PR fusionados (30 d)
256

Preparar el entorno

Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de goplus/llcppg

Todos los issues de goplus/llcppg

Issues similares

Más issues de Go

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.