Proposal: generate a layout test package for converted Go packages
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 5/5
- Tempo stimato
- Più di una settimana
- Idoneità per principianti
- 35/100
- Tipo di issue
- Funzionalità
- Chiarezza
- Abbastanza chiara
- Stato di attività
- Attiva
- Ambito
- build-system, testing-qa
Direzione di ricerca
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.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
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:
sizeofmatches.alignofmatches.- 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,alignofandoffsetoffor 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.goandlayout.cppfrom 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 extendlayout.instancesfor templates.
7. Rollout
- Non-template structs and classes: size, alignment, field offsets and sizes.
- Embedded bases, standalone empty-class exceptions, skipped-item reporting.
- Template instantiations from headers, then config-listed instantiations.
-layout=staticmode.- 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 aninternal/layouttestsubpackage 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.
- Lingua principale
- Go
- Stelle
- 40
- Fork
- 13
- Merge medio
- 2h 8m
- PR unite (30g)
- 199
Preparare l'ambiente
Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di goplus/llcppg
-
Architecture-Design configuration feature proposal
Difficoltà 5/5 Più di una settimana Idoneità per principianti 25/100
I maintainer di solito rispondono entro 1 giorno
-
Proposal: `Define` — inject constants into the generated packageForse già presa Una pull request collegata a questa issue è aperta o già unita. Apertaconfiguration feature proposal
Difficoltà 5/5 Più di una settimana Idoneità per principianti 25/100
goplus/llcppg#1067 · 2 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
bug
Difficoltà 5/5 Più di una settimana Idoneità per principianti 28/100
goplus/llcppg#1035 · 2 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
bug
Difficoltà 4/5 3-5 giorni Idoneità per principianti 55/100
I maintainer di solito rispondono entro 1 giorno
-
feature proposal
Difficoltà 5/5 Più di una settimana Idoneità per principianti 18/100
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di goplus/llcppg
Issue simili
-
enhancement
Difficoltà 2/5 1-3 ore Idoneità per principianti 61/100
I maintainer di solito rispondono entro 1 giorno
-
stage-fail
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 72/100
siyuan-note/bazaar#2405 ·
I maintainer di solito rispondono entro 1 giorno
-
bug priority: high workstream:cache
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
rossoctl/context-guru#707 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 73/100
prime-radiant-inc/evener#5028 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 85/100
I maintainer di solito rispondono entro 1 giorno