Proposal: `MethodPrefix` in llcppg.cfg
Maintainer thường phản hồi trong vòng 1 ngày
Một pull request liên quan đã được merge.
- #1066 của @luoliwoshang — đã merge
Đá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
- 30/100
Hướng nghiên cứu
This is a design proposal, so confirm with maintainers before coding. Start with how TypeAbbr and FuncPrefix are read from llcppg.cfg, then find the step where a global function judged to be a method of a type has its prefix removed. The fix should strip a literal per-type prefix there and leave other types unchanged. Done when gzbuffer on GzFile becomes Buffer and existing configs produce the same output.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Summary
Add a MethodPrefix field to llcppg.cfg that lets a library author declare, per type, a literal prefix to remove from the names of that type's methods. It targets older C libraries such as zlib, where the type is encoded in the function name by a short prefix (gz for gzFile) that cannot be recovered by lexical analysis.
"MethodPrefix": {
"GzFile": "gz"
}
With this configuration, gzbuffer(gzFile file, unsigned size) becomes (*GzFile).Buffer.
Background
When llcppg turns a global C function into a method, it has to decide what the method is called. For most modern C libraries the type name is embedded in the function name, separated from the rest by _ or by a camel-case boundary, and llcppg strips it using lexical rules:
CXTargetInfo
clang_getTranslationUnitTargetInfo(CXTranslationUnit CTUnit);
Here TranslationUnit appears as a whole word inside the name, so after the library prefix (clang_) and the type name are removed, what remains is TargetInfo. Type abbreviations (TypeAbbr) extend this for names where the type is written in a shortened form.
All of these mechanisms work at the lexical level: they only remove things that form complete, well-delimited words.
Problem
Older libraries often do not follow that convention. In zlib:
int gzbuffer(gzFile file, unsigned size);
int gzread(gzFile file, voidp buf, unsigned len);
int gzclose(gzFile file);
The receiver type is gzFile, but the function name only carries the two-letter prefix gz, glued directly to the rest of the name (gz + buffer). There is no _, no case change, and no complete type word to match. Because gz is not a lexically valid unit, neither the built-in type-name stripping nor TypeAbbr can remove it, and the generated method ends up with an awkward name like Gzbuffer.
The existing FuncPrefix cannot help either: it is global rather than per type, and it removes the prefix from function names whether or not the function becomes a method of that type.
Proposal
MethodPrefix is a map from a type's Go name to the prefix to strip from that type's method names.
{
"MethodPrefix": {
"GzFile": "gz"
}
}
Key points of the design:
- The key is the type's goName, consistent with
TypeAbbr. In the example above, the C typegzFilehas the Go nameGzFile. - The value is a literal prefix, written the way it appears in the C function name. Following the convention used throughout
llcppg.cfg, Prefix and Suffix fields ignore lexical boundaries, while all other concepts work within them.MethodPrefixis a Prefix field, sogzis removed fromgzbuffereven thoughgzandbufferare not separated by any word boundary. - Matching is on the C function name, case-sensitive, and the prefix must match at the start of the name.
When it runs
MethodPrefix is applied at the point where the type's goName and cName are both known, but the method's name is still in its original C style (before any conversion to Go-style camel case). In terms of the function-name pipeline, it takes part in the step where a global function that has been judged to be a method gets its type-related prefix removed.
After the prefix is removed, the remainder goes through the normal C-style to Go-style conversion, so the result is exported and camel-cased as usual:
| C function | Receiver | Method |
|---|---|---|
gzbuffer |
gzFile |
(*GzFile).Buffer |
gzread |
gzFile |
(*GzFile).Read |
gzclose |
gzFile |
(*GzFile).Close |
What it does not do
- It does not decide whether a function is a method. That is still determined by the existing mechanisms (the receiver is the first parameter, and so on).
MethodPrefixonly affects the name of a function that has already been classified as a method of the given type. - It does not change functions that are not methods of that type.
gzopen(const char *path, const char *mode)returns agzFilebut does not take one as its first parameter, so it remains a plain function andMethodPrefixdoes not touch it.
Behavior details
- No match. If a method of the type does not start with the configured prefix,
MethodPrefixhas no effect on that name, and the usual lexical rules apply as before, includingTypeAbbr. - Match. If the prefix matches, it is removed and the remainder is used as the base of the method name. For that name,
MethodPrefixandTypeAbbrare mutually exclusive: onceMethodPrefixmatches,TypeAbbris not consulted at all, neither for type-prefix removal nor for suffix removal. The built-in lexical type-name stripping is likewise not applied on top of the removed prefix, since the author has explicitly said how this type's methods are prefixed. The remainder only goes through the normal C-style to Go-style conversion. - Empty remainder. If removing the prefix would leave nothing (a function named exactly
gz), the prefix is not applied. - Types without an entry. Behave exactly as today. The option is purely additive, and existing configurations keep working unchanged.
Interaction with TypeAbbr
TypeAbbr has two roles: every abbreviation can be used for suffix removal, and only the last abbreviation is used for type-prefix removal. When MethodPrefix matches a method name, neither role runs for that name.
Consider this configuration:
{
"MethodPrefix": {
"FooType": "foo"
},
"TypeAbbr": [
"FooType: InFoo, Foo"
]
}
For a method named fooBarInFoo:
fooBarInFoo
-> MethodPrefix "foo" matches, remove it
-> BarInFoo
-> TypeAbbr is skipped (no prefix removal, no suffix removal)
-> BarInFoo
The result is BarInFoo, not Bar. If a type needs both a literal prefix and an abbreviation suffix stripped, that combination is not supported for now. A method of the same type that does not start with foo is unaffected by MethodPrefix and still goes through TypeAbbr as usual.
The reason for keeping the two mechanisms mutually exclusive is predictability: a name is handled either by the literal rule the author wrote or by the lexical rules, never by a mix of both. This is also the more conservative choice, because it can be relaxed later without breaking any configuration, whereas the opposite direction cannot.
Relationship to other options
| Option | Scope | Lexical rules | Role |
|---|---|---|---|
FuncPrefix |
all functions | none (literal) | Strips a library-wide prefix such as clang_ |
TypeAbbr |
per type | yes | Strips the type name or an abbreviation of it when it appears as a word |
MethodCheck |
per type family | pattern-based | Decides whether a function belongs to a logical type |
MethodPrefix (new) |
per type | none (literal) | Strips a type-specific prefix that is not a word; when it matches, TypeAbbr is not applied to that name |
MethodPrefix fills the one gap the others leave: a type-specific prefix that has no lexical structure.
Example: zlib
{
"name": "zlib",
"MethodPrefix": {
"GzFile": "gz"
}
}
Generated Go (abridged):
func (p GzFile) Buffer(size c.Uint) c.Int
func (p GzFile) Read(buf c.Pointer, len c.Uint) c.Int
func (p GzFile) Close() c.Int
Without the option, the same functions would appear as Gzbuffer, Gzread and Gzclose.
Possible extensions (out of scope)
- Wildcard or replacement syntax in the value, in the style of the unified name matcher. The literal prefix covers the zlib case, and the matcher can be reused later if a real need appears.
- Allowing a list of prefixes for a type whose methods are spread over several prefixes. This can be added later by accepting a list in addition to a string without breaking existing configurations.
- Letting
TypeAbbrsuffix removal continue to apply after aMethodPrefixmatch, for types that need both. This would only relax the current mutual exclusion, so it can be added later if a real library needs it.
- Ngôn ngữ chính
- Go
- Star
- 40
- Fork
- 13
- Merge trung bình
- 2 giờ 8 phút
- Pull request đã merge (30 ngày)
- 199
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 feature proposal
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
goplus/llcppg#1035 · 2 bình luận ·
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ự
-
Project submission:Đang mở
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 72/100
slavakurilyak/awesome-ai-agents#845 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
Maintainer thường phản hồi trong vòng 3 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 85/100
-
[BUG] deploy.restart_policy without condition drops the restart policyCó thể đã có người làm @akshat-lakhera đã nhận 1 ngày trước. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
docker/compose#14383 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 72/100
Azure/azure-storage-fuse#2429 ·
Maintainer thường phản hồi trong vòng 1 ngày