├── .github └── workflows │ └── go.yml ├── .gitignore ├── LICENSE ├── README.md ├── asmcall ├── asmcall.go ├── cdecl │ ├── cdecl.go │ ├── cdecl_386.s │ ├── cdecl_posix.go │ ├── cdecl_posix_amd64.s │ ├── cdecl_test.go │ ├── cdecl_windows.go │ └── cdecl_windows_amd64.s ├── internal │ └── asmcalltest │ │ ├── asmcalltest.go │ │ ├── asmcalltest_windows.go │ │ └── test.cpp ├── stdcall │ ├── stdcall.go │ ├── stdcall_386.s │ ├── stdcall_amd64.s │ ├── stdcall_posix.go │ ├── stdcall_windows.go │ └── stdcall_windows_test.go └── thiscall │ ├── thiscall.go │ ├── thiscall_386.s │ ├── thiscall_amd64.s │ ├── thiscall_posix.go │ ├── thiscall_windows.go │ └── thiscall_windows_test.go ├── call.go ├── call_posix.go ├── call_windows.go ├── cpp.go ├── cpp_posix_test.go ├── cpp_test.go ├── cpp_windows_test.go ├── dl ├── dl.go ├── dl_posix.go └── dl_windows.go ├── fixtures ├── build.bat ├── build.sh └── dll.cpp ├── go.mod ├── go.sum ├── internal └── cpptest │ ├── cpptest.cpp │ └── cpptest.go ├── ptr.go └── util.go /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | jobs: 10 | build: 11 | strategy: 12 | matrix: 13 | os: [ubuntu-latest, windows-latest, macos-latest] 14 | runs-on: ${{matrix.os}} 15 | steps: 16 | - uses: actions/checkout@v2 17 | 18 | - name: Setup Go 19 | uses: actions/setup-go@v2 20 | with: 21 | go-version: "^1.16" 22 | 23 | - name: Setup Modules 24 | run: go mod download 25 | 26 | - name: Test 27 | run: go test ./... 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /fixtures/*.dll 2 | /fixtures/*.so 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Loren Segal 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cppgo 2 | 3 | [![Build Status](https://img.shields.io/travis/lsegal/cppgo.svg)](https://travis-ci.org/lsegal/cppgo) 4 | [![GoDoc](http://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/lsegal/cppgo) 5 | [![MIT License](http://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/lsegal/cppgo/blob/master/LICENSE.txt) 6 | 7 | This library allows methods on C++ objects to be called directly from the 8 | Go runtime without requiring cgo compilation. 9 | 10 | To set up a Go object that proxies method calls to a C++ object: 11 | 12 | 1. Define a Go struct type with function pointer field declarations that match 13 | the C++ class declaration, 14 | 2. Get the address of the C++ object in memory as a `uintptr` type, 15 | 3. Call `cpp.ConvertRef(addr, &o)` to point this proxy struct object (`o`) at 16 | the C++ object by its `addr`. 17 | 4. After this initial setup, the function pointers on the struct object will be 18 | ready to use like any Go method. 19 | 20 | ## Example Usage 21 | 22 | The following example will call into a C++ class `Library` with a 23 | `GetString(char *name)` object method prototype. See the "STEP X" comments 24 | for usage guides: 25 | 26 | ```go 27 | package main 28 | 29 | import ( 30 | "fmt" 31 | 32 | cpp "github.com/lsegal/cppgo" 33 | "github.com/lsegal/cppgo/dl" 34 | ) 35 | 36 | var ( 37 | dll = dl.Open("mylib.dll") 38 | create = dll.Load("new_object") 39 | ) 40 | 41 | // STEP 1. define our C++ proxy struct type with function pointers. 42 | // The functions in this struct will be filled in by the `cpp.ConvertRef()` 43 | // function, at which point this struct will proxy all method calls to the 44 | // C++ object. 45 | type Library struct { 46 | GetString func(name string) string 47 | } 48 | 49 | func main() { 50 | // STEP 2. get an address for the C++ object 51 | // NOTE: you may need to free this later depending on call semantics. 52 | o, _ := create.Call() // o is a uintptr 53 | 54 | // STEP 3. point our proxy structure at the functions located in the object 55 | // that we got from step 2. 56 | var lib Library 57 | if err := cpp.ConvertRef(o, &lib); err != nil { 58 | panic(err) 59 | } 60 | 61 | // STEP 4. call the function with arguments 62 | fmt.Println(lib.GetString("Loren")) 63 | 64 | // Clean up library 65 | dll.Close() 66 | } 67 | ``` 68 | 69 | The C++ class for the above program could look something like: 70 | 71 | ```cpp 72 | #include 73 | 74 | #ifndef WIN32 75 | # define __declspec(x) 76 | #endif 77 | 78 | class Library { 79 | std::string str_; 80 | 81 | public: 82 | virtual const char *GetString(char *name) { 83 | str_ = "Hello, " + std::string(name) + "!"; 84 | return str_.c_str(); 85 | } 86 | }; 87 | 88 | extern "C" __declspec(dllexport) Library* new_object() { 89 | return new Library(); 90 | } 91 | ``` 92 | 93 | ## Getting a C++ Object Pointer 94 | 95 | In some cases you may also need to figure out how to get access to the C++ 96 | object you want to call into. Although you may already have the object pointer 97 | in memory, sometimes this object will come from a library call. This library 98 | also abstracts the loading of dynamic libraries through the `dl` package: 99 | 100 | ```go 101 | dll := dl.Open("path/to/library") 102 | fn := dll.Load("get_object") 103 | result, err := fn.Call() 104 | 105 | // result now olds a uintptr to your object 106 | 107 | // call this when you are done with the C++ object. 108 | // this may not be at the end of the load function. 109 | dll.Close() 110 | ``` 111 | 112 | See documentation for `dl` for more information on advanced usage. 113 | 114 | ## Using `__stdcall` & `__cdecl` Calling Convention on Windows 115 | 116 | By default, this library will use the "default" calling convention for 117 | C++ methods on that platform. For POSIX systems, this is `__cdecl`, but on 118 | Windows, the default calling convention is `__thiscall`. 119 | 120 | In short, if you are using the default calling convention for your C++ methods, 121 | you do not have to do anything differently from the above example. However, 122 | if you encounter methods tagged with `__stdcall` or `__cdecl`, you can 123 | support these conventions by adding a `call:"std"` or `call:"cdecl"` 124 | tag on the field declaration respectively: 125 | 126 | ```go 127 | type Library struct { 128 | GetID() int `call:"std"` // "stdcall" is also valid here 129 | GetName() string `call:"cdecl"` 130 | } 131 | ``` 132 | 133 | This will ensure that the function pointer is compatible with the library's 134 | calling convention. 135 | 136 | ## Caveats & Gotchas 137 | 138 | ### Limited Type Conversion 139 | 140 | You can define arbitrary function arguments and return types, but the internal 141 | translation does not support a full range of types. Currently, only the 142 | following types are supported: 143 | 144 | * `uint*`, `int*`, `string`, `uintptr` 145 | 146 | Any other type of pointer will be passed as a pointer directly, which may be 147 | what you want, but may not be. For any type that isn't well converted by 148 | the library, use `uintptr` to send its address. Note also that floating points 149 | are explicitly unsupported. 150 | 151 | Note that slices are not well supported due to the extra information 152 | encoded in a Go slice. 153 | 154 | Note also that `string` converts only to and from the `char*` C type, in other 155 | words, C strings. The `std::cstring` or `wchar_t` types are not yet supported. 156 | 157 | ### Passing Objects as Arguments 158 | 159 | When passing C++ objects to methods, you will want to use the `cpp.Ptr` or 160 | `uintptr` value representing the address of the object. You cannot use the 161 | pointer to the proxy struct, since this does not actually point to the 162 | object, and cppgo does not know how to translate between the two. 163 | 164 | For example, to send objects to methods, define a function and call it using 165 | the bare address pointers: 166 | 167 | ```go 168 | // C++ class defined as: 169 | // 170 | // class MyClass { 171 | // virtual void DoWork(MyClass *other); 172 | // } 173 | type MyClass struct { 174 | DoWork(obj uintptr) 175 | } 176 | 177 | func main() { 178 | var m1 MyClass 179 | var m2 MyClass 180 | o1 := get_object_address() // uintptr 181 | o2 := get_object_address() // uintptr 182 | 183 | cpp.ConvertRef(o, &m1) 184 | cpp.ConvertRef(o, &m2) 185 | 186 | // we may have m2 here, but we call DoWork() with the uintptr address. 187 | m1.DoWork(o2) 188 | } 189 | ``` 190 | 191 | ### Non-Virtual Functions Not Supported 192 | 193 | This library does not yet support non-virtual functions. Only functions 194 | defined with the `virtual` keyword are callable. 195 | 196 | ## Author & License 197 | 198 | Written by Loren Segal in 2017, licensed under MIT License (see LICENSE). 199 | -------------------------------------------------------------------------------- /asmcall/asmcall.go: -------------------------------------------------------------------------------- 1 | // Package asmcall implements low-level ABI method calls for various C calling 2 | // conventions without using cgo. 3 | package asmcall 4 | -------------------------------------------------------------------------------- /asmcall/cdecl/cdecl.go: -------------------------------------------------------------------------------- 1 | // Package cdecl implements method call ABI for the POSIX C/C++ calling 2 | // convention. 3 | // 4 | // Note that this package does not rely on cgo to implement calling, so a 5 | // compiler is not needed to call into C functions using this library. 6 | package cdecl 7 | 8 | import "errors" 9 | 10 | // Call calls a cdecl style function at memory address addr with the arguments 11 | // list a. The function result value is returned as a uintptr to be translated 12 | // by the caller. If the function cannot be called (usually due to an invalid 13 | // number of argument), an error is returned. 14 | func Call(addr uintptr, a ...uintptr) (uintptr, error) { 15 | switch l := len(a); l { 16 | case 0: 17 | return call0(addr), nil 18 | case 1: 19 | return call1(addr, a[0]), nil 20 | case 2: 21 | return call2(addr, a[0], a[1]), nil 22 | case 3: 23 | return call3(addr, a[0], a[1], a[2]), nil 24 | case 4: 25 | return call4(addr, a[0], a[1], a[2], a[3]), nil 26 | case 5: 27 | return call5(addr, a[0], a[1], a[2], a[3], a[4]), nil 28 | case 6: 29 | return call6(addr, a[0], a[1], a[2], a[3], a[4], a[5]), nil 30 | default: 31 | return 0, errors.New("too many arguments") 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /asmcall/cdecl/cdecl_386.s: -------------------------------------------------------------------------------- 1 | TEXT ·call0(SB),4,$0-8 2 | MOVL addr+0(FP), AX 3 | CALL AX 4 | MOVL AX, ret+4(FP) 5 | RET 6 | 7 | TEXT ·call1(SB),4,$4-12 8 | MOVL a+4(FP), BX 9 | MOVL BX, 0(SP) 10 | 11 | MOVL addr+0(FP), AX 12 | CALL AX 13 | MOVL AX, ret+8(FP) 14 | RET 15 | 16 | TEXT ·call2(SB),4,$8-16 17 | MOVL a+4(FP), BX 18 | MOVL BX, 0(SP) 19 | MOVL b+8(FP), BX 20 | MOVL BX, 4(SP) 21 | 22 | MOVL addr+0(FP), AX 23 | CALL AX 24 | MOVL AX, ret+12(FP) 25 | RET 26 | 27 | TEXT ·call3(SB),4,$12-20 28 | MOVL a+4(FP), BX 29 | MOVL BX, 0(SP) 30 | MOVL b+8(FP), BX 31 | MOVL BX, 4(SP) 32 | MOVL c+12(FP), BX 33 | MOVL BX, 8(SP) 34 | 35 | MOVL addr+0(FP), AX 36 | CALL AX 37 | MOVL AX, ret+16(FP) 38 | RET 39 | 40 | TEXT ·call4(SB),4,$16-24 41 | MOVL a+4(FP), BX 42 | MOVL BX, 0(SP) 43 | MOVL b+8(FP), BX 44 | MOVL BX, 4(SP) 45 | MOVL c+12(FP), BX 46 | MOVL BX, 8(SP) 47 | MOVL d+16(FP), BX 48 | MOVL BX, 12(SP) 49 | 50 | MOVL addr+0(FP), AX 51 | CALL AX 52 | MOVL AX, ret+20(FP) 53 | RET 54 | 55 | TEXT ·call5(SB),4,$20-28 56 | MOVL a+4(FP), BX 57 | MOVL BX, 0(SP) 58 | MOVL b+8(FP), BX 59 | MOVL BX, 4(SP) 60 | MOVL c+12(FP), BX 61 | MOVL BX, 8(SP) 62 | MOVL d+16(FP), BX 63 | MOVL BX, 12(SP) 64 | MOVL e+20(FP), BX 65 | MOVL BX, 16(SP) 66 | 67 | MOVL addr+0(FP), AX 68 | CALL AX 69 | MOVL AX, ret+24(FP) 70 | RET 71 | 72 | TEXT ·call6(SB),4,$24-32 73 | MOVL a+4(FP), BX 74 | MOVL BX, 0(SP) 75 | MOVL b+8(FP), BX 76 | MOVL BX, 4(SP) 77 | MOVL c+12(FP), BX 78 | MOVL BX, 8(SP) 79 | MOVL d+16(FP), BX 80 | MOVL BX, 12(SP) 81 | MOVL e+20(FP), BX 82 | MOVL BX, 16(SP) 83 | MOVL f+24(FP), BX 84 | MOVL BX, 20(SP) 85 | 86 | MOVL addr+0(FP), AX 87 | CALL AX 88 | MOVL AX, ret+28(FP) 89 | RET 90 | -------------------------------------------------------------------------------- /asmcall/cdecl/cdecl_posix.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package cdecl 4 | 5 | func call0(addr uintptr) uintptr 6 | func call1(addr uintptr, a uintptr) uintptr 7 | func call2(addr uintptr, a uintptr, b uintptr) uintptr 8 | func call3(addr uintptr, a uintptr, b uintptr, c uintptr) uintptr 9 | func call4(addr uintptr, a uintptr, b uintptr, c uintptr, d uintptr) uintptr 10 | func call5(addr uintptr, a uintptr, b uintptr, c uintptr, d uintptr, e uintptr) uintptr 11 | func call6(addr uintptr, a uintptr, b uintptr, c uintptr, d uintptr, e uintptr, f uintptr) uintptr 12 | -------------------------------------------------------------------------------- /asmcall/cdecl/cdecl_posix_amd64.s: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | TEXT ·call0(SB),4,$0-16 4 | MOVQ addr+0(FP), AX 5 | CALL AX 6 | MOVQ AX, ret+8(FP) 7 | RET 8 | 9 | TEXT ·call1(SB),4,$0-24 10 | MOVQ a+8(FP), DI 11 | 12 | MOVQ addr+0(FP), AX 13 | CALL AX 14 | MOVQ AX, ret+16(FP) 15 | RET 16 | 17 | TEXT ·call2(SB),4,$0-32 18 | MOVQ a+8(FP), DI 19 | MOVQ b+16(FP), SI 20 | 21 | MOVQ addr+0(FP), AX 22 | CALL AX 23 | MOVQ AX, ret+24(FP) 24 | RET 25 | 26 | TEXT ·call3(SB),4,$0-40 27 | MOVQ a+8(FP), DI 28 | MOVQ b+16(FP), SI 29 | MOVQ c+24(FP), DX 30 | 31 | MOVQ addr+0(FP), AX 32 | CALL AX 33 | MOVQ AX, ret+32(FP) 34 | RET 35 | 36 | TEXT ·call4(SB),4,$0-48 37 | MOVQ a+8(FP), DI 38 | MOVQ b+16(FP), SI 39 | MOVQ c+24(FP), DX 40 | MOVQ d+32(FP), CX 41 | 42 | MOVQ addr+0(FP), AX 43 | CALL AX 44 | MOVQ AX, ret+40(FP) 45 | RET 46 | 47 | TEXT ·call5(SB),4,$0-56 48 | MOVQ a+8(FP), DI 49 | MOVQ b+16(FP), SI 50 | MOVQ c+24(FP), DX 51 | MOVQ d+32(FP), CX 52 | MOVQ e+40(FP), R8 53 | 54 | MOVQ addr+0(FP), AX 55 | CALL AX 56 | MOVQ AX, ret+48(FP) 57 | RET 58 | 59 | TEXT ·call6(SB),4,$0-64 60 | MOVQ a+8(FP), DI 61 | MOVQ b+16(FP), SI 62 | MOVQ c+24(FP), DX 63 | MOVQ d+32(FP), CX 64 | MOVQ e+40(FP), R8 65 | MOVQ f+48(FP), R9 66 | 67 | MOVQ addr+0(FP), AX 68 | CALL AX 69 | MOVQ AX, ret+56(FP) 70 | RET 71 | -------------------------------------------------------------------------------- /asmcall/cdecl/cdecl_test.go: -------------------------------------------------------------------------------- 1 | package cdecl 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/lsegal/cppgo/asmcall/internal/asmcalltest" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestCall(t *testing.T) { 11 | v, e := Call(asmcalltest.GetCdeclcallF0Addr()) 12 | assert.NoError(t, e) 13 | assert.Equal(t, uintptr(42), v) 14 | 15 | v, e = Call(asmcalltest.GetCdeclcallF1Addr(), 16) 16 | assert.NoError(t, e) 17 | assert.Equal(t, uintptr(17), v) 18 | 19 | v, e = Call(asmcalltest.GetCdeclcallF2Addr(), 4, 2) 20 | assert.NoError(t, e) 21 | assert.Equal(t, uintptr(2), v) 22 | 23 | v, e = Call(asmcalltest.GetCdeclcallF3Addr(), 4, 2, 2) 24 | assert.NoError(t, e) 25 | assert.Equal(t, uintptr(1), v) 26 | 27 | v, e = Call(asmcalltest.GetCdeclcallF4Addr(), 16, 2, 4, 2) 28 | assert.NoError(t, e) 29 | assert.Equal(t, uintptr(1), v) 30 | 31 | v, e = Call(asmcalltest.GetCdeclcallF5Addr(), 99, 99, 99, 99, 12) 32 | assert.NoError(t, e) 33 | assert.Equal(t, uintptr(12), v) 34 | 35 | v, e = Call(asmcalltest.GetCdeclcallF6Addr(), 99, 99, 99, 99, 2, 3) 36 | assert.NoError(t, e) 37 | assert.Equal(t, uintptr(6), v) 38 | } 39 | -------------------------------------------------------------------------------- /asmcall/cdecl/cdecl_windows.go: -------------------------------------------------------------------------------- 1 | package cdecl 2 | 3 | func call0(addr uintptr) uintptr 4 | func call1(addr uintptr, a uintptr) uintptr 5 | func call2(addr uintptr, a uintptr, b uintptr) uintptr 6 | func call3(addr uintptr, a uintptr, b uintptr, c uintptr) uintptr 7 | func call4(addr uintptr, a uintptr, b uintptr, c uintptr, d uintptr) uintptr 8 | func call5(addr uintptr, a uintptr, b uintptr, c uintptr, d uintptr, e uintptr) uintptr 9 | func call6(addr uintptr, a uintptr, b uintptr, c uintptr, d uintptr, e uintptr, f uintptr) uintptr 10 | -------------------------------------------------------------------------------- /asmcall/cdecl/cdecl_windows_amd64.s: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | TEXT ·call0(SB),4,$32-16 4 | MOVQ addr+0(FP), AX 5 | CALL AX 6 | MOVQ AX, ret+8(FP) 7 | RET 8 | 9 | TEXT ·call1(SB),0,$32-24 10 | MOVQ a+8(FP), CX 11 | 12 | MOVQ addr+0(FP), AX 13 | CALL AX 14 | MOVQ AX, ret+16(FP) 15 | RET 16 | 17 | TEXT ·call2(SB),0,$32-32 18 | MOVQ a+8(FP), CX 19 | MOVQ b+16(FP), DX 20 | 21 | MOVQ addr+0(FP), AX 22 | CALL AX 23 | MOVQ AX, ret+24(FP) 24 | RET 25 | 26 | TEXT ·call3(SB),0,$32-40 27 | MOVQ a+8(FP), CX 28 | MOVQ b+16(FP), DX 29 | MOVQ c+24(FP), R8 30 | 31 | MOVQ addr+0(FP), AX 32 | CALL AX 33 | MOVQ AX, ret+32(FP) 34 | RET 35 | 36 | TEXT ·call4(SB),0,$32-48 37 | MOVQ a+8(FP), CX 38 | MOVQ b+16(FP), DX 39 | MOVQ c+24(FP), R8 40 | MOVQ d+32(FP), R9 41 | 42 | MOVQ addr+0(FP), AX 43 | CALL AX 44 | MOVQ AX, ret+40(FP) 45 | RET 46 | 47 | TEXT ·call5(SB),0,$40-56 48 | MOVQ a+8(FP), CX 49 | MOVQ b+16(FP), DX 50 | MOVQ c+24(FP), R8 51 | MOVQ d+32(FP), R9 52 | MOVQ e+40(FP), BX 53 | MOVQ BX, e-8(SP) 54 | 55 | MOVQ addr+0(FP), AX 56 | CALL AX 57 | MOVQ AX, ret+48(FP) 58 | RET 59 | 60 | TEXT ·call6(SB),0,$48-64 61 | MOVQ a+8(FP), CX 62 | MOVQ b+16(FP), DX 63 | MOVQ c+24(FP), R8 64 | MOVQ d+32(FP), R9 65 | MOVQ e+40(FP), BX 66 | MOVQ BX, e-16(SP) 67 | MOVQ f+48(FP), BX 68 | MOVQ BX, f-8(SP) 69 | 70 | MOVQ addr+0(FP), AX 71 | CALL AX 72 | MOVQ AX, ret+56(FP) 73 | RET 74 | -------------------------------------------------------------------------------- /asmcall/internal/asmcalltest/asmcalltest.go: -------------------------------------------------------------------------------- 1 | package asmcalltest 2 | 3 | /* 4 | #include 5 | #ifndef WIN32 6 | # define __cdecl 7 | #endif 8 | 9 | static int __cdecl f0() { return 42; } 10 | static int __cdecl f1(int x) { return x + 1; } 11 | static int __cdecl f2(int x, int y) { return x / y; } 12 | static int __cdecl f3(int x, int y, int z) { return x / y / z; } 13 | static int __cdecl f4(int a, int b, int c, int d) { return a / b / c / d; } 14 | static int __cdecl f5(int a, int b, int c, int d, int e) { return e; } 15 | static int __cdecl f6(int a, int b, int c, int d, int e, int f) { return e * f; } 16 | 17 | static void *cdeclcall_addr_f0() { return (void *)f0; } 18 | static void *cdeclcall_addr_f1() { return (void *)f1; } 19 | static void *cdeclcall_addr_f2() { return (void *)f2; } 20 | static void *cdeclcall_addr_f3() { return (void *)f3; } 21 | static void *cdeclcall_addr_f4() { return (void *)f4; } 22 | static void *cdeclcall_addr_f5() { return (void *)f5; } 23 | static void *cdeclcall_addr_f6() { return (void *)f6; } 24 | */ 25 | import "C" 26 | 27 | func GetCdeclcallF0Addr() uintptr { 28 | return uintptr(C.cdeclcall_addr_f0()) 29 | } 30 | 31 | func GetCdeclcallF1Addr() uintptr { 32 | return uintptr(C.cdeclcall_addr_f1()) 33 | } 34 | 35 | func GetCdeclcallF2Addr() uintptr { 36 | return uintptr(C.cdeclcall_addr_f2()) 37 | } 38 | 39 | func GetCdeclcallF3Addr() uintptr { 40 | return uintptr(C.cdeclcall_addr_f3()) 41 | } 42 | 43 | func GetCdeclcallF4Addr() uintptr { 44 | return uintptr(C.cdeclcall_addr_f4()) 45 | } 46 | 47 | func GetCdeclcallF5Addr() uintptr { 48 | return uintptr(C.cdeclcall_addr_f5()) 49 | } 50 | 51 | func GetCdeclcallF6Addr() uintptr { 52 | return uintptr(C.cdeclcall_addr_f6()) 53 | } 54 | -------------------------------------------------------------------------------- /asmcall/internal/asmcalltest/asmcalltest_windows.go: -------------------------------------------------------------------------------- 1 | package asmcalltest 2 | 3 | /* 4 | #include 5 | static int __stdcall f0() { return 42; } 6 | static int __stdcall f1(int x) { return x + 1; } 7 | static int __stdcall f2(int x, int y) { return x / y; } 8 | static int __stdcall f3(int x, int y, int z) { return x / y / z; } 9 | static int __stdcall f4(int a, int b, int c, int d) { return a / b / c / d; } 10 | static int __stdcall f5(int a, int b, int c, int d, int e) { return e; } 11 | static int __stdcall f6(int a, int b, int c, int d, int e, int f) { return e * f; } 12 | 13 | static void *stdcall_addr_f0() { return (void *)f0; } 14 | static void *stdcall_addr_f1() { return (void *)f1; } 15 | static void *stdcall_addr_f2() { return (void *)f2; } 16 | static void *stdcall_addr_f3() { return (void *)f3; } 17 | static void *stdcall_addr_f4() { return (void *)f4; } 18 | static void *stdcall_addr_f5() { return (void *)f5; } 19 | static void *stdcall_addr_f6() { return (void *)f6; } 20 | 21 | extern void *thiscall_obj(); 22 | extern void *thiscall_addr_f0(); 23 | extern void *thiscall_addr_f1(); 24 | extern void *thiscall_addr_f2(); 25 | extern void *thiscall_addr_f3(); 26 | extern void *thiscall_addr_f4(); 27 | extern void *thiscall_addr_f5(); 28 | */ 29 | import "C" 30 | 31 | func GetStdcallF0Addr() uintptr { 32 | return uintptr(C.stdcall_addr_f0()) 33 | } 34 | 35 | func GetStdcallF1Addr() uintptr { 36 | return uintptr(C.stdcall_addr_f1()) 37 | } 38 | 39 | func GetStdcallF2Addr() uintptr { 40 | return uintptr(C.stdcall_addr_f2()) 41 | } 42 | 43 | func GetStdcallF3Addr() uintptr { 44 | return uintptr(C.stdcall_addr_f3()) 45 | } 46 | 47 | func GetStdcallF4Addr() uintptr { 48 | return uintptr(C.stdcall_addr_f4()) 49 | } 50 | 51 | func GetStdcallF5Addr() uintptr { 52 | return uintptr(C.stdcall_addr_f5()) 53 | } 54 | 55 | func GetStdcallF6Addr() uintptr { 56 | return uintptr(C.stdcall_addr_f6()) 57 | } 58 | 59 | func GetThiscallObj() uintptr { 60 | return uintptr(C.thiscall_obj()) 61 | } 62 | 63 | func GetThiscallF0Addr() uintptr { 64 | return uintptr(C.thiscall_addr_f0()) 65 | } 66 | 67 | func GetThiscallF1Addr() uintptr { 68 | return uintptr(C.thiscall_addr_f1()) 69 | } 70 | 71 | func GetThiscallF2Addr() uintptr { 72 | return uintptr(C.thiscall_addr_f2()) 73 | } 74 | 75 | func GetThiscallF3Addr() uintptr { 76 | return uintptr(C.thiscall_addr_f3()) 77 | } 78 | 79 | func GetThiscallF4Addr() uintptr { 80 | return uintptr(C.thiscall_addr_f4()) 81 | } 82 | 83 | func GetThiscallF5Addr() uintptr { 84 | return uintptr(C.thiscall_addr_f5()) 85 | } 86 | -------------------------------------------------------------------------------- /asmcall/internal/asmcalltest/test.cpp: -------------------------------------------------------------------------------- 1 | class Test { 2 | public: 3 | int f0() { return this->_f0(); } 4 | int f1(int x) { return this->_f1(x); } 5 | int f2(int x, int y) { return this->_f2(x, y); } 6 | int f3(int x, int y, int z) { return this->_f3(x, y, z); } 7 | int f4(int a, int b, int c, int d) { return this->_f4(a, b, c, d); } 8 | int f5(int a, int b, int c, int d, int e) { return this->_f5(a, b, c, d, e); } 9 | private: 10 | int _f0() { return 42; } 11 | int _f1(int x) { return x + 1; } 12 | int _f2(int x, int y) { return x / y; } 13 | int _f3(int x, int y, int z) { return x / y / z; } 14 | int _f4(int a, int b, int c, int d) { return a / b / c / d; } 15 | int _f5(int a, int b, int c, int d, int e) { return e; } 16 | }; 17 | 18 | typedef int (Test::*tf0)(); 19 | typedef int (Test::*tf1)(int); 20 | typedef int (Test::*tf2)(int, int); 21 | typedef int (Test::*tf3)(int, int, int); 22 | typedef int (Test::*tf4)(int, int, int, int); 23 | typedef int (Test::*tf5)(int, int, int, int, int); 24 | 25 | extern "C" 26 | { 27 | 28 | void *thiscall_obj() { return new Test(); } 29 | 30 | #ifdef WIN32 31 | #pragma GCC diagnostic ignored "-Wpmf-conversions" 32 | void *thiscall_addr_f0() { return (void *)&Test::f0; } 33 | void *thiscall_addr_f1() { return (void *)&Test::f1; } 34 | void *thiscall_addr_f2() { return (void *)&Test::f2; } 35 | void *thiscall_addr_f3() { return (void *)&Test::f3; } 36 | void *thiscall_addr_f4() { return (void *)&Test::f4; } 37 | void *thiscall_addr_f5() { return (void *)&Test::f5; } 38 | #else /* !WIN32 */ 39 | tf0 thiscall_addr_f0() { return &Test::f0; } 40 | tf1 thiscall_addr_f1() { return &Test::f1; } 41 | tf2 thiscall_addr_f2() { return &Test::f2; } 42 | tf3 thiscall_addr_f3() { return &Test::f3; } 43 | tf4 thiscall_addr_f4() { return &Test::f4; } 44 | tf5 thiscall_addr_f5() { return &Test::f5; } 45 | #endif /* WIN32 */ 46 | 47 | } 48 | -------------------------------------------------------------------------------- /asmcall/stdcall/stdcall.go: -------------------------------------------------------------------------------- 1 | // Package stdcall implements method call ABI for the __stdcall calling 2 | // convention. 3 | // 4 | // This package is only supported on Windows. 5 | package stdcall 6 | 7 | // Call calls a stdcall style function at memory address addr with the arguments 8 | // list a. The function result value is returned as a uintptr to be translated 9 | // by the caller. If the function cannot be called (usually due to an invalid 10 | // number of argument), an error is returned. 11 | func Call(addr uintptr, a ...uintptr) (uintptr, error) { 12 | return call(addr, a...) 13 | } 14 | -------------------------------------------------------------------------------- /asmcall/stdcall/stdcall_386.s: -------------------------------------------------------------------------------- 1 | TEXT ·call0(SB),4,$0-8 2 | MOVL addr+0(FP), AX 3 | CALL AX 4 | MOVL AX, ret+4(FP) 5 | RET 6 | 7 | TEXT ·call1(SB),4,$4-12 8 | MOVL a+4(FP), BX 9 | MOVL BX, 0(SP) 10 | 11 | MOVL addr+0(FP), AX 12 | CALL AX 13 | SUBL $4, SP 14 | MOVL AX, ret+8(FP) 15 | RET 16 | 17 | TEXT ·call2(SB),4,$8-16 18 | MOVL a+4(FP), BX 19 | MOVL BX, 0(SP) 20 | MOVL b+8(FP), BX 21 | MOVL BX, 4(SP) 22 | 23 | MOVL addr+0(FP), AX 24 | CALL AX 25 | SUBL $8, SP 26 | MOVL AX, ret+12(FP) 27 | RET 28 | 29 | TEXT ·call3(SB),4,$12-20 30 | MOVL a+4(FP), BX 31 | MOVL BX, 0(SP) 32 | MOVL b+8(FP), BX 33 | MOVL BX, 4(SP) 34 | MOVL c+12(FP), BX 35 | MOVL BX, 8(SP) 36 | 37 | MOVL addr+0(FP), AX 38 | CALL AX 39 | SUBL $12, SP 40 | MOVL AX, ret+16(FP) 41 | RET 42 | 43 | TEXT ·call4(SB),4,$16-24 44 | MOVL a+4(FP), BX 45 | MOVL BX, 0(SP) 46 | MOVL b+8(FP), BX 47 | MOVL BX, 4(SP) 48 | MOVL c+12(FP), BX 49 | MOVL BX, 8(SP) 50 | MOVL d+16(FP), BX 51 | MOVL BX, 12(SP) 52 | 53 | MOVL addr+0(FP), AX 54 | CALL AX 55 | SUBL $16, SP 56 | MOVL AX, ret+20(FP) 57 | RET 58 | 59 | TEXT ·call5(SB),4,$20-28 60 | MOVL a+4(FP), BX 61 | MOVL BX, 0(SP) 62 | MOVL b+8(FP), BX 63 | MOVL BX, 4(SP) 64 | MOVL c+12(FP), BX 65 | MOVL BX, 8(SP) 66 | MOVL d+16(FP), BX 67 | MOVL BX, 12(SP) 68 | MOVL e+20(FP), BX 69 | MOVL BX, 16(SP) 70 | 71 | MOVL addr+0(FP), AX 72 | CALL AX 73 | SUBL $20, SP 74 | MOVL AX, ret+24(FP) 75 | RET 76 | 77 | TEXT ·call6(SB),4,$24-32 78 | MOVL a+4(FP), BX 79 | MOVL BX, 0(SP) 80 | MOVL b+8(FP), BX 81 | MOVL BX, 4(SP) 82 | MOVL c+12(FP), BX 83 | MOVL BX, 8(SP) 84 | MOVL d+16(FP), BX 85 | MOVL BX, 12(SP) 86 | MOVL e+20(FP), BX 87 | MOVL BX, 16(SP) 88 | MOVL f+24(FP), BX 89 | MOVL BX, 20(SP) 90 | 91 | MOVL addr+0(FP), AX 92 | CALL AX 93 | SUBL $24, SP 94 | MOVL AX, ret+28(FP) 95 | RET 96 | -------------------------------------------------------------------------------- /asmcall/stdcall/stdcall_amd64.s: -------------------------------------------------------------------------------- 1 | TEXT ·call0(SB),4,$32-16 2 | MOVQ addr+0(FP), AX 3 | CALL AX 4 | MOVQ AX, ret+8(FP) 5 | RET 6 | 7 | TEXT ·call1(SB),0,$32-24 8 | MOVQ a+8(FP), CX 9 | 10 | MOVQ addr+0(FP), AX 11 | CALL AX 12 | MOVQ AX, ret+16(FP) 13 | RET 14 | 15 | TEXT ·call2(SB),0,$32-32 16 | MOVQ a+8(FP), CX 17 | MOVQ b+16(FP), DX 18 | 19 | MOVQ addr+0(FP), AX 20 | CALL AX 21 | MOVQ AX, ret+24(FP) 22 | RET 23 | 24 | TEXT ·call3(SB),0,$32-40 25 | MOVQ a+8(FP), CX 26 | MOVQ b+16(FP), DX 27 | MOVQ c+24(FP), R8 28 | 29 | MOVQ addr+0(FP), AX 30 | CALL AX 31 | MOVQ AX, ret+32(FP) 32 | RET 33 | 34 | TEXT ·call4(SB),0,$32-48 35 | MOVQ a+8(FP), CX 36 | MOVQ b+16(FP), DX 37 | MOVQ c+24(FP), R8 38 | MOVQ d+32(FP), R9 39 | 40 | MOVQ addr+0(FP), AX 41 | CALL AX 42 | MOVQ AX, ret+40(FP) 43 | RET 44 | 45 | TEXT ·call5(SB),0,$40-56 46 | MOVQ a+8(FP), CX 47 | MOVQ b+16(FP), DX 48 | MOVQ c+24(FP), R8 49 | MOVQ d+32(FP), R9 50 | MOVQ e+40(FP), BX 51 | MOVQ BX, e-8(SP) 52 | 53 | MOVQ addr+0(FP), AX 54 | CALL AX 55 | MOVQ AX, ret+48(FP) 56 | RET 57 | 58 | TEXT ·call6(SB),0,$48-64 59 | MOVQ a+8(FP), CX 60 | MOVQ b+16(FP), DX 61 | MOVQ c+24(FP), R8 62 | MOVQ d+32(FP), R9 63 | MOVQ e+40(FP), BX 64 | MOVQ BX, e-16(SP) 65 | MOVQ f+48(FP), BX 66 | MOVQ BX, f-8(SP) 67 | 68 | MOVQ addr+0(FP), AX 69 | CALL AX 70 | MOVQ AX, ret+56(FP) 71 | RET 72 | -------------------------------------------------------------------------------- /asmcall/stdcall/stdcall_posix.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package stdcall 4 | 5 | import "errors" 6 | 7 | func call(addr uintptr, a ...uintptr) (uintptr, error) { 8 | return 0, errors.New("unsupported on this platform") 9 | } 10 | -------------------------------------------------------------------------------- /asmcall/stdcall/stdcall_windows.go: -------------------------------------------------------------------------------- 1 | package stdcall 2 | 3 | import "errors" 4 | 5 | func call0(addr uintptr) uintptr 6 | func call1(addr uintptr, a uintptr) uintptr 7 | func call2(addr uintptr, a uintptr, b uintptr) uintptr 8 | func call3(addr uintptr, a uintptr, b uintptr, c uintptr) uintptr 9 | func call4(addr uintptr, a uintptr, b uintptr, c uintptr, d uintptr) uintptr 10 | func call5(addr uintptr, a uintptr, b uintptr, c uintptr, d uintptr, e uintptr) uintptr 11 | func call6(addr uintptr, a uintptr, b uintptr, c uintptr, d uintptr, e uintptr, f uintptr) uintptr 12 | 13 | func call(addr uintptr, a ...uintptr) (uintptr, error) { 14 | switch l := len(a); l { 15 | case 0: 16 | return call0(addr), nil 17 | case 1: 18 | return call1(addr, a[0]), nil 19 | case 2: 20 | return call2(addr, a[0], a[1]), nil 21 | case 3: 22 | return call3(addr, a[0], a[1], a[2]), nil 23 | case 4: 24 | return call4(addr, a[0], a[1], a[2], a[3]), nil 25 | case 5: 26 | return call5(addr, a[0], a[1], a[2], a[3], a[4]), nil 27 | case 6: 28 | return call6(addr, a[0], a[1], a[2], a[3], a[4], a[5]), nil 29 | default: 30 | return 0, errors.New("too many arguments") 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /asmcall/stdcall/stdcall_windows_test.go: -------------------------------------------------------------------------------- 1 | package stdcall 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/lsegal/cppgo/asmcall/internal/asmcalltest" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestCall(t *testing.T) { 11 | v, e := Call(asmcalltest.GetStdcallF0Addr()) 12 | assert.NoError(t, e) 13 | assert.Equal(t, uintptr(42), v) 14 | 15 | v, e = Call(asmcalltest.GetStdcallF1Addr(), 16) 16 | assert.NoError(t, e) 17 | assert.Equal(t, uintptr(17), v) 18 | 19 | v, e = Call(asmcalltest.GetStdcallF2Addr(), 4, 2) 20 | assert.NoError(t, e) 21 | assert.Equal(t, uintptr(2), v) 22 | 23 | v, e = Call(asmcalltest.GetStdcallF3Addr(), 4, 2, 2) 24 | assert.NoError(t, e) 25 | assert.Equal(t, uintptr(1), v) 26 | 27 | v, e = Call(asmcalltest.GetStdcallF4Addr(), 16, 2, 4, 2) 28 | assert.NoError(t, e) 29 | assert.Equal(t, uintptr(1), v) 30 | 31 | v, e = Call(asmcalltest.GetStdcallF5Addr(), 99, 99, 99, 99, 12) 32 | assert.NoError(t, e) 33 | assert.Equal(t, uintptr(12), v) 34 | 35 | v, e = Call(asmcalltest.GetStdcallF6Addr(), 99, 99, 99, 99, 2, 3) 36 | assert.NoError(t, e) 37 | assert.Equal(t, uintptr(6), v) 38 | } 39 | -------------------------------------------------------------------------------- /asmcall/thiscall/thiscall.go: -------------------------------------------------------------------------------- 1 | // Package thiscall implements method call ABI for the __thiscall calling 2 | // convention. 3 | // 4 | // This package is only supported on Windows. 5 | package thiscall 6 | 7 | // Call calls a thiscall style function at memory address addr with the arguments 8 | // list a. The function result value is returned as a uintptr to be translated 9 | // by the caller. If the function cannot be called (usually due to an invalid 10 | // number of argument), an error is returned. 11 | func Call(addr uintptr, a ...uintptr) (uintptr, error) { 12 | return call(addr, a...) 13 | } 14 | -------------------------------------------------------------------------------- /asmcall/thiscall/thiscall_386.s: -------------------------------------------------------------------------------- 1 | TEXT ·call0(SB),4,$0-12 2 | MOVL a+4(FP), CX 3 | 4 | MOVL addr+0(FP), AX 5 | CALL AX 6 | MOVL AX, ret+8(FP) 7 | RET 8 | 9 | TEXT ·call1(SB),4,$4-16 10 | MOVL a+4(FP), CX 11 | MOVL b+8(FP), BX 12 | MOVL BX, 0(SP) 13 | 14 | MOVL addr+0(FP), AX 15 | CALL AX 16 | SUBL $4, SP 17 | MOVL AX, ret+12(FP) 18 | RET 19 | 20 | TEXT ·call2(SB),4,$8-20 21 | MOVL a+4(FP), CX 22 | MOVL b+8(FP), BX 23 | MOVL BX, 0(SP) 24 | MOVL c+12(FP), BX 25 | MOVL BX, 4(SP) 26 | 27 | MOVL addr+0(FP), AX 28 | CALL AX 29 | SUBL $8, SP 30 | MOVL AX, ret+16(FP) 31 | RET 32 | 33 | TEXT ·call3(SB),4,$12-24 34 | MOVL a+4(FP), CX 35 | MOVL b+8(FP), BX 36 | MOVL BX, 0(SP) 37 | MOVL c+12(FP), BX 38 | MOVL BX, 4(SP) 39 | MOVL d+16(FP), BX 40 | MOVL BX, 8(SP) 41 | 42 | MOVL addr+0(FP), AX 43 | CALL AX 44 | SUBL $12, SP 45 | MOVL AX, ret+20(FP) 46 | RET 47 | 48 | TEXT ·call4(SB),4,$16-28 49 | MOVL a+4(FP), CX 50 | MOVL b+8(FP), BX 51 | MOVL BX, 0(SP) 52 | MOVL c+12(FP), BX 53 | MOVL BX, 4(SP) 54 | MOVL d+16(FP), BX 55 | MOVL BX, 8(SP) 56 | MOVL e+20(FP), BX 57 | MOVL BX, 12(SP) 58 | 59 | MOVL addr+0(FP), AX 60 | CALL AX 61 | SUBL $16, SP 62 | MOVL AX, ret+24(FP) 63 | RET 64 | 65 | TEXT ·call5(SB),4,$20-32 66 | MOVL a+4(FP), CX 67 | MOVL b+8(FP), BX 68 | MOVL BX, 0(SP) 69 | MOVL c+12(FP), BX 70 | MOVL BX, 4(SP) 71 | MOVL d+16(FP), BX 72 | MOVL BX, 8(SP) 73 | MOVL e+20(FP), BX 74 | MOVL BX, 12(SP) 75 | MOVL f+24(FP), BX 76 | MOVL BX, 16(SP) 77 | 78 | MOVL addr+0(FP), AX 79 | CALL AX 80 | SUBL $20, SP 81 | MOVL AX, ret+28(FP) 82 | RET 83 | -------------------------------------------------------------------------------- /asmcall/thiscall/thiscall_amd64.s: -------------------------------------------------------------------------------- 1 | TEXT ·call0(SB),0,$32-24 2 | MOVQ a+8(FP), CX 3 | 4 | MOVQ addr+0(FP), AX 5 | CALL AX 6 | MOVQ AX, ret+16(FP) 7 | RET 8 | 9 | TEXT ·call1(SB),0,$32-32 10 | MOVQ a+8(FP), CX 11 | MOVQ b+16(FP), DX 12 | 13 | MOVQ addr+0(FP), AX 14 | CALL AX 15 | MOVQ AX, ret+24(FP) 16 | RET 17 | 18 | TEXT ·call2(SB),0,$32-40 19 | MOVQ a+8(FP), CX 20 | MOVQ b+16(FP), DX 21 | MOVQ c+24(FP), R8 22 | 23 | MOVQ addr+0(FP), AX 24 | CALL AX 25 | MOVQ AX, ret+32(FP) 26 | RET 27 | 28 | TEXT ·call3(SB),0,$32-48 29 | MOVQ a+8(FP), CX 30 | MOVQ b+16(FP), DX 31 | MOVQ c+24(FP), R8 32 | MOVQ d+32(FP), R9 33 | 34 | MOVQ addr+0(FP), AX 35 | CALL AX 36 | MOVQ AX, ret+40(FP) 37 | RET 38 | 39 | TEXT ·call4(SB),0,$40-56 40 | MOVQ a+8(FP), CX 41 | MOVQ b+16(FP), DX 42 | MOVQ c+24(FP), R8 43 | MOVQ d+32(FP), R9 44 | MOVQ e+40(FP), BX 45 | MOVQ BX, e-8(SP) 46 | 47 | MOVQ addr+0(FP), AX 48 | CALL AX 49 | MOVQ AX, ret+48(FP) 50 | RET 51 | 52 | TEXT ·call5(SB),0,$48-64 53 | MOVQ a+8(FP), CX 54 | MOVQ b+16(FP), DX 55 | MOVQ c+24(FP), R8 56 | MOVQ d+32(FP), R9 57 | MOVQ e+40(FP), BX 58 | MOVQ BX, e-16(SP) 59 | MOVQ f+48(FP), BX 60 | MOVQ BX, f-8(SP) 61 | 62 | MOVQ addr+0(FP), AX 63 | CALL AX 64 | MOVQ AX, ret+56(FP) 65 | RET 66 | -------------------------------------------------------------------------------- /asmcall/thiscall/thiscall_posix.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package thiscall 4 | 5 | import "errors" 6 | 7 | func call(addr uintptr, a ...uintptr) (uintptr, error) { 8 | return 0, errors.New("unsupported on this platform") 9 | } 10 | -------------------------------------------------------------------------------- /asmcall/thiscall/thiscall_windows.go: -------------------------------------------------------------------------------- 1 | package thiscall 2 | 3 | import "errors" 4 | 5 | func call0(addr uintptr, a uintptr) uintptr 6 | func call1(addr uintptr, a uintptr, b uintptr) uintptr 7 | func call2(addr uintptr, a uintptr, b uintptr, c uintptr) uintptr 8 | func call3(addr uintptr, a uintptr, b uintptr, c uintptr, d uintptr) uintptr 9 | func call4(addr uintptr, a uintptr, b uintptr, c uintptr, d uintptr, e uintptr) uintptr 10 | func call5(addr uintptr, a uintptr, b uintptr, c uintptr, d uintptr, e uintptr, f uintptr) uintptr 11 | 12 | func call(addr uintptr, a ...uintptr) (uintptr, error) { 13 | switch l := len(a); l { 14 | case 0: 15 | return 0, errors.New("must pass this arg") 16 | case 1: 17 | return call0(addr, a[0]), nil 18 | case 2: 19 | return call1(addr, a[0], a[1]), nil 20 | case 3: 21 | return call2(addr, a[0], a[1], a[2]), nil 22 | case 4: 23 | return call3(addr, a[0], a[1], a[2], a[3]), nil 24 | case 5: 25 | return call4(addr, a[0], a[1], a[2], a[3], a[4]), nil 26 | case 6: 27 | return call5(addr, a[0], a[1], a[2], a[3], a[4], a[5]), nil 28 | default: 29 | return 0, errors.New("too many arguments") 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /asmcall/thiscall/thiscall_windows_test.go: -------------------------------------------------------------------------------- 1 | package thiscall 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/lsegal/cppgo/asmcall/internal/asmcalltest" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestCall(t *testing.T) { 11 | obj := asmcalltest.GetThiscallObj() 12 | 13 | v, e := Call(asmcalltest.GetThiscallF0Addr(), obj) 14 | assert.NoError(t, e) 15 | assert.Equal(t, uintptr(42), v) 16 | 17 | v, e = Call(asmcalltest.GetThiscallF1Addr(), obj, 16) 18 | assert.NoError(t, e) 19 | assert.Equal(t, uintptr(17), v) 20 | 21 | v, e = Call(asmcalltest.GetThiscallF2Addr(), obj, 4, 2) 22 | assert.NoError(t, e) 23 | assert.Equal(t, uintptr(2), v) 24 | 25 | v, e = Call(asmcalltest.GetThiscallF3Addr(), obj, 4, 2, 2) 26 | assert.NoError(t, e) 27 | assert.Equal(t, uintptr(1), v) 28 | 29 | v, e = Call(asmcalltest.GetThiscallF4Addr(), obj, 16, 2, 4, 2) 30 | assert.NoError(t, e) 31 | assert.Equal(t, uintptr(1), v) 32 | 33 | v, e = Call(asmcalltest.GetThiscallF5Addr(), obj, 99, 99, 99, 99, 12) 34 | assert.NoError(t, e) 35 | assert.Equal(t, uintptr(12), v) 36 | } 37 | -------------------------------------------------------------------------------- /call.go: -------------------------------------------------------------------------------- 1 | package cpp 2 | 3 | import "unsafe" 4 | 5 | func (p ptr) getaddr(offset int) uintptr { 6 | paddr := indirect(uintptr(p)) + uintptr(offset)*unsafe.Sizeof(p) 7 | return indirect(paddr) 8 | } 9 | -------------------------------------------------------------------------------- /call_posix.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package cpp 4 | 5 | import "github.com/lsegal/cppgo/asmcall/cdecl" 6 | 7 | func (p ptr) stdcall(offset int, a ...uintptr) (uintptr, error) { 8 | // ignore stdcalls on non-Windows platform 9 | return p.cdeclcall(offset, a...) 10 | } 11 | 12 | func (p ptr) thiscall(offset int, a ...uintptr) (uintptr, error) { 13 | // ignore thiscalls on non-Windows platform 14 | return p.cdeclcall(offset, a...) 15 | } 16 | 17 | func (p ptr) cdeclcall(offset int, a ...uintptr) (uintptr, error) { 18 | addr := p.getaddr(offset) 19 | return cdecl.Call(addr, a...) 20 | } 21 | -------------------------------------------------------------------------------- /call_windows.go: -------------------------------------------------------------------------------- 1 | package cpp 2 | 3 | import ( 4 | "github.com/lsegal/cppgo/asmcall/stdcall" 5 | "github.com/lsegal/cppgo/asmcall/thiscall" 6 | ) 7 | 8 | func (p ptr) cdeclcall(offset int, a ...uintptr) (uintptr, error) { 9 | return p.thiscall(offset, a...) 10 | } 11 | 12 | func (p ptr) stdcall(offset int, a ...uintptr) (uintptr, error) { 13 | addr := p.getaddr(offset) 14 | return stdcall.Call(addr, a...) 15 | } 16 | 17 | func (p ptr) thiscall(offset int, a ...uintptr) (uintptr, error) { 18 | addr := p.getaddr(offset) 19 | return thiscall.Call(addr, a...) 20 | } 21 | -------------------------------------------------------------------------------- /cpp.go: -------------------------------------------------------------------------------- 1 | // Package cpp allows methods on C++ objects to be called directly from the 2 | // Go runtime without requiring cgo compilation. 3 | // 4 | // For more information on how to use this library, see the project README: 5 | // https://github.com/lsegal/cppgo/blob/master/README.md 6 | package cpp 7 | 8 | // ConvertRef converts a C++ object ref into a wrapper obj that can call 9 | // methods on the reference object. The obj interface type should be a struct 10 | // containing function pointers matching the interface of the C++ class. 11 | // For example, given the following class: 12 | // 13 | // class Math { 14 | // public: 15 | // int multiply(int value, int times); 16 | // } 17 | // 18 | // 19 | // You might create a struct type Math as follows: 20 | // 21 | // type Math struct { 22 | // Multiply func(value, times int) int 23 | // } 24 | // 25 | // You would then call ConvertRef with a pointer to this structure. 26 | func ConvertRef(ref uintptr, obj interface{}) error { 27 | return ptr(ref).convert(obj) 28 | } 29 | -------------------------------------------------------------------------------- /cpp_posix_test.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package cpp_test 4 | 5 | import "os/exec" 6 | 7 | var ( 8 | compileCmd = exec.Command("sh", "build.sh") 9 | libname = `fixtures/dll.so` 10 | ) 11 | -------------------------------------------------------------------------------- /cpp_test.go: -------------------------------------------------------------------------------- 1 | package cpp_test 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "runtime" 8 | "testing" 9 | 10 | cpp "github.com/lsegal/cppgo" 11 | "github.com/lsegal/cppgo/dl" 12 | "github.com/lsegal/cppgo/internal/cpptest" 13 | "github.com/stretchr/testify/assert" 14 | ) 15 | 16 | var ( 17 | dll *dl.Library 18 | get_object *dl.Func 19 | procname = "get_object" 20 | ) 21 | 22 | type lib struct { 23 | GetInt func() int `call:"cdecl"` 24 | GetBool func(b bool) bool `call:"this"` 25 | FlipBool func(b *bool) 26 | GetString func() string 27 | GetSelf func() *lib 28 | TestValues func(string, int, uintptr) bool 29 | Add func(n, m int) int `call:"std"` 30 | } 31 | 32 | func compile() { 33 | _, f, _, _ := runtime.Caller(0) 34 | compileCmd.Dir = filepath.Join(filepath.Dir(f), "fixtures") 35 | b, err := compileCmd.CombinedOutput() 36 | if err != nil { 37 | panic(fmt.Sprintf("ERROR: %v\n----\n%s\n----\n", err, string(b))) 38 | } 39 | } 40 | 41 | func load() { 42 | dll = dl.Open(libname, dl.RTLD_NOW) 43 | get_object = dll.Load(procname) 44 | } 45 | 46 | func shutdown() { 47 | dll.Close() 48 | } 49 | 50 | func objref() uintptr { 51 | o, _ := get_object.Call() 52 | return o 53 | } 54 | 55 | func TestMain(m *testing.M) { 56 | compile() 57 | load() 58 | defer shutdown() 59 | 60 | code := m.Run() 61 | os.Exit(code) 62 | } 63 | 64 | func TestCpp(t *testing.T) { 65 | var l lib 66 | o := objref() 67 | cpp.ConvertRef(o, &l) 68 | 69 | assert.Equal(t, 42, l.GetInt()) 70 | assert.Equal(t, "hello world", l.GetString()) 71 | assert.True(t, l.GetBool(false)) 72 | assert.False(t, l.GetBool(true)) 73 | b := false 74 | l.FlipBool(&b) 75 | assert.True(t, b) 76 | if assert.NotNil(t, l.GetSelf()) { 77 | assert.Equal(t, 42, l.GetSelf().GetInt()) 78 | } 79 | assert.Equal(t, true, l.TestValues("hello world", -1, o)) 80 | assert.Equal(t, 13, l.Add(11, 2)) 81 | } 82 | 83 | func ExampleConvertRef() { 84 | /* 85 | // with Go type `lib`: 86 | type lib struct { 87 | GetString func() string 88 | } 89 | 90 | // and C++ class Library: 91 | class Library { 92 | public: 93 | const char *get_string(); 94 | } 95 | */ 96 | var l lib 97 | err := cpp.ConvertRef(objref(), &l) 98 | if err != nil { 99 | return 100 | } 101 | fmt.Println(l.GetString()) 102 | // Output: hello world 103 | } 104 | 105 | func BenchmarkCppGo(b *testing.B) { 106 | var l lib 107 | cpp.ConvertRef(objref(), &l) 108 | 109 | for i := 0; i < b.N; i++ { 110 | l.GetString() 111 | } 112 | } 113 | 114 | func BenchmarkCgo(b *testing.B) { 115 | o := cpptest.GetObject() 116 | for i := 0; i < b.N; i++ { 117 | cpptest.GetString(o) 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /cpp_windows_test.go: -------------------------------------------------------------------------------- 1 | package cpp_test 2 | 3 | import "os/exec" 4 | import "runtime" 5 | 6 | var ( 7 | compileCmd = exec.Command("cmd", "/c", "build.bat") 8 | libname = `fixtures\dll.dll` 9 | ) 10 | 11 | func init() { 12 | if runtime.GOARCH == "386" { 13 | procname += "@0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /dl/dl.go: -------------------------------------------------------------------------------- 1 | // Package dl implements support for loading dynamic libraries at runtime 2 | // without cgo. 3 | package dl 4 | 5 | import ( 6 | "unsafe" 7 | 8 | "github.com/lsegal/cppgo/asmcall/cdecl" 9 | ) 10 | 11 | // Library represents a dynamic library object. 12 | type Library struct { 13 | p unsafe.Pointer 14 | } 15 | 16 | // Open will open a dynamic library by the given filename on disk and optional 17 | // flags which are binary ANDed together. If no flags are passed, RTLD_NOW 18 | // is assumed on POSIX systems. 19 | // 20 | // Note: The flags parameter is ignored on the Windows platform. 21 | func Open(filename string, flags ...int) *Library { 22 | return open(filename, flags...) 23 | } 24 | 25 | // Load will search for a function by procname and return it if available. 26 | // If the function is not available, this function may panic. 27 | func (l Library) Load(procname string) *Func { 28 | return l.load(procname) 29 | } 30 | 31 | // Close closes a library and frees its resources. Calling any Func objects 32 | // returned by the library after Close() will cause a panic. 33 | func (l Library) Close() { 34 | l.close() 35 | } 36 | 37 | // Func represents a function exported by a library. 38 | type Func struct { 39 | p uintptr 40 | } 41 | 42 | // FuncAt returns a bare function object at address addr. Use this function 43 | // if you have loaded up a library using another mechanism but still want 44 | // to take advantage of the Call abstraction method. 45 | func FuncAt(addr uintptr) *Func { 46 | return &Func{p: addr} 47 | } 48 | 49 | // Call calls a function with argument list a. It returns the result as a 50 | // uintptr, and a possible error if an unsupported number of arguments were 51 | // passed. 52 | func (f Func) Call(a ...uintptr) (uintptr, error) { 53 | return cdecl.Call(f.p, a...) 54 | } 55 | 56 | // Stdcall calls a function using the __stdcall calling convention with 57 | // argument list a. It returns the result as a uintptr, and a possible error 58 | // if an unsupported number of arguments were passed. 59 | // 60 | // Note: On non-Windows systems, this function is equivalent to Call(). 61 | func (f Func) Stdcall(a ...uintptr) (uintptr, error) { 62 | return f.stdcall(a...) 63 | } 64 | -------------------------------------------------------------------------------- /dl/dl_posix.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package dl 4 | 5 | // #cgo LDFLAGS: -ldl 6 | // #include 7 | // #include 8 | import "C" 9 | import ( 10 | "unsafe" 11 | 12 | "github.com/lsegal/cppgo/asmcall/cdecl" 13 | ) 14 | 15 | var ( 16 | RTLD_NOW = int(C.RTLD_NOW) 17 | RTLD_LAZY = int(C.RTLD_LAZY) 18 | RTLD_GLOBAL = int(C.RTLD_GLOBAL) 19 | RTLD_LOCAL = int(C.RTLD_LOCAL) 20 | ) 21 | 22 | func open(filename string, flags ...int) *Library { 23 | cfilename := C.CString(filename) 24 | defer C.free(unsafe.Pointer(cfilename)) 25 | flag := 0 26 | if len(flags) == 0 { 27 | flag = RTLD_NOW 28 | } 29 | for _, f := range flags { 30 | flag |= f 31 | } 32 | return &Library{p: C.dlopen(cfilename, C.int(flag))} 33 | } 34 | 35 | func (l Library) load(procname string) *Func { 36 | cprocname := C.CString(procname) 37 | defer C.free(unsafe.Pointer(cprocname)) 38 | return &Func{p: uintptr(C.dlsym(l.p, cprocname))} 39 | } 40 | 41 | func (l Library) close() { 42 | C.dlclose(l.p) 43 | } 44 | 45 | func (f Func) stdcall(a ...uintptr) (uintptr, error) { 46 | return cdecl.Call(f.p, a...) 47 | } 48 | -------------------------------------------------------------------------------- /dl/dl_windows.go: -------------------------------------------------------------------------------- 1 | package dl 2 | 3 | import ( 4 | "syscall" 5 | "unsafe" 6 | 7 | "github.com/lsegal/cppgo/asmcall/stdcall" 8 | ) 9 | 10 | const ( 11 | // RTLD flags are used on POSIX systems to provide extra attributes for 12 | // a loaded library. See man dlopen(3) for more information on flags. 13 | RTLD_NOW = iota 14 | RTLD_LAZY 15 | RTLD_GLOBAL 16 | RTLD_LOCAL 17 | ) 18 | 19 | func open(filename string, flags ...int) *Library { 20 | lib := syscall.MustLoadDLL(filename) 21 | return &Library{p: unsafe.Pointer(lib)} 22 | } 23 | 24 | func (l Library) load(procname string) *Func { 25 | lib := (*syscall.DLL)(l.p) 26 | proc := lib.MustFindProc(procname) 27 | return &Func{p: uintptr(proc.Addr())} 28 | } 29 | 30 | func (l Library) close() { 31 | (*syscall.DLL)(l.p).Release() 32 | } 33 | 34 | func (f Func) stdcall(a ...uintptr) (uintptr, error) { 35 | return stdcall.Call(f.p, a...) 36 | } 37 | -------------------------------------------------------------------------------- /fixtures/build.bat: -------------------------------------------------------------------------------- 1 | g++ -shared -o dll.dll dll.cpp 2 | -------------------------------------------------------------------------------- /fixtures/build.sh: -------------------------------------------------------------------------------- 1 | g++ -shared -fPIC -o dll.so dll.cpp 2 | -------------------------------------------------------------------------------- /fixtures/dll.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifndef WIN32 4 | # define __cdecl 5 | # define __stdcall 6 | # define __thiscall 7 | # define __declspec(dllexport) 8 | #endif 9 | 10 | class Library 11 | { 12 | public: 13 | Library() {} 14 | virtual int __cdecl return_int() { 15 | return 42; 16 | } 17 | virtual bool __thiscall return_bool(bool in) { 18 | return !in; 19 | } 20 | virtual void flip_bool(bool *in) { 21 | *in = !*in; 22 | } 23 | virtual const char *return_string() { 24 | return "hello world"; 25 | } 26 | virtual Library *self() { 27 | return this; 28 | } 29 | virtual bool accept_string_int_and_object(char *str, unsigned int val, Library *other) { 30 | return strcmp(str, "hello world") == 0 && val == (unsigned int)-1 && 31 | this == other && other->return_int() == this->return_int(); 32 | } 33 | virtual int __stdcall stdcall_add(int n, int m) { 34 | return n+m; 35 | } 36 | }; 37 | 38 | extern "C" __declspec(dllexport) Library* __stdcall get_object() { 39 | return new Library(); 40 | } 41 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/lsegal/cppgo 2 | 3 | go 1.16 4 | 5 | require github.com/stretchr/testify v1.7.0 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 4 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 5 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 6 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 7 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 8 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 9 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 10 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 11 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 12 | -------------------------------------------------------------------------------- /internal/cpptest/cpptest.cpp: -------------------------------------------------------------------------------- 1 | class BenchmarkLibrary 2 | { 3 | public: 4 | BenchmarkLibrary() {} 5 | virtual const char *get_string() { 6 | return "hello world"; 7 | } 8 | }; 9 | 10 | extern "C" const BenchmarkLibrary *get_object() { 11 | return new BenchmarkLibrary(); 12 | } 13 | 14 | extern "C" const char *get_string(BenchmarkLibrary *obj) { 15 | return obj->get_string(); 16 | } 17 | -------------------------------------------------------------------------------- /internal/cpptest/cpptest.go: -------------------------------------------------------------------------------- 1 | package cpptest 2 | 3 | // extern void *get_object(); 4 | // extern char *get_string(void *obj); 5 | import "C" 6 | import "unsafe" 7 | 8 | func GetObject() unsafe.Pointer { 9 | return C.get_object() 10 | } 11 | 12 | func GetString(obj unsafe.Pointer) string { 13 | return C.GoString(C.get_string(obj)) 14 | } 15 | -------------------------------------------------------------------------------- /ptr.go: -------------------------------------------------------------------------------- 1 | package cpp 2 | 3 | import "C" 4 | import ( 5 | "errors" 6 | "reflect" 7 | "runtime" 8 | "strings" 9 | "unsafe" 10 | ) 11 | 12 | var ( 13 | errMustBeStruct = errors.New("value must be a reference to struct") 14 | ) 15 | 16 | const ( 17 | callCdecl = iota 18 | callStdcall 19 | callThiscall 20 | ) 21 | 22 | type ptr uintptr 23 | 24 | func (p ptr) convert(obj interface{}) error { 25 | if p == 0 { 26 | return errors.New("invalid address") 27 | } 28 | 29 | t := reflect.TypeOf(obj) 30 | if t.Kind() != reflect.Ptr { 31 | return errMustBeStruct 32 | } 33 | e := t.Elem() 34 | if e.Kind() != reflect.Struct { 35 | return errMustBeStruct 36 | } 37 | 38 | for idx := 0; idx < e.NumField(); idx++ { 39 | i := idx 40 | f := e.Field(i) 41 | ft := f.Type 42 | if ft.Kind() != reflect.Func { 43 | continue 44 | } 45 | if ft.NumOut() > 1 { 46 | return errors.New(e.Field(i).Name + ": more than 1 return value is unsupported") 47 | } 48 | 49 | // check for call convention (only affects Windows) 50 | calltype := callCdecl 51 | if runtime.GOOS == "windows" { 52 | if c := f.Tag.Get("call"); strings.HasPrefix(c, "std") { 53 | calltype = callStdcall 54 | } else if strings.HasPrefix(c, "cdecl") { 55 | calltype = callCdecl 56 | } else if c == "" { 57 | calltype = callThiscall 58 | } 59 | } 60 | 61 | vfn := reflect.MakeFunc(ft, func(args []reflect.Value) []reflect.Value { 62 | gchold := make([]interface{}, len(args)) 63 | ins := make([]uintptr, len(args)+1) 64 | ins[0] = uintptr(p) 65 | var o interface{} 66 | for n, arg := range args { 67 | ins[n+1], o = toptr(arg) 68 | gchold[n] = o 69 | } 70 | 71 | var out uintptr 72 | switch calltype { 73 | case callStdcall: 74 | out, _ = p.stdcall(i, ins...) 75 | case callThiscall: 76 | out, _ = p.thiscall(i, ins...) 77 | default: 78 | out, _ = p.cdeclcall(i, ins...) 79 | } 80 | 81 | if ft.NumOut() == 0 { 82 | return []reflect.Value{} 83 | } 84 | return []reflect.Value{toval(ft.Out(0), out)} 85 | }) 86 | reflect.ValueOf(obj).Elem().Field(i).Set(vfn) 87 | } 88 | 89 | return nil 90 | } 91 | 92 | func toptr(v reflect.Value) (uintptr, interface{}) { 93 | switch v.Type().Kind() { 94 | case reflect.Uintptr: 95 | return uintptr(v.Interface().(uintptr)), nil 96 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 97 | return uintptr(v.Int()), nil 98 | case reflect.Bool: 99 | if v.Bool() { 100 | return 1, nil 101 | } 102 | return 0, nil 103 | case reflect.String: 104 | return strtoptr(v.String()) 105 | default: 106 | return v.Pointer(), nil 107 | } 108 | } 109 | 110 | func isCppObj(t reflect.Type) bool { 111 | for t.Kind() == reflect.Ptr { 112 | t = t.Elem() 113 | } 114 | if t.Kind() != reflect.Struct { 115 | return false 116 | } 117 | for i := 0; i < t.NumField(); i++ { 118 | if t.Field(i).Type.Kind() == reflect.Func { 119 | return true 120 | } 121 | } 122 | return false 123 | } 124 | 125 | func toval(t reflect.Type, p uintptr) reflect.Value { 126 | if isCppObj(t) { 127 | v := reflect.New(t.Elem()) 128 | err := ConvertRef(p, v.Interface()) 129 | if err != nil { 130 | return reflect.Zero(t) 131 | } 132 | return v 133 | } 134 | 135 | switch t.Kind() { 136 | case reflect.String: 137 | return strtoval(p) 138 | default: 139 | return reflect.NewAt(t, unsafe.Pointer(&p)).Elem() 140 | } 141 | } 142 | 143 | func strtoptr(s string) (uintptr, interface{}) { 144 | b := []byte(s) 145 | return uintptr(unsafe.Pointer(&b[0])), b 146 | } 147 | 148 | func strtoval(p uintptr) reflect.Value { 149 | b := *(**[1 << 20]byte)(unsafe.Pointer(&p)) 150 | i := 0 151 | for b[i] != 0 { 152 | i++ 153 | } 154 | return reflect.ValueOf(string(b[0:i])) 155 | } 156 | -------------------------------------------------------------------------------- /util.go: -------------------------------------------------------------------------------- 1 | package cpp 2 | 3 | import "unsafe" 4 | 5 | func indirect(ptr uintptr) uintptr { 6 | if ptr == 0 { 7 | return 0 8 | } 9 | return **(**uintptr)(unsafe.Pointer(&ptr)) 10 | } 11 | --------------------------------------------------------------------------------