Proposal: `FieldAlias` — override the Go name and type of a C/C++ field
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ó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức phù hợp với người mới
- 55/100
Hướng nghiên cứu
Start by reading the configuration parsing and field conversion paths in llcppg, then inspect nearby tests for TypeAlias and C++ field translation. Add FieldAlias handling so a matching qualified field uses the configured Go name and type in its original position, and unmatched keys produce a warning. Done when the example translates as specified and the configuration rules are covered by 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, FieldAlias, that lets a user replace both the Go name and the Go type of a specific C/C++ field when libclang cannot provide enough information to convert it automatically.
The motivating case is libc++'s std::shared_ptr machinery (__shared_ptr_emplace), where a field is declared as a raw byte buffer whose size depends on a template parameter. libclang reports such a field as CXType_DependentSizedArray, and the array length is not available, so llcppg cannot produce a correct Go field.
Motivation
Consider this (simplified) code from libc++:
class __shared_weak_count {};
template <class _Tp, class _Alloc>
struct __shared_ptr_emplace : __shared_weak_count {
struct _Storage {
struct _Data {
[[no_unique_address]] _Alloc __alloc_;
[[no_unique_address]] _Tp __elem_;
};
alignas(_Data) char __buffer_[sizeof(_Data)];
};
_Storage __storage_;
};
The field __shared_ptr_emplace::_Storage::__buffer_ has the type char[sizeof(_Data)]. Because _Data depends on the template parameters _Tp and _Alloc, libclang classifies the type as CXType_DependentSizedArray. For this kind of type libclang only gives us the element type (char); it does not expose the length expression or its value. As a result:
- llcppg cannot emit
[N]byte, sinceNis unknown. - Emitting a zero-length or slice-like placeholder would silently produce a wrong struct layout, and
__storage_(and therefore__shared_ptr_emplace) would be unusable from Go.
The intent of the declaration is well known to a human reader, though: the buffer is raw storage for exactly one _Data object, with _Data's alignment. In Go that is simply an array of length 1 whose element type is the translated _Data struct. What is missing is a way to tell llcppg this.
This is not specific to libc++. Any library that uses sizeof(T)-sized, alignas(T)-aligned raw buffers inside templates (small-buffer optimizations, std::function storage, std::variant/std::optional storage, and so on) hits the same limitation.
Proposal
Add an optional FieldAlias object to llcppg.cfg. Each entry maps a field, identified by its qualified C++ path, to a replacement Go field declaration.
{
"FieldAlias": {
"__shared_ptr_emplace::_Storage::__buffer_": "X__bufferRaw_ [1].X__SharedPtrEmplace_Storage_Data[_Tp, _Alloc]"
}
}
Key: which field
The key is the field's full C++ name (its cName), consistent with every other name in llcppg.cfg: enclosing namespaces (if any), then the enclosing class or classes, then the field name, all separated by ::. Because the path is always fully qualified, two classes with the same name in different namespaces never collide.
In the example, __shared_ptr_emplace::_Storage::__buffer_ means: the field __buffer_ of the class _Storage that is nested inside __shared_ptr_emplace.
Template parameters are not written in the key. The entry applies to the class template as a whole.
Value: the replacement declaration
The value has the shape of a Go struct field declaration, <name> <type>:
- The first word is the Go field name to use in the generated struct. It may differ from the C++ name. In the example,
__buffer_becomesX__bufferRaw_, which also makes it obvious in the Go API that this is raw storage and not a typed member. - The rest of the string is the Go type expression. Its syntax and semantics are exactly the same as the type definition on the right-hand side of a
TypeAliasentry, so users only have to learn one notation. Array lengths, pointers, slices, builtin types and generic instantiations are all accepted.
Referring to types in the current package
As in TypeAlias, a name that starts with a dot refers to a type generated in the current package. In the example, .X__SharedPtrEmplace_Storage_Data is the Go type that llcppg produces for __shared_ptr_emplace::_Storage::_Data. The leading dot is only a marker for llcppg and is dropped in the output; it lets llcppg tell a local generated name from a builtin Go type (int, byte, ...) or an external package reference (unsafe.Pointer) without guessing.
Generic arguments
If the enclosing C++ class is a template, the Go struct is generated as a generic type, and the template parameters (_Tp, _Alloc) are in scope in the type expression. They can be passed to generic types in the usual Go way, as in .X__SharedPtrEmplace_Storage_Data[_Tp, _Alloc].
Expected result
With the entry above, the class __shared_ptr_emplace::_Storage is translated to:
type X__SharedPtrEmplace_Storage[_Tp any, _Alloc any] struct {
X__bufferRaw_ [1]X__SharedPtrEmplace_Storage_Data[_Tp, _Alloc]
}
Without the entry, llcppg has no valid translation for this field.
Because _Data is translated like any other nested struct, it is emitted as its own generic type:
type X__SharedPtrEmplace_Storage_Data[_Tp any, _Alloc any] struct {
__alloc_ _Alloc
__elem_ _Tp
}
Behavior and rules
- Scope of the override.
FieldAliasonly changes the single field it names. All other fields of the same struct are converted as usual. - Field position is preserved. The replacement field takes the place of the original in declaration order, so the surrounding layout stays consistent.
- Name and type are always replaced together. An entry always provides a Go name and a Go type. If the user only wants to keep the C++ name, they repeat it as the first word.
- No semantic validation of the Go type. llcppg does not verify that the replacement type has the same size or alignment as the original C++ field. The user who writes the entry is responsible for that. llcppg only checks that the expression parses, that
.-prefixed names resolve to generated types, and that generic arguments name in-scope template parameters. - Unmatched keys are reported. If a key in
FieldAliasmatches no field in the parsed headers, llcppg emits a warning, so typos and stale entries after a header upgrade are caught. - Applies wherever the field would be converted, whether or not the type is a template, and whether the field was directly convertible or not. This makes
FieldAliasusable as a general escape hatch and not only for dependent-sized arrays.
Known limitations
-
Layout equivalence is by convention. The
[1]Datareplacement foralignas(Data) char buf[sizeof(Data)]has the same size and alignment as the C++ original only as long as the Go translation ofDatahas the same layout as the C++Data. llcppg does not verify this; the user who writes the entry is responsible for checking it when the buffer is accessed across the C/Go boundary.In most cases the layouts do agree, including the
[[no_unique_address]]members in the libc++ example. An empty class occupies 1 byte in C++ by default, but a member declared[[no_unique_address]]may occupy no space, and a zero-size Go field (struct{}) behaves the same way when it is not the last field. For the_Dataabove, an empty_Allocplaced before__elem_therefore produces the same layout in C++ and in Go. There are two edge cases where the layouts can still differ:- Empty member as the last field. Go adds trailing padding after a zero-size final field, so that a pointer to that field cannot point past the end of the object. C++ adds the same padding for a plain empty member, but not for a
[[no_unique_address]]one. For example,struct { int x; [[no_unique_address]] E e; }is 4 bytes in C++, while the corresponding Go struct{ x int32; e E }is 8 bytes. If the empty member is last in theDatabeing aliased, the Go struct may be larger than the C++ one. - A struct containing only empty members. C++ never gives a class size 0, so it is at least 1 byte. The Go equivalent has size 0.
- Empty member as the last field. Go adds trailing padding after a zero-size final field, so that a pointer to that field cannot point past the end of the object. C++ adds the same padding for a plain empty member, but not for a
-
Not a substitute for fixing libclang gaps in general. Where the array length is a plain constant, llcppg should keep resolving it automatically.
FieldAliasis for cases that cannot be resolved from the AST.
Alternatives considered
- Extending
TypeAlias.TypeAliasmaps whole C/C++ types to Go symbols. The problem here is specific to one field whose type has no usable identity (a dependent-sized array ofchar), so there is nothing meaningful to key on at the type level. Keying on the field is the natural choice, and it also allows renaming. - Computing the length from the template. Evaluating
sizeof(_Data)for a dependent type would require instantiating the template or implementing a C++ constant evaluator, which is far outside llcppg's scope. - Emitting
[0]byteor an opaque blob. This loses the type information and the alignment, and silently produces a wrong layout, which is worse than requiring an explicit configuration.
Non-goals
- Dropping a field.
FieldAliashas no "remove this field" form. It is only for the case where the field needs a different name and a concrete replacement type. - A new type notation. The type expression reuses the
TypeAliasnotation (including the.prefix for same-package types), and keys use the same fully qualifiedcNameform as the rest of the config.FieldAliasintroduces no new syntax rules beyond<name> <type>.
- Ngôn ngữ chính
- Go
- Star
- 39
- Fork
- 13
- Merge trung bình
- 1 giờ 27 phút
- Pull request đã merge (30 ngày)
- 256
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
-
fix(cl): preserve imports for types initialized across filesCó thể đã có người làm @luoliwoshang đã nhận 1 ngày trước. Đang mở
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 22/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ 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
-
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 25/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ự
-
component/server severity/minor type/bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
Maintainer thường phản hồi trong vòng 1 ngày
-
diagnose globalnet reports a failure for every exported headless ServiceCó thể đã có người làm @kurtyoon đã nhận hôm nay. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
submariner-io/subctl#1948 ·
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 62/100
-
Upgrade to Go 1.27.1Đang mởhelp wanted
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 70/100
meshery-extensions/meshery-mcp-server#63 · 1 bình luận ·
-
Upgrade to Go 1.27.1Đang mởhelp wanted
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 62/100