docs(llgo/doc): documents fix

This commit is contained in:
morpingsss
2024-07-26 16:14:41 +08:00
parent 98d075728f
commit e764a2298d
2 changed files with 127 additions and 144 deletions

View File

@@ -74,30 +74,31 @@ gogensig - # read AST from stdin
### Process ### Process
1. The Parsing Module reads `config.json` to obtain dynamic libraries, header files, and the package name. After parsing, it writes the generated `common_symbol_info.json` path into `config.json`. 1. The Parsing Module reads `llcppg.cfg` to obtain dynamic libraries, header files, and the package name. After parsing, it writes the generated `llcppg.symb.json` path into `llcppg.cfg`.
2. The Function Declaration Generation Module reads `config.json` to get the package name, header files, and the previously generated `common_symbol_info.json`. After parsing, it generates the function prototype `func_prototype.json`. 2. The Function Declaration Generation Module reads `llcppg.cfg` to get the package name, header files, and the previously generated `llcppg.symb.json`. After parsing, it generates the function prototype `llcppg.function.json`.
3. Reads the previously generated `func_prototype.json`, stores it as a structure, and uses gogen to generate code based on the structure. 3. Reads the previously generated `llcppg.information.json`, stores it as a structure, and uses gogen to generate code based on the structure.
## Parsing Module ## Parsing Module
### Input ### Input
Obtains the paths to header files and dynamic library files by reading the JSON file `config.json`. Obtains the paths to header files and dynamic library files by reading the JSON file `llcppg.cfg`.
```json ```json
{ {
"PackageName": "inireader", "name": "inih",
"HeaderFiles": [ "cflags": "$(pkg-config --cflags INIReader)",
"/path/to/header/INIReader.h", "include": [
"/path/to/header/AnotherHeader.h" "INIReader.h",
"AnotherHeaderFile.h"
], ],
"DLLFile": "/path/to/lib/libINIReader.dylib", "libs": "$(pkg-config --libs INIReader)",
"JSONFile": "/path/to/json/config.json" "trimPrefixes": ["Ini", "INI"]
} }
``` ```
```bash ```bash
./generate_symbol_table /path/to/config.json llcppsymg config-file
``` ```
### Implementation Steps ### Implementation Steps
@@ -105,15 +106,15 @@ Obtains the paths to header files and dynamic library files by reading the JSON
1. Parse dylib and store: 1. Parse dylib and store:
```go ```go
// common.go // types.go
type CPPSymbol struct { type CPPSymbol struct {
Address string Symbol string `json:"symbol"`
Type string Type string `json:"type"`
Name string Name string `json:"name"`
} }
// parser_dylib.go // parser_dylib.go
func ParseDylibSymbols(dylibPath string) ([]common.CPPSymbol, error) func parseDylibSymbols(lib string) ([]common.CPPSymbol, error)
``` ```
2. Parse header files and store: 2. Parse header files and store:
@@ -121,48 +122,46 @@ func ParseDylibSymbols(dylibPath string) ([]common.CPPSymbol, error)
```go ```go
// common.go // common.go
type ASTInformation struct { type ASTInformation struct {
Namespace string Namespace string `json:"namespace"`
Class string Class string `json:"class"`
Name string Name string `json:"name"`
BaseClasses []string BaseClasses []string `json:"baseClasses"`
ReturnType string ReturnType string `json:"returnType"`
Location string Location string `json:"location"`
Parameters []Parameter Parameters []Parameter `json:"parameters"`
Symbol string Symbol string `json:"symbol"`
} }
type Parameter struct { type Parameter struct {
Name string Name string `json:"name"`
Type string Type string `json:"type"`
} }
// parser_ast.go // parser_ast.go
func ParseHeaderFile(files []string) ([]common.ASTInformation, error) func parseHeaderFile(config types.Config) ([]common.ASTInformation, error)
``` ```
3. Cross-reference data from the first two steps to get the final output 3. Cross-reference data from the first two steps to get the final output
```go ```go
// common.go // common.go
type CommonSymbolInfo struct { type SymbolInfo struct {
FunctionName string Mangle string `json:"mangle"` // C++ Symbol
Symbol string CPP string `json:"c++"` // C++ function name
Location string Go string `json:"go"` // Go function name
UserFunctionName string
} }
// common_symbols.go // common_symbols.go
func GetCommonSymbols(dylibSymbols []common.CPPSymbol, astInfoList []common.ASTInformation) []common.CommonSymbolInfo func getCommonSymbols(dylibSymbols []common.CPPSymbol, astInfoList []common.ASTInformation) []common.SymbolInfo {
``` ```
4. Generate `common_symbol_info.json` file and store the JSON file path into `config.json` 4. Generate `llcppg.symb.json` file and store the JSON file path into `llcppg.cfg`
```go ```go
// generator.go func generateJSON([]CommonSymbolInfo)
func GenerateJSON([]CommonSymbolInfo)
``` ```
5. Example `common_symbol_info.json` file 5. Example `llcppg.symb.json` file
```json ```json
{ {
@@ -177,14 +176,14 @@ func GenerateJSON([]CommonSymbolInfo)
### Input ### Input
No input required, directly reads the `config.json` file No input required, directly reads the `llcppg.cfg` file
### Implementation Steps ### Implementation Steps
1. Execute the executable 1. Execute the executable
```bash ```bash
./generate_func_decl /path/to/config.json llcppsigfetch config-file
``` ```
2. Parse header files 2. Parse header files
@@ -192,18 +191,19 @@ No input required, directly reads the `config.json` file
```go ```go
// common.go // common.go
type ASTInformation struct { type ASTInformation struct {
Namespace string Namespace string `json:"namespace"`
Class string Class string `json:"class"`
Name string Name string `json:"name"`
BaseClasses []string BaseClasses []string `json:"baseClasses"`
ReturnType string ReturnType string `json:"returnType"`
Location string Location string `json:"location"`
Parameters []Parameter Parameters []Parameter `json:"parameters"`
Symbol string `json:"symbol"`
} }
type Parameter struct { type Parameter struct {
Name string Name string `json:"name"`
Type string Type string `json:"type"`
} }
// parser_ast.go // parser_ast.go
@@ -218,17 +218,17 @@ func ParseHeaderFile(filePath string) ([]common.ASTInformation, error)
```json ```json
{ {
"FunctionName": "A::B::C", "functionName": "A::B::C",
"Symbol": "_ZN9INIReaderC1ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE", "symbol": "_ZN9INIReaderC1ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE",
"Location": "a.h", "location": "a.h",
"ReturnType" : "int", "returnType": "int",
"UserFunctionName": "CFromA", "userFunctionName": "CFromA",
"Parameters" : [ "parameters": [
{ {
"arg1" : "int" "arg1": "int"
}, },
{ {
"arg2" : "*char" "arg2": "*char"
} }
] ]
} }
@@ -245,7 +245,7 @@ No input required, directly reads `func_prototype.json` file
1. Execute the executable 1. Execute the executable
```bash ```bash
./generate_code /path/to/func_prototype.json gogensig ast-file
``` ```
2. Parse JSON file 2. Parse JSON file
@@ -253,11 +253,11 @@ No input required, directly reads `func_prototype.json` file
```go ```go
// common.go // common.go
type HeaderFileInfo struct { type HeaderFileInfo struct {
FunctionName string FunctionName string `json:"functionName"`
Symbol string Symbol string `json:"symbol"`
Location string Location string `json:"location"`
UserFunctionName string UserFunctionName string `json:"userFunctionName"`
Parameters map[string]string Parameters map[string]string `json:"parameters"`
} }
// parse_json.go // parse_json.go

View File

@@ -1,5 +1,3 @@
# How to support a C/C++ Library
# Support a C Library # Support a C Library
## Install a C Library ## Install a C Library
@@ -14,7 +12,7 @@ brew install inih
1. On macOS, use `nm -gU libbar.dylib` to parse C-style symbols 1. On macOS, use `nm -gU libbar.dylib` to parse C-style symbols
```jsx ```bash
0000000000003e55 T _ini_parse 0000000000003e55 T _ini_parse
``` ```
@@ -26,14 +24,14 @@ int ini_parse(const char* filename, ini_handler handler, void* user);
3. Create the corresponding Go file 3. Create the corresponding Go file
```c ```c
inih/ inih/
├── _demo _demo
inih_demo inih_demo
inih_demo.go inih_demo.go
└── inih.go inih.go
```
```
4. In `inih.go`, use LLGoPackage to specify the location of the third-party library so that llgo can link to the third-party library. Both `pkg-config --libs inih` and `linih` are used to specify the location of the third-party library. 4. In `inih.go`, use LLGoPackage to specify the location of the third-party library so that llgo can link to the third-party library. Both `pkg-config --libs inih` and `linih` are used to specify the location of the third-party library.
```go ```go
@@ -50,42 +48,42 @@ const (
5. Write the corresponding function in `inih.go` 5. Write the corresponding function in `inih.go`
Note that the basic C function type mapping to Go function type can be found at [https://github.com/goplus/llgo/blob/main/doc/Type-Mapping-between-C-and-Go.md](https://github.com/goplus/llgo/blob/main/doc/Type-Mapping-between-C-and-Go.md). Some types requiring special handling are listed at the end of this document for reference. Note that the basic C function type mapping to Go function type can be found at [https://github.com/goplus/llgo/blob/main/doc/Type-Mapping-between-C-and-Go.md](https://github.com/goplus/llgo/blob/main/doc/Type-Mapping-between-C-and-Go.md). Some types requiring special handling are listed at the end of this document for reference.
```go ```go
//go:linkname Parse C.ini_parse //go:linkname Parse C.ini_parse
func Parse(filename *c.Char, handler func(user c.Pointer, section *c.Char, name *c.Char, value *c.Char) c.Int, user c.Pointer) c.Int func Parse(filename *c.Char, handler func(user c.Pointer, section *c.Char, name *c.Char, value *c.Char) c.Int, user c.Pointer) c.Int
``` ```
6. Write the function call in `inih_demo.go` 6. Write the function call in `inih_demo.go`
```go ```go
package main package main
import ( import (
"github.com/goplus/llgo/c" "github.com/goplus/llgo/c"
"github.com/goplus/llgo/cpp/inih" "github.com/goplus/llgo/cpp/inih"
) )
func main() { func main() {
filename := c.Str("path/to/yourIniFile") filename := c.Str("path/to/yourIniFile")
if inih.Parse(filename, func(user c.Pointer, section *c.Char, name *c.Char, value *c.Char) c.Int { if inih.Parse(filename, func(user c.Pointer, section *c.Char, name *c.Char, value *c.Char) c.Int {
println("section:", c.GoString(section), "name:", c.GoString(name), "value:", c.GoString(value)) println("section:", c.GoString(section), "name:", c.GoString(name), "value:", c.GoString(value))
return 1 return 1
}, nil) < 0 { }, nil) < 0 {
println("Error parsing config file") println("Error parsing config file")
return return
} }
} }
```
```
7. Use llgo to run the demo 7. Use llgo to run the demo
```bash ```bash
cd inih/_demo/inih_demo cd inih/_demo/inih_demo
llgo run . llgo run .
``` ```
## Handling Special Types ## Handling Special Types
@@ -106,6 +104,9 @@ typedef enum {
BLEND_CUSTOM_SEPARATE // Blend textures using custom rgb/alpha separate src/dst factors (use rlSetBlendFactorsSeparate()) BLEND_CUSTOM_SEPARATE // Blend textures using custom rgb/alpha separate src/dst factors (use rlSetBlendFactorsSeparate())
} BlendMode; } BlendMode;
*/ */
type BlendMode c.Int
const ( const (
BLEND_ALPHA BlendMode = iota // Blend textures considering alpha (default) BLEND_ALPHA BlendMode = iota // Blend textures considering alpha (default)
BLEND_ADDITIVE // Blend textures adding colors BLEND_ADDITIVE // Blend textures adding colors
@@ -140,7 +141,7 @@ type Vector4 struct {
} }
// If class member variables don't need to be exposed, like llgo/c/cjson, wrap functions that use these member variables as methods of the class. Example: // If class member variables don't need to be exposed, like llgo/c/cjson, wrap functions that use these member variables as methods of the class. Example:
//
/* /*
typedef struct cJSON typedef struct cJSON
{ {
@@ -171,7 +172,6 @@ type JSON struct {
} }
// llgo:link (*JSON).AddItem C.cJSON_AddItemToArray // llgo:link (*JSON).AddItem C.cJSON_AddItemToArray
func (o *JSON) AddItem(item *JSON) c.Int { return 0 } func (o *JSON) AddItem(item *JSON) c.Int { return 0 }
``` ```
For the size of Unused, if the methods bound to the structure do not need to create objects, i.e., the receiver of the Go methods bound to this structure is of pointer type, you can declare `Unused [0]byte`. Otherwise, you need to write a simple C file using the `sizeof` operator to calculate the size of the structure. Assuming the structure size is 38 bytes, then declare `Unused [38]byte`. For the size of Unused, if the methods bound to the structure do not need to create objects, i.e., the receiver of the Go methods bound to this structure is of pointer type, you can declare `Unused [0]byte`. Otherwise, you need to write a simple C file using the `sizeof` operator to calculate the size of the structure. Assuming the structure size is 38 bytes, then declare `Unused [38]byte`.
@@ -216,7 +216,6 @@ func main() {
strings[3] = c.Str("hao") strings[3] = c.Str("hao")
ptrtest.PrintStrings(unsafe.SliceData(strings), c.Int(4)) ptrtest.PrintStrings(unsafe.SliceData(strings), c.Int(4))
} }
``` ```
# LLGO for C++ Third-Party Libraries # LLGO for C++ Third-Party Libraries
@@ -241,7 +240,6 @@ inih/
├── _wrap/cpp_wrap.cpp (optional) ├── _wrap/cpp_wrap.cpp (optional)
└── inih.go └── inih.go
└── reader.go └── reader.go
``` ```
## Writing Go Files to Link Library Functions ## Writing Go Files to Link Library Functions
@@ -255,28 +253,24 @@ Ordinary functions can be directly linked using the corresponding symbol in the
```bash ```bash
nm -gU $(brew --prefix inih)/lib/libINIReader.dylib > output.txt nm -gU $(brew --prefix inih)/lib/libINIReader.dylib > output.txt
c++filt <output.txt> symbol.txt c++filt <output.txt> symbol.txt
``` ```
Function prototype Function prototype
```cpp ```cpp
int ParseError() const; int ParseError() const;
``` ```
Example of `symbol.txt` Example of `symbol.txt`
```bash ```bash
0000000000002992 T INIReader::ParseError() const 0000000000002992 T INIReader::ParseError() const
``` ```
Example of `output.txt` Example of `output.txt`
```bash ```bash
0000000000002992 T __ZNK9INIReader10ParseErrorEv 0000000000002992 T __ZNK9INIReader10ParseErrorEv
``` ```
Find the offset of the function you want to use in `symbol.txt`, then go back to `output.txt` and find the symbol corresponding to that offset. Find the offset of the function you want to use in `symbol.txt`, then go back to `output.txt` and find the symbol corresponding to that offset.
@@ -287,7 +281,6 @@ For functions, generally use `go:linkname` to link. Here, refer to the migration
// The inih library currently does not involve ordinary functions, this is for demonstration purposes only and is not needed for migrating inih // The inih library currently does not involve ordinary functions, this is for demonstration purposes only and is not needed for migrating inih
//go:linkname ParseError C.__ZNK9INIReader10ParseErrorEv //go:linkname ParseError C.__ZNK9INIReader10ParseErrorEv
func ParseError() c.Int func ParseError() c.Int
``` ```
### Migrating Classes ### Migrating Classes
@@ -299,7 +292,6 @@ func ParseError() c.Int
type Reader struct { type Reader struct {
Unused [32]byte Unused [32]byte
} }
``` ```
- Constructor - Constructor
@@ -323,18 +315,16 @@ func ParseError() c.Int
// llgo:link (*Reader).InitFromBuffer C._ZN9INIReaderC1EPKcm // llgo:link (*Reader).InitFromBuffer C._ZN9INIReaderC1EPKcm
func (r *Reader) InitFromBuffer(buffer *c.Char, bufferSize uintptr) {} func (r *Reader) InitFromBuffer(buffer *c.Char, bufferSize uintptr) {}
``` ```
- Constructor is not explicitly declared in the class (cannot find the corresponding symbol in the dynamic library) - Constructor is not explicitly declared in the class (cannot find the corresponding symbol in the dynamic library)
If the destructor is not explicitly declared in the source code, the compiler will automatically generate a default destructor. Use `extern "C"` to wrap it in cppWrap.cpp: If the destructor is not explicitly declared in the source code, the compiler will automatically generate a default destructor. Use `extern "C"` to wrap it in cppWrap.cpp:
```jsx ```c
extern "C" void INIReaderInit(INIReader* r) extern "C" void INIReaderInit(INIReader* r)
{ {
r->INIReader(); r->INIReader();
} }
``` ```
Link in Go: Link in Go:
@@ -342,7 +332,6 @@ func ParseError() c.Int
```go ```go
// llgo:link (*Reader).INIReaderInit C.INIReaderInit // llgo:link (*Reader).INIReaderInit C.INIReaderInit
func (r *Reader) INIReaderInit() {} func (r *Reader) INIReaderInit() {}
``` ```
- Class Methods - Class Methods
@@ -353,7 +342,6 @@ func ParseError() c.Int
func (r *Reader) GetInteger(section *std.String, name *std.String, defaultValue c.Long) c.Long { func (r *Reader) GetInteger(section *std.String, name *std.String, defaultValue c.Long) c.Long {
return 0 return 0
} }
``` ```
Template or inline methods of the class will be introduced in the next section. Template or inline methods of the class will be introduced in the next section.
@@ -364,7 +352,6 @@ func ParseError() c.Int
```go ```go
reader := inih.NewReader(c.Str(buf), uintptr(len(buf))) reader := inih.NewReader(c.Str(buf), uintptr(len(buf)))
defer reader.Dispose() defer reader.Dispose()
``` ```
### Templates and Inlines ### Templates and Inlines
@@ -376,7 +363,6 @@ Templates or inlines do not generate symbols in dynamic libraries (dylib) (defau
extern "C" void stdStringInitFromCStrLen(std::string* s, const char* cstr, size_t len) { extern "C" void stdStringInitFromCStrLen(std::string* s, const char* cstr, size_t len) {
new(s) std::string(cstr, len); new(s) std::string(cstr, len);
} }
``` ```
Then use LLGoFiles to link in Go: the writing of standard library's `LLGoFiles` and `LLGoPackage` is slightly different from third-party libraries. Using `std::string` and `spdlog` library as examples, inih does not involve this step: Then use LLGoFiles to link in Go: the writing of standard library's `LLGoFiles` and `LLGoPackage` is slightly different from third-party libraries. Using `std::string` and `spdlog` library as examples, inih does not involve this step:
@@ -393,7 +379,6 @@ func (s *String) InitFromCStrLen(cstr *c.Char, n uintptr) {}
const ( const (
LLGoFiles = "$(pkg-config --cflags spdlog): cppWrap/cppWrap.cpp" LLGoFiles = "$(pkg-config --cflags spdlog): cppWrap/cppWrap.cpp"
LLGoPackage = "link: $(pkg-config --libs spdlog); -lspdlog -pthread -lfmt") LLGoPackage = "link: $(pkg-config --libs spdlog); -lspdlog -pthread -lfmt")
``` ```
## Writing and Running the Demo ## Writing and Running the Demo
@@ -409,9 +394,8 @@ import (
func demoFromBuffer() { func demoFromBuffer() {
buf := `[settings] buf := `[settings]
username=admin username=admin
timeout=100 timeout=100`
`
reader := inih.NewReader(c.Str(buf), uintptr(len(buf))) reader := inih.NewReader(c.Str(buf), uintptr(len(buf)))
defer reader.Dispose() defer reader.Dispose()
@@ -441,7 +425,6 @@ func main() {
demoFromBuffer() demoFromBuffer()
demoFromFile() demoFromFile()
} }
``` ```
Use `llgo run reader_demo.go` to run in the directory where the demo is written. Use `llgo run .` to run in the directory where the demo is written.