Proposal: `TypeDef` — supplement types that should exist but are missing
Maintainer thường phản hồi trong vòng 1 ngày
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 5/5
- Thời gian dự kiến
- Hơn một tuần
- Mức phù hợp với người mới
- 35/100
Hướng nghiên cứu
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.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
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.
- Ngôn ngữ chính
- Go
- Star
- 40
- Fork
- 13
- Merge trung bình
- 1 giờ 34 phút
- Pull request đã merge (30 ngày)
- 251
Chuẩn bị môi trường
Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của goplus/llcppg
-
Architecture-Design configuration
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 35/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Proposal: `Define` — inject constants into the generated packageCó thể đã có người làm Có pull request liên kết đang mở hoặc đã được merge. Đang mởconfiguration feature proposal
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 25/100
goplus/llcppg#1067 · 2 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
bug
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 28/100
Maintainer thường phản hồi trong vòng 1 ngày
-
bug
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 55/100
Maintainer thường phản hồi trong vòng 1 ngày
-
feature proposal
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 18/100
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của goplus/llcppg
Issue tương tự
-
bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
Rafaeldelinares/gentle-mesh#125 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
Gentleman-Programming/gentle-ai#5643 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
security
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 80/100
FootprintAI/Containarium#2869 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
kind/bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 86/100
kubevirt/kubevirt#19559 · 1 reaction ·
Maintainer thường phản hồi trong vòng 1 ngày
-
bug raised-by:verifier
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
medici-finance/assay#3817 ·
Maintainer thường phản hồi trong vòng 1 ngày