Proposal: `TypeDef` — supplement types that should exist but are missing
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
Direzione di ricerca
Start by tracing the configuration loading, type registration, and generation flow described in the proposal, especially the phases that consume MethodCheck, TypeAbbr, and NewCheck. Check the existing TypeAlias handling and its tests to find the extension points. Done means TypeDef definitions are validated, registered before attribution, and emitted to their configured files in order; the issue does not name specific source files or tests.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Summary
Add a new llcppg.cfg option, TypeDef, that lets a binding author declare a Go type that should exist in the generated package but is missing from the C/C++ headers llcppg parses. The author names the file the type goes into, the type's name, and its fields. llcppg then emits the type into that file as part of the normal generation flow.
"TypeDef": {
"moduleobject.go": [{
"Name": "ModuleObject",
"Fields": [".Object"]
}]
}
This means: in moduleobject.go, generate a ModuleObject struct with one embedded field, Object, where the leading dot in .Object says the symbol is the Object type of the current package. Each file maps to an array of type definitions, so the order in which types are emitted is exactly the order written in the config.
Motivation
Some C libraries use a type in their public API surface conceptually, but never expose its definition in the public headers.
The motivating case is the Python C API. PyModuleObject is the concrete object type behind every module, yet its definition lives in an internal header and is not part of what users include. The public API only ever sees it as PyObject *. The PyModule_* functions (PyModule_GetDict, PyModule_GetName, ...) clearly belong to a "module" type.
After conversion to Go, we would like:
- the type to be called
ModuleObject(following the existingPyprefix stripping), and - the
PyModule_*functions to become methods on*ModuleObject, in the same wayPyFloat_AsDoublebecomes a method onFloatviaMethodCheck.
Today this is impossible, because there is no PyModuleObject in the parsed headers, so there is no type to attach methods to and nothing for other config options (MethodCheck, TypeAbbr, NewCheck) to refer to.
Existing options do not cover this:
TypeAliasmaps an existing C/C++ type to a Go symbol. It cannot invent a new one.- Hand-written Go files next to the generated ones work, but they are outside the generation flow: they are invisible to llcppg's name matching and method attribution, and they are lost or must be re-synchronized when the package is regenerated.
TypeDef fills the gap: the type is declared in the config, created by llcppg, and participates in the rest of the pipeline like any other type.
Configuration
TypeDef is an object whose keys are file names and whose values are arrays of type definitions:
"TypeDef": {
"<file>.go": [
{ "Name": "<TypeName>", "Fields": [ ... ] },
{ "Name": "<AnotherType>", "Fields": [ ... ] }
]
}
- Key — the target Go file name (for example
moduleobject.go). The file is created in the output package directory, next to the other generated files. - Value — an ordered array of type definitions. Several types may share one file by listing several entries. The array order is the emission order, so the result is deterministic and a type can be written after the types it depends on.
Name— the Go name of the type to define. This is the final Go name, exactly as it should appear in the package (ModuleObject, notPyModuleObject). It is not run through the prefix-stripping or naming rules.Fields— an ordered list of strings, one per field, in struct order.
Field forms
Each entry in Fields is either an embedded field or a named field, distinguished by its shape:
- Embedded field — a single token, the type itself:
".Object". This produces an embedded field. A pointer embed is written"*.Object". - Named field — two tokens separated by a space,
"Name Type":"name *c.Char","dict *.Object","count int". This produces an ordinary field namedNameof typeType.
Type references: the leading dot
The type part of a field uses a small convention to say where a symbol comes from:
- A leading dot means "this package".
.Objectis theObjecttype of the package being generated, and.ModuleObjectis another type in the same package (including one defined byTypeDefitself). The dot is written right before the symbol, after any*,[]or[N]prefix:*.Object,[]*.Object,[4].Object. - No leading dot means a global predefined type, such as
int,uint8,float64,stringorbool. - Types from other packages that the generated file imports keep their qualified form (
c.Int,c.Char), since the qualifier already says where they come from.
The dot removes any ambiguity between a name in the package and a predeclared one. A package may well contain a type that happens to be spelled like a predefined type, and .string and string then mean different things.
Example
"TypeDef": {
"moduleobject.go": [
{
"Name": "ModuleObject",
"Fields": [".Object"]
},
{
"Name": "ModuleDef",
"Fields": [".Object", "name *c.Char", "size c.Long", "dict *.Object", "flags int"]
}
]
}
generates moduleobject.go:
package python
import "github.com/goplus/lib/c"
type ModuleObject struct {
Object
}
type ModuleDef struct {
Object
name *c.Char
size c.Long
dict *Object
flags int
}
(Field-name export rules follow the usual llcppg conventions; the example above shows names exactly as written in the config.)
Semantics
Where the type is emitted
The type is written to the file named by the key, in the order it appears in that file's array. Files themselves are processed in the order their keys appear in the config. Files created only for TypeDef contain just the package clause, the needed imports, and the type declarations. If the file name collides with a file llcppg generates for another reason, the declarations are appended to that file rather than overwriting it.
Layout
The generated struct is an ordinary LLGo struct whose layout follows from its fields. For the embedded-base case this means ModuleObject has the same leading layout as Object, so a *ModuleObject can be used wherever the base is expected through the embedding, and the Go-side type reflects the C-side "subtype" relationship (PyModuleObject begins with PyObject_HEAD).
TypeDef does not attempt to reproduce the real C layout beyond what the author lists. If the author lists only Object, the Go type is only guaranteed to be a handle that starts with Object; code that needs fields of the hidden C struct must not rely on this definition.
Interaction with other options
A TypeDef type is registered before the load and attribution phases that consume type names, so it behaves like a type that came from the headers:
MethodCheckcan attribute functions such asPyModule_GetDicttoModuleObject, turning them into methods.TypeAbbrandNewCheckcan reference it by its Go name.- It can be used as a field or parameter type by later
TypeDefentries.
Nothing in this proposal changes how those options work; it only guarantees the type exists when they run.
Relation to llcppg.pub
A TypeDef type has no C declaration to map from. By default it is not written to llcppg.pub, which records C-name to Go-name mappings used by dependent packages. See the open questions below for the dependent-package case.
Validation and errors
llcppg reports a configuration error (and stops before generating) when:
- a file key is empty or does not end in
.go; - an entry has no
Name, or itsNameis not a valid Go identifier; - a type name already exists in the package, whether from the headers or from another
TypeDefentry.TypeDefonly supplies missing types; it never overrides existing ones (useTypeAliasfor that); - a
Fieldsentry is empty, has more than two tokens, or its type part does not parse as a Go type expression; - two fields end up with the same name (including an embedded field whose type name equals another field's name);
- a dotted reference (
.Name) names a type that does not exist in the generated package, or an undotted name is neither a predefined type nor a qualified type from an imported package; - a referenced type otherwise cannot be resolved in the generated package or its imports.
Errors name the file, the type, and the offending field entry.
Alternatives considered
- Hand-written files in the package. Works today but sits outside generation:
MethodCheckcannot see the type, and regeneration needs manual care. Rejected as the primary mechanism, though it remains available for anythingTypeDefcannot express. - Extending
TypeAliasto allow undefined targets. Mixes two different intents (re-map an existing type vs. create a new one) and has no natural place for fields. - A flat list instead of a file-keyed object. Simpler, but loses control over which file a type lands in, which matters for readable output and for keeping generated files stable across regenerations.
- Keying definitions by type name (
"moduleobject.go": { "ModuleObject": {...} }). This was the first draft. JSON objects are unordered, so the emission order of several types in one file would be undefined, and a type could not reliably be placed after the types it refers to. An array of{ "Name", "Fields" }entries makes the order explicit.
Open questions
- Empty
Fields. Should"Fields": [](or omitting it) produce an empty struct, or be rejected? An empty struct seems useful for opaque handle types, but it should be a deliberate choice. llcppg.pubentries. If a dependent package needs to refer toModuleObjectby its C namePyModuleObject, an optional C-name field (for example"CName": "PyModuleObject") could register the mapping. It is left out of the first version to keep the surface small.- Doc comments. Should a
TypeDefentry accept an optional"Doc"string to attach a doc comment to the generated type? Useful forllcppg -doc, but not required for the core feature. - Methods on
TypeDeftypes beyondMethodCheck. Out of scope here; the type only needs to exist so that existing attribution mechanisms can target it.
Compatibility
Purely additive. Configs without TypeDef behave exactly as before.
- Lingua principale
- Go
- Stelle
- 40
- Fork
- 13
- Merge medio
- 1h 34m
- PR unite (30g)
- 251
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
Difficoltà 5/5 Più di una settimana Idoneità per principianti 35/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
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
-
area/gateway kind/bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 74/100
vllm-project/aibrix#3226 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
I maintainer di solito rispondono entro 1 giorno
-
gencrl: serial numbers that fail to parse are added to the CRL as 0 or a partial valueForse già presa @sebxt l’ha presa oggi. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
cloudflare/cfssl#1460 ·
I maintainer di solito rispondono entro 2 giorni
-
bug
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 75/100
ironcore-dev/cloud-hypervisor-provider#210 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
OpenTollGate/tollgate-module-basic-go#1027 ·
I maintainer di solito rispondono entro 1 giorno