Skip to content

Commit

Permalink
codegen: now gengo_callbacks is a standalone \'gengo\'s part which ge…
Browse files Browse the repository at this point in the history
…nerates stuff independently from typedefs
  • Loading branch information
gucio321 committed Nov 16, 2024
1 parent ba35a47 commit 99b3f67
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 68 deletions.
87 changes: 64 additions & 23 deletions cmd/codegen/gengo_callbacks.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"regexp"
"sort"
"strings"

"github.com/kpango/glg"
Expand All @@ -18,11 +19,51 @@ const (

var callbackNotGeneratedError = errors.New("callback was not generated")

type callbacksGenerator struct {
goSb, cgoSb *strings.Builder
ctx *Context
}

func GenerateCallbacks(callbacks []CIdentifier, context *Context) (validTypes []CIdentifier, err error) {
result := &callbacksGenerator{
goSb: &strings.Builder{},
cgoSb: &strings.Builder{},
ctx: context,
}

validTypes = make([]CIdentifier, 0)

// sort just in case
sort.Slice(callbacks, func(i, j int) bool {
return callbacks[i] < callbacks[j]
})

for _, typedefName := range callbacks {
if err := result.writeCallback(typedefName, result.ctx.typedefs.data[typedefName]); err != nil {
if errors.Is(err, callbackNotGeneratedError) {
if context.flags.showNotGenerated {
glg.Warnf("Callback \"%s\" was not generated", typedefName)
}

continue
}

return nil, err
}

glg.Successf("Callback \"%s\" was generated", typedefName)

validTypes = append(validTypes, typedefName)
}

return validTypes, nil
}

// This is an underlying function for gengo_typedefs.go for now.
// May be reworked to be a separated "genertor" in the future.
// - includes logging
// - returns CallbackNotGeneratedError
func (g *typedefsGenerator) writeCallback(typedefName CIdentifier, def string, context *Context) error {
func (g *callbacksGenerator) writeCallback(typedefName CIdentifier, def string) error {
// see https://github.com/AllenDang/cimgui-go/issues/224#issuecomment-2452156237
// 1: preprocessing - parse typedefs.data[k] to get return type and arguments

Expand Down Expand Up @@ -192,7 +233,7 @@ func (g *typedefsGenerator) writeCallback(typedefName CIdentifier, def string, c
// 4: Write code
switch cbType {
case callbackType1:
fmt.Fprintf(g.GoSb, `
fmt.Fprintf(g.goSb, `
type %[1]s func(%[4]s) %[2]s
type c%[1]s func(%[5]s) %[3]s
Expand All @@ -205,15 +246,15 @@ func (c %[1]s) C() (C.%[6]s, func()) {
return pool%[1]s.Allocate(c), func() { }
}
`,
typedefName.renameGoIdentifier(context),
typedefName.renameGoIdentifier(g.ctx),
returnType.ArgType,
returnType.CType,
goCallStmt,
cCallStmt,
typedefName,
)
case callbackType2:
fmt.Fprintf(g.GoSb, `
fmt.Fprintf(g.goSb, `
type %[1]s func(%[4]s) %[2]s
type c%[1]s func(%[5]s) %[3]s
Expand All @@ -227,7 +268,7 @@ func (c %[1]s) Handle() (*C.%[6]s, func()) {
return result, func() {}
}
`,
typedefName.renameGoIdentifier(context),
typedefName.renameGoIdentifier(g.ctx),
returnType.ArgType,
returnType.CType,
goCallStmt,
Expand All @@ -242,12 +283,12 @@ func (c %[1]s) Handle() (*C.%[6]s, func()) {
}

if returnType.ArgType == "" {
fmt.Fprintf(g.GoSb, `
fmt.Fprintf(g.goSb, `
func wrap%[1]s(cb %[1]s %[3]s) %[2]s {
cb(%[4]s)
}
`,
typedefName.renameGoIdentifier(context),
typedefName.renameGoIdentifier(g.ctx),
returnType.CType,
cCallStmt2,
func() string {
Expand All @@ -260,7 +301,7 @@ func wrap%[1]s(cb %[1]s %[3]s) %[2]s {
}(),
)
} else {
fmt.Fprintf(g.GoSb, `
fmt.Fprintf(g.goSb, `
func wrap%[1]s(cb %[1]s %[5]s) %[2]s {
result := cb(%[6]s)
%[3]s
Expand All @@ -270,7 +311,7 @@ func wrap%[1]s(cb %[1]s %[5]s) %[2]s {
return %[4]s
}
`,
typedefName.renameGoIdentifier(context),
typedefName.renameGoIdentifier(g.ctx),
returnType.CType,
returnType.ArgDef,
returnType.VarName,
Expand All @@ -287,8 +328,8 @@ func wrap%[1]s(cb %[1]s %[5]s) %[2]s {
)
}

size := context.preset.TypedefsPoolSize
if h, ok := context.preset.TypedefsCustomPoolSizes[typedefName]; ok {
size := g.ctx.preset.TypedefsPoolSize
if h, ok := g.ctx.preset.TypedefsCustomPoolSizes[typedefName]; ok {
size = h
}

Expand All @@ -297,11 +338,11 @@ func wrap%[1]s(cb %[1]s %[5]s) %[2]s {
for i := 0; i < size; i++ {
switch cbType {
case callbackType1:
fmt.Fprintf(g.GoSb,
fmt.Fprintf(g.goSb,
`//export callback%[1]s%[2]d
func callback%[1]s%[2]d(%[5]s) %[3]s { %[4]s wrap%[1]s(pool%[1]s.Get(%[2]d), %[6]s) }
`,
typedefName.renameGoIdentifier(context),
typedefName.renameGoIdentifier(g.ctx),
i,
returnType.CType,
func() string {
Expand All @@ -316,11 +357,11 @@ func callback%[1]s%[2]d(%[5]s) %[3]s { %[4]s wrap%[1]s(pool%[1]s.Get(%[2]d), %[6
)
case callbackType2:

fmt.Fprintf(g.GoSb,
fmt.Fprintf(g.goSb,
`//export callback%[1]s%[2]d
func callback%[1]s%[2]d(%[5]s) %[3]s {%[4]s wrap%[1]s(pool%[1]s.Get(%[2]d), %[6]s) }
`,
typedefName.renameGoIdentifier(context),
typedefName.renameGoIdentifier(g.ctx),
i,
returnType.CType,
func() string {
Expand All @@ -335,11 +376,11 @@ func callback%[1]s%[2]d(%[5]s) %[3]s {%[4]s wrap%[1]s(pool%[1]s.Get(%[2]d), %[6]
)
}

fmt.Fprintf(g.CGoHeaderSb,
fmt.Fprintf(g.cgoSb,
`// extern %[1]s callback%[2]s%[3]d(%[4]s);
`,
returnTypeC,
typedefName.renameGoIdentifier(context),
typedefName.renameGoIdentifier(g.ctx),
i,
func() string {
result := ""
Expand All @@ -354,15 +395,15 @@ func callback%[1]s%[2]d(%[5]s) %[3]s {%[4]s wrap%[1]s(pool%[1]s.Get(%[2]d), %[6]

switch cbType {
case callbackType1:
poolNames[i] = fmt.Sprintf("C.%[3]s(C.callback%[1]s%[2]d)", typedefName.renameGoIdentifier(context), i, typedefName)
poolNames[i] = fmt.Sprintf("C.%[3]s(C.callback%[1]s%[2]d)", typedefName.renameGoIdentifier(g.ctx), i, typedefName)
case callbackType2:
poolNames[i] = fmt.Sprintf("(*C.%[3]s)(C.callback%[1]s%[2]d)", typedefName.renameGoIdentifier(context), i, typedefName)
poolNames[i] = fmt.Sprintf("(*C.%[3]s)(C.callback%[1]s%[2]d)", typedefName.renameGoIdentifier(g.ctx), i, typedefName)
}
}

switch cbType {
case callbackType1:
fmt.Fprintf(g.GoSb, `
fmt.Fprintf(g.goSb, `
var pool%[1]s *internal.Pool[%[1]s, C.%[3]s]
func init() {
pool%[1]s = internal.NewPool[%[1]s, C.%[3]s](
Expand All @@ -374,12 +415,12 @@ func Clear%[1]sPool() {
pool%[1]s.Clear()
}
`,
typedefName.renameGoIdentifier(context),
typedefName.renameGoIdentifier(g.ctx),
strings.Join(poolNames, ",\n")+",\n",
typedefName,
)
case callbackType2:
fmt.Fprintf(g.GoSb, `
fmt.Fprintf(g.goSb, `
var pool%[1]s *internal.Pool[%[1]s, *C.%[3]s]
func init() {
pool%[1]s = internal.NewPool[%[1]s, *C.%[3]s](
Expand All @@ -391,7 +432,7 @@ func Clear%[1]sPool() {
pool%[1]s.Clear()
}
`,
typedefName.renameGoIdentifier(context),
typedefName.renameGoIdentifier(g.ctx),
strings.Join(poolNames, ",\n")+",\n",
typedefName,
)
Expand Down
69 changes: 25 additions & 44 deletions cmd/codegen/gengo_typedefs.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,28 @@ import (
)

type typedefsGenerator struct {
GoSb *strings.Builder
CGoHeaderSb *strings.Builder
HSb *strings.Builder
CppSb *strings.Builder
ctx *Context
GoSb *strings.Builder
HSb *strings.Builder
CppSb *strings.Builder
ctx *Context
}

// GenerateTypedefs will proceed all typedefs from typedefs_dict.json
func GenerateTypedefs(
typedefs *Typedefs,
structs []StructDef,
ctx *Context,
) (validTypeNames []CIdentifier, err error) {
) (validTypeNames, callbacks []CIdentifier, err error) {
// quick counter for coverage control
generatedTypedefs := 0
maxTypedefs := len(typedefs.data)

generator := &typedefsGenerator{
// we need FILES
GoSb: &strings.Builder{},
CGoHeaderSb: &strings.Builder{},
HSb: &strings.Builder{},
CppSb: &strings.Builder{},
ctx: ctx,
GoSb: &strings.Builder{},
HSb: &strings.Builder{},
CppSb: &strings.Builder{},
ctx: ctx,
}

generator.writeHeaders()
Expand Down Expand Up @@ -130,17 +128,8 @@ func GenerateTypedefs(
generatedTypedefs++
validTypeNames = append(validTypeNames, k)
case IsCallbackTypedef(typedefs.data[k]):
if err := generator.writeCallback(k, typedef, ctx); err != nil {
if ctx.flags.showNotGenerated {
glg.Failf("cannot write callback %s: %v", k, err)
continue
}
}

generatedTypedefs++
validTypeNames = append(validTypeNames, k)

glg.Successf("typedef %s is a callback. Implemented.", k)
maxTypedefs--
callbacks = append(callbacks, k)
case HasPrefix(typedefs.data[k], "struct"):
isOpaque := !IsStructName(k, ctx)
if ctx.flags.showGenerated {
Expand All @@ -159,11 +148,11 @@ func GenerateTypedefs(
}

if err := generator.saveToDisk(); err != nil {
return nil, fmt.Errorf("cannot save typedefs to disk: %w", err)
return nil, nil, fmt.Errorf("cannot save typedefs to disk: %w", err)
}

glg.Infof("Typedefs generation complete. Generated %d/%d (%.2f%%) typedefs.", generatedTypedefs, maxTypedefs, float32(generatedTypedefs*100)/float32(maxTypedefs))
return validTypeNames, nil
return validTypeNames, callbacks, nil
}

// Let's say our pureType is of form "short"
Expand Down Expand Up @@ -232,6 +221,17 @@ extern "C" {
#include "%[1]s_typedefs.h"
#include "%[2]s"
`, g.ctx.prefix, g.ctx.flags.include)

g.GoSb.WriteString(getGoPackageHeader(g.ctx))
fmt.Fprintf(g.GoSb,
`// #include <stdlib.h>
// #include <memory.h>
// #include "../imgui/extra_types.h"
// #include "%[1]s_wrapper.h"
// #include "%[1]s_typedefs.h"
import "C"
import "unsafe"
`, g.ctx.prefix)
}

// k is plain C name of the typedef (key in typedefs_dict.json)
Expand Down Expand Up @@ -422,26 +422,7 @@ func (g *typedefsGenerator) saveToDisk() error {
}
#endif`)

typedefsGoResultSb := &strings.Builder{}
typedefsGoResultSb.WriteString(getGoPackageHeader(g.ctx))
fmt.Fprintf(typedefsGoResultSb,
`// #include <stdlib.h>
// #include <memory.h>
// #include "../imgui/extra_types.h"
// #include "%[1]s_wrapper.h"
// #include "%[1]s_typedefs.h"
`, g.ctx.prefix)

typedefsGoResultSb.WriteString(g.CGoHeaderSb.String())

fmt.Fprintf(typedefsGoResultSb,
`import "C"
import "unsafe"
`)

typedefsGoResultSb.WriteString(g.GoSb.String())

if err := os.WriteFile(fmt.Sprintf("%s_typedefs.go", g.ctx.prefix), []byte(typedefsGoResultSb.String()), 0644); err != nil {
if err := os.WriteFile(fmt.Sprintf("%s_typedefs.go", g.ctx.prefix), []byte(g.GoSb.String()), 0644); err != nil {
return fmt.Errorf("cannot write %s_typedefs.go: %w", g.ctx.prefix, err)
}

Expand Down
9 changes: 8 additions & 1 deletion cmd/codegen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,13 +239,20 @@ func main() {
context.enumNames = SliceToMap(enumNames)

// 1.2. Generate Go typedefs
typedefsNames, err := GenerateTypedefs(context.typedefs, context.structs, context)
typedefsNames, callbacksToGenerate, err := GenerateTypedefs(context.typedefs, context.structs, context)
if err != nil {
log.Panic(err)
}

context.typedefsNames = SliceToMap(typedefsNames)

validCallbacks, err := GenerateCallbacks(callbacksToGenerate, context)
if err != nil {
log.Panic(err)
}

context.typedefsNames = MergeMaps(context.typedefsNames, SliceToMap(validCallbacks))

// 1.3. Generate C wrapper
validFuncs, err := generateCppWrapper(flags.prefix, flags.include, context.funcs, context)
if err != nil {
Expand Down

0 comments on commit 99b3f67

Please sign in to comment.