githubEdit

Go

Let's build a Go application using the TEN framework.

Creating the TEN App

First, we'll create a basic TEN Go app by integrating several pre-built TEN packages. Follow these steps:

>_ Terminal
tman install app default_app_go
cd default_app_go

tman install protocol msgpack
tman install extension_group default_extension_group

Installing a Default TEN Extension

Next, install a default TEN extension written in Go:

>_ Terminal
tman install extension default_extension_go

Declaring the Prebuilt Graph for Auto-Start

Now, we'll modify the property.json file of the TEN app to include a graph declaration. This will ensure the default extension starts automatically when the TEN app is launched.

.json
"predefined_graphs": [
  {
    "name": "default",
    "auto_start": true,
    "nodes": [
      {
        "type": "extension_group",
        "name": "default_extension_group",
        "addon": "default_extension_group"
      },
      {
        "type": "extension",
        "name": "default_extension_go",
        "addon": "default_extension_go",
        "extension_group": "default_extension_group"
      }
    ]
  }
]

Building the App

Unlike standard Go projects, the TEN Go app uses CGo, so you need to set up certain environment variables before building. A build script is already provided in the TEN runtime Go binding system package, so you can build the app with a single command:

The compiled binary, main, will be generated in the /bin folder.

Starting the App

Since some environment variables need to be set, it is recommended to start the app using the provided script:

Debugging

If you are using Visual Studio Code (VSCode) as your development environment, you can use the following configurations to debug Go/C code.

Debugging Go Code

Debugging C Code

CGO

Generated Code

When interfacing Go with C, the cgo tool is crucial. When a C function is called from Go, cgo converts the Go source files into multiple output files, including both Go and C source files. These generated C source files are then compiled using compilers like gcc or clang.

Use the following command to generate these C/Go source files:

Example:

The generated files include:

  • _cgo_export.h is key to the interoperability between Go and C within the cgo context. It contains necessary declarations for Go functions accessible from C.

    This file is automatically generated based on Go functions explicitly exported using the //export directive. It also includes definitions for C types corresponding to the Go types used in these functions, ensuring compatibility between the two languages.

    Example of type definitions:

  • _cgo_gotypes.go contains the corresponding Go types defined in C and used in Go.

    For example, if you define a struct ten_go_error_t in a header file common.h and use C.ten_go_error_t in Go, there will be a corresponding Go type in _cgo_gotypes.go:

  • cmd.cgo1.go is a cgo-generated counterpart to the original Go source file cmd.go. It replaces direct calls to C functions and types with calls to the generated Go functions and types provided by cgo.

    Example:

  • cmd.cgo2.c is a wrapper of the original C function called from Go.

    Example:

So, the calling sequence of C.ten_go_cmd_create_cmd() from Go is:

Incomplete Type

As mentioned earlier, the cgo tool generates corresponding Go types in _cgo_gotypes.go based on C header files imported via import "C". If a struct is opaque in the C header, the cgo tool generates an incomplete type instead.

Example of an opaque C struct:

The cgo tool will generate an incomplete type in Go:

What happens if you use the incomplete type in Go?

  • Incomplete types cannot be allocated.

    Since sys.NotInHeap cannot be allocated on the Go heap, operations like new or make won't work. Attempting to create a new instance of an opaque struct in Go will result in a compiler error:

  • Pointers to incomplete types cannot be passed to C directly.

    If you have a C function with a pointer to an opaque struct as a parameter, passing a Go pointer to this incomplete type directly to a C function will not work according to cgo rules. The Go compiler will require the pointer to be "pinned" to ensure it adheres to Go's garbage collector (GC) constraints.

Rules for Using C.uintptr_t Instead of a Pointer to an Opaque Struct

  • Benefits:

    • C.uintptr_t and uintptr in Go are integers large enough to hold a C pointer, avoiding memory allocation or conversion when passing from Go to C.

  • Limitations:

    • uintptr is an integer, not a pointer. It represents the bit pattern of an address with no pointer arithmetic.

    • uintptr cannot be dereferenced in Go. Converting it to unsafe.Pointer can cause issues with Go's GC.

    • Since uintptr is an integer, nil or NULL cannot be passed to C. Use 0 instead of nil to represent a null address.

Last updated