Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

Proposal: `FieldAlias` — override the Go name and type of a C/C++ field

Đang mở
#1,019 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

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
Loại issue
Tính năng
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
go
Lĩnh vực
tooling

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ả

configuration proposal

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, since N is 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_ becomes X__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 TypeAlias entry, 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

  1. Scope of the override. FieldAlias only changes the single field it names. All other fields of the same struct are converted as usual.
  2. Field position is preserved. The replacement field takes the place of the original in declaration order, so the surrounding layout stays consistent.
  3. 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.
  4. 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.
  5. Unmatched keys are reported. If a key in FieldAlias matches no field in the parsed headers, llcppg emits a warning, so typos and stale entries after a header upgrade are caught.
  6. 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 FieldAlias usable as a general escape hatch and not only for dependent-sized arrays.

Known limitations

  • Layout equivalence is by convention. The [1]Data replacement for alignas(Data) char buf[sizeof(Data)] has the same size and alignment as the C++ original only as long as the Go translation of Data has 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 _Data above, an empty _Alloc placed 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 the Data being 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.
  • Not a substitute for fixing libclang gaps in general. Where the array length is a plain constant, llcppg should keep resolving it automatically. FieldAlias is for cases that cannot be resolved from the AST.

Alternatives considered

  • Extending TypeAlias. TypeAlias maps 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 of char), 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]byte or 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. FieldAlias has 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 TypeAlias notation (including the . prefix for same-package types), and keys use the same fully qualified cName form as the rest of the config. FieldAlias introduces 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

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. 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.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của goplus/llcppg

Tất cả issue của goplus/llcppg

Issue tương tự

Thêm issue về Go

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.