Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge overloads into nocgo #1

Open
wants to merge 28 commits into
base: nocgo
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
922e2e0
Update XML files
hajimehoshi Apr 3, 2019
ce3a028
Add go.mod
hajimehoshi Apr 3, 2019
7d0be8e
Merge pull request #104 from go-gl/xml
errcw Apr 3, 2019
a50212f
Merge pull request #105 from go-gl/gomod
errcw Apr 3, 2019
ef683c5
Ignoring GoLand directory
dertseha May 23, 2020
5545d4d
first attempt at providing overloads
dertseha May 23, 2020
65398df
reading overload information from an XML file
dertseha May 23, 2020
ffa35d5
added example overloads file
dertseha May 23, 2020
c90bbed
allowing parameter names to be changed as well
dertseha May 23, 2020
fc28c39
removed debug printfs
dertseha May 23, 2020
b2aad41
removed obsolete attributes
dertseha May 23, 2020
65d743f
using fixed suffix "WithOffset" for example overloads
dertseha May 27, 2020
8dda71e
renamed List member to Overloads
dertseha May 30, 2020
6d57b8a
extracted single function getter
dertseha May 30, 2020
9664fe7
removed unnecessary Name field from Overload
dertseha May 30, 2020
e338703
added test for signature parsing
dertseha May 30, 2020
f28200d
changed type change description for overloads to use c defintions
dertseha May 30, 2020
9ba163d
extended readme about overload function
dertseha May 30, 2020
8a40325
Updated wording to be more explicit about overloads
dertseha Jun 3, 2020
76a5465
Merge pull request #107 from dertseha/feature/with-offset-overloads
errcw Jun 3, 2020
9abe4ae
Merge branch 'master' into pr/1
neclepsio Jun 19, 2020
c155bd0
Implement Overloads for non-Windows OS
neclepsio Jun 19, 2020
8391f37
Implement overloads for Windows
neclepsio Jun 19, 2020
fb1b2d6
Merge pull request #2 from neclepsio/pr/1
neclepsio Jun 19, 2020
21cd06e
Fix DebugProc
neclepsio Jun 19, 2020
a03605c
Update import path
neclepsio Jun 19, 2020
013f246
Use Syscall18
neclepsio Jun 19, 2020
77db216
Fix debug_windows.tmpl
neclepsio Jun 19, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,6 @@ wgl
# Vim
*.swp
*.swo

# GoLand
.idea/
18 changes: 14 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,31 @@ Features:
- Go functions that mirror the C specification using Go types.
- Support for multiple OpenGL APIs (GL/GLES/EGL/WGL/GLX/EGL), versions, and profiles.
- Support for extensions (including debug callbacks).
- Support for overloads to provide Go functions with different parameter signatures.

See the [open issues](https://github.com/go-gl/glow/issues) for caveats about the current state of the implementation.
See the [open issues](https://github.com/neclepsio/glow/issues) for caveats about the current state of the implementation.

Generated Packages
------------------

Generated OpenGL binding packages are available in the [go-gl/gl](https://github.com/go-gl/gl) repository.
Generated OpenGL binding packages are available in the [go-gl/gl](https://github.com/neclepsio/gl) repository.

Overloads
---------

See subdirectory `xml/overload` for examples. The motivation here is to provide Go functions with different parameter signatures of existing OpenGL functions.

For example, `glVertexAttribPointer(..., void *)` cannot be used with `gl.VertexAttribPointer(..., unsafe.Pointer)` when using arbitrary offset values. The `checkptr` safeguard will abort the program when doing so.
Overloads allow the creation of an additional `gl.VertexAttribPointerWithOffset(..., uintptr)`, which calls the original OpenGL function with appropriate casts.


Custom Packages
---------------

If the prebuilt, go-gettable packages are not suitable for your needs you can build your own. For example,

go get github.com/go-gl/glow
cd $GOPATH/src/github.com/go-gl/glow
go get github.com/neclepsio/glow
cd $GOPATH/src/github.com/neclepsio/glow
go build
./glow download
./glow generate -api=gl -version=3.3 -profile=core -remext=GL_ARB_cl_event
Expand Down
33 changes: 28 additions & 5 deletions functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,38 @@ type Function struct {
GoName string // Go name of the function with the API prefix stripped
Parameters []Parameter
Return Type
Overloads []Overload
}

// An Overload describes an alternative signature for the same function.
type Overload struct {
GoName string // Go name of the original function
OverloadName string // Go name of the overload
Parameters []Parameter
Return Type
}

func (o Overload) function() Function {
return Function{
GoName: o.GoName,
Parameters: o.Parameters,
Return: o.Return,
}
}

// IsImplementedForSyscall reports whether the function is implemented for syscall or not.
func (o Overload) IsImplementedForSyscall() bool {
return o.function().IsImplementedForSyscall()
}

// Syscall returns a syscall expression for Windows.
func (o Overload) Syscall() string {
return o.function().Syscall()
}

// IsImplementedForSyscall reports whether the function is implemented for syscall or not.
func (f Function) IsImplementedForSyscall() bool {
// TODO: Use syscall.Syscall18 when Go 1.12 is the minimum supported version.
if len(f.Parameters) > 15 {
return false
}
return true
return len(f.Parameters) <= 18
}

// Syscall returns a syscall expression for Windows.
Expand Down
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/neclepsio/glow

go 1.12

require golang.org/x/tools v0.0.0-20190402200628-202502a5a924
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190402200628-202502a5a924 h1:XgD7l1aFq63td2d4omZwFUpt50/DxIpXW3yrk+V4EOc=
golang.org/x/tools v0.0.0-20190402200628-202502a5a924/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
14 changes: 12 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (

func generate(name string, args []string) {
flags := flag.NewFlagSet(name, flag.ExitOnError)
dir := importPathToDir("github.com/go-gl/glow")
dir := importPathToDir("github.com/neclepsio/glow")
var (
xmlDir = flags.String("xml", filepath.Join(dir, "xml"), "XML directory")
tmplDir = flags.String("tmpl", filepath.Join(dir, "tmpl"), "Template directory")
Expand Down Expand Up @@ -114,6 +114,7 @@ func performRestriction(pkg *Package, jsonPath string) {

func parseSpecifications(xmlDir string) []*Specification {
specDir := filepath.Join(xmlDir, "spec")
overloadDir := filepath.Join(xmlDir, "overload")
specFiles, err := ioutil.ReadDir(specDir)
if err != nil {
log.Fatalln("error reading spec file entries:", err)
Expand All @@ -124,7 +125,16 @@ func parseSpecifications(xmlDir string) []*Specification {
if !strings.HasSuffix(specFile.Name(), "xml") {
continue
}
spec, err := NewSpecification(filepath.Join(specDir, specFile.Name()))

registry, err := readSpecFile(filepath.Join(specDir, specFile.Name()))
if err != nil {
log.Fatalln("error reading XML spec file: ", specFile.Name(), err)
}
overloads, err := readOverloadFile(filepath.Join(overloadDir, specFile.Name()))
if err != nil {
log.Fatalln("error reading XML overload file: ", specFile.Name(), err)
}
spec, err := NewSpecification(*registry, overloads)
if err != nil {
log.Fatalln("error parsing specification:", specFile.Name(), err)
}
Expand Down
52 changes: 52 additions & 0 deletions overload.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package main

import (
"encoding/xml"
"os"
)

type xmlOverloads struct {
Overloads []xmlOverload `xml:"overload"`
}

type xmlOverload struct {
Name string `xml:"name,attr"`
OverloadName string `xml:"overloadName,attr"`

ParameterChanges []xmlParameterChange `xml:"parameterChanges>change"`
}

type xmlParameterChange struct {
// Index is the zero-based index of the parameter list.
Index int `xml:"index,attr"`
// Name describes a change of the parameter name.
Name *xmlNameChange `xml:"name"`
// Type describes a change of the parameter type.
Type *xmlTypeChange `xml:"type"`
}

type xmlNameChange struct {
Value string `xml:"value,attr"`
}

type xmlTypeChange struct {
Signature string `xml:"signature,attr"`
}

func readOverloadFile(file string) (xmlOverloads, error) {
var overloads xmlOverloads

_, err := os.Stat(file)
if err != nil {
return overloads, nil
}

f, err := os.Open(file)
if err != nil {
return overloads, err
}
defer f.Close()

err = xml.NewDecoder(f).Decode(&overloads)
return overloads, err
}
6 changes: 5 additions & 1 deletion package.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ type PackageFunction struct {
func (f *PackageFunction) Comment() string {
var lines []string
if f.Doc != "" {
lines = append(lines, "// " + f.Doc)
lines = append(lines, "// "+f.Doc)
}

// Adds explanations about C types that are unsafe.Pointer in Go world.
Expand Down Expand Up @@ -182,5 +182,9 @@ func importPathToDir(importPath string) string {
if err != nil {
log.Fatalln(err)
}
if len(pkgs[0].GoFiles) == 0 {
exe, _ := os.Executable()
return filepath.Dir(exe)
}
return filepath.Dir(pkgs[0].GoFiles[0])
}
64 changes: 60 additions & 4 deletions spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,53 @@ func parseFunctions(commands []xmlCommand) (specFunctions, error) {
return functions, nil
}

func parseOverloads(functions specFunctions, overloads xmlOverloads) (specFunctions, error) {
for _, overloadInfo := range overloads.Overloads {
function := functions.getByName(overloadInfo.Name)
if function == nil {
return nil, fmt.Errorf("function <%s> not found to overload", overloadInfo.Name)
}
err := overloadFunction(function, overloadInfo)
if err != nil {
return nil, err
}
}
return functions, nil
}

func overloadFunction(function *Function, info xmlOverload) error {
overload := Overload{
GoName: function.GoName,
OverloadName: info.OverloadName,
Parameters: make([]Parameter, len(function.Parameters)),
Return: function.Return,
}
copy(overload.Parameters, function.Parameters)
for _, change := range info.ParameterChanges {
if (change.Index < 0) || (change.Index >= len(function.Parameters)) {
return fmt.Errorf("overload for <%s> has invalid parameter index", info.Name)
}
param := &overload.Parameters[change.Index]

if change.Type != nil {
_, ctype, err := parseSignature(xmlSignature(change.Type.Signature))
if err != nil {
return fmt.Errorf("failed to parse signature of overload for <%s>: %v", info.Name, err)
}
// store original type definition as a cast, as this most likely will be needed.
param.Type.Cast = param.Type.CDefinition
param.Type.PointerLevel = ctype.PointerLevel
param.Type.Name = ctype.Name
param.Type.CDefinition = ctype.CDefinition
}
if change.Name != nil {
param.Name = change.Name.Value
}
}
function.Overloads = append(function.Overloads, overload)
return nil
}

func parseSignature(signature xmlSignature) (name string, ctype Type, err error) {
readingName := false
readingType := false
Expand Down Expand Up @@ -402,6 +449,15 @@ func (functions specFunctions) get(name, api string) *Function {
return functions[specRef{name, ""}]
}

func (functions specFunctions) getByName(name string) *Function {
for key, function := range functions {
if key.name == name {
return function
}
}
return nil
}

func (enums specEnums) get(name, api string) *Enum {
enum, ok := enums[specRef{name, api}]
if ok {
Expand Down Expand Up @@ -459,14 +515,14 @@ func (addRem *specAddRemSet) shouldInclude(pkgSpec *PackageSpec) bool {
return true
}

// NewSpecification creates a new specification based on an XML file.
func NewSpecification(file string) (*Specification, error) {
registry, err := readSpecFile(file)
// NewSpecification creates a new specification based on an XML registry.
func NewSpecification(registry xmlRegistry, overloads xmlOverloads) (*Specification, error) {
functions, err := parseFunctions(registry.Commands)
if err != nil {
return nil, err
}

functions, err := parseFunctions(registry.Commands)
functions, err = parseOverloads(functions, overloads)
if err != nil {
return nil, err
}
Expand Down
80 changes: 80 additions & 0 deletions spec_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package main

import "testing"

func TestParseSignature(t *testing.T) {
tt := []struct {
input string
expectedName string
expectedType Type
}{
{
input: "const void *<name>pointer</name>",
expectedName: "pointer",
expectedType: Type{
Name: "void",
PointerLevel: 1,
CDefinition: "const void *",
},
},
{
input: "<ptype>GLsizei</ptype> <name>stride</name>",
expectedName: "stride",
expectedType: Type{
Name: "GLsizei",
PointerLevel: 0,
CDefinition: "GLsizei ",
},
},
{
input: "const <ptype>GLuint</ptype> *<name>value</name>",
expectedName: "value",
expectedType: Type{
Name: "GLuint",
PointerLevel: 1,
CDefinition: "const GLuint *",
},
},
{
input: "<ptype>GLuint</ptype> <name>baseAndCount</name>[2]",
expectedName: "baseAndCount",
expectedType: Type{
Name: "GLuint",
PointerLevel: 1,
CDefinition: "GLuint *",
},
},
{
input: "uintptr_t **",
expectedName: "",
expectedType: Type{
Name: "uintptr_t",
PointerLevel: 2,
CDefinition: "uintptr_t **",
},
},
}

for _, tc := range tt {
tc := tc
t.Run(tc.input, func(t *testing.T) {
name, ctype, err := parseSignature(xmlSignature(tc.input))
failed := false
if err != nil {
t.Logf("parseSignature returned error: %v", err)
failed = true
}
if name != tc.expectedName {
t.Logf("name [%s] does not match expected [%s]", name, tc.expectedName)
failed = true
}
if ctype != tc.expectedType {
t.Logf("type [%v] does not match expected [%v]", ctype, tc.expectedType)
failed = true
}
if failed {
t.Fail()
}
})
}
}
9 changes: 9 additions & 0 deletions test.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@echo off
go build
glow generate -out=../gl/v3.3-core/gl/ -api=gl -version=3.3 -profile=core -xml=../glow/xml/

echo Building...
pushd .
cd ../gl/v3.3-core/gl
go build
popd
2 changes: 1 addition & 1 deletion tmpl/conversions.tmpl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//glow:keepspace

// Code generated by glow (https://github.com/go-gl/glow). DO NOT EDIT.
// Code generated by glow (https://github.com/neclepsio/glow). DO NOT EDIT.

package {{.Name}}

Expand Down
Loading