Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

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

Cerrado
#1,019 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
4/5
Tiempo estimado
3-5 días
Aptitud para principiantes
55/100
Tipo de issue
Nueva funcionalidad
Claridad
Bien especificado
Estado de actividad
Activo
Stack tecnológico
go
Área
tooling

Línea de trabajo

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.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

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>.
Lenguaje dominante
Go
Estrellas
39
Forks
13
Merge medio
1 h 28 min
PR fusionados (30 d)
256

Preparar el entorno

Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de goplus/llcppg

Todos los issues de goplus/llcppg

Issues similares

Más issues de Go

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.