Stephen
Stephen

Reputation: 621

type-cast operator overloading as a global function in 'GO'

What I would like to do is as follows. I have an application code (say, MyApp), one package (perhaps) from a third party, and the other from gccgo. They are particularly golang.org/sys/unix and syscall, respectively.

MyApp tries to assign a syscall.Timespec variable to an unix.Timespec variable. That is mismatch when I use syscall from gccgo while it is not when the syscall is from Gc.

I would like to avoid changing the two libraries. I would like to make a change to MyApp. However, the patch will not go to the upstream of MyApp. I, or my employer, should maintain the patch locally. That says if the git repo of MyApp is updated, we would need to pull it again and apply another patch to it.

In that sense, I would like to try to minimize the efforts for the future maintenance.

The mismatch itself is as follows, if I simplify:

// example.go
package main

import "fmt"

type aliased_int int

type SrcStruct struct {
    x aliased_int
}

type DstStruct struct {
    x int
}

func main() {
    src := SrcStruct{x: 30}
    dst := DstStruct(src)
    fmt.Println(dst.x)
}

The code above caused a compile error due to the "x aliased_int" line. If it were "x int," the code is compiled successfully.

If this were a C++ application and libraries, I would define a type-casting operator overloading from SrcStruct to DstStruct. (%p.s. this won't work because C++ does not allow type-casting operator overloading as a global function. However, I wanted to know such a solution is available in Go.)

I am not sure if I could do the same in Go. (i.e. defining a custom type-casting operator as a global function or so) If not, what is the best for this situation when it comes to Go programming?

%P.S.

Michael Hampton kindly pointed out that gccgo's Timespec is different from Google Go compiler, Gc's Timespec. The difference is, however, gccgo uses an alias to int64 such as Timespec_sec_t or Timespec_nsec_t.

Gccgo's Timespec is defined in Gccgo's package. Gc's Timespec is included in MyApp's source tree. MyApp assumes that Timespec is the same as Gc defined. I would like only touch MyApp code minimally.

Upvotes: 0

Views: 807

Answers (1)

Michael Hampton
Michael Hampton

Reputation: 10000

Well, first, your code sample doesn't seem to accurately reflect the problem you described in prose, so I'll begin by ignoring it for the moment and writing code more similar to what you actually described:

package main

import (
    "fmt"
    "reflect"
    "syscall"

    "golang.org/x/sys/unix"
)

func main() {
    src := syscall.Timespec{3, 4}
    dst := unix.Timespec(src)

    fmt.Printf("%v is a %s\n", dst, reflect.TypeOf(dst))
}

We're just doing a simple type conversion, which works because the fields in both structs have exactly the same names and types.

{3 4} is a unix.Timespec

syscall.Timespec

type Timespec struct {
        Sec  int64
        Nsec int64
}

unix.Timespec

type Timespec struct {
    Sec  int64
    Nsec int64
}

You can copy these with a simple type conversion because the structs are exactly the same and the fields are exported.

Note that you can do this in cgo, but not in gccgo, which rejects the type conversion. The language specification states that this type conversion should work (as the structs have identical underlying types).

The obvious workaround is to deep copy the struct yourself, something like:

    dst := unix.Timespec{src.Sec, src.Nsec}

But this doesn't work because gccgo complains:

# command-line-arguments
./x.go:13:29: error: incompatible type for field 1 in struct construction (cannot use type syscall.Timespec_sec_t as type int64)
     dst := unix.Timespec{src.Sec, src.Nsec}
                             ^
./x.go:13:38: error: incompatible type for field 2 in struct construction (cannot use type syscall.Timespec_nsec_t as type int64)
     dst := unix.Timespec{src.Sec, src.Nsec}
                                      ^

So we see the root of the problem is that gccgo's syscall.Timespec is not identical to unix.Timespec, and it's not even identical to the syscall.Timespec in the cgo standard library.

You're not the first person to run into this issue either. Unfortunately that issue was closed without an obvious workaround that would work for you. Your best bet, of course, is to rewrite the offending code to not use the syscall package. Or stop using gccgo.


The example code you posted in your question is a bit different. In this case the fields have different types. Go doesn't really care that aliased_int is "actually" an int underneath. It treats them as separate types, because in Go the predeclared numeric types are also considered defined types, and thus you can't use a simple type conversion here. The spec indicates under what circumstances type conversions are allowed, and this doesn't appear to fall into any of them.

You'll have to do a deep copy yourself. There are numerous Go libraries to help with doing deep copies of structs.

The other problem in this code is that the struct fields are unexported. You can't really deep copy them at all. If you exported the fields, then you could.

Upvotes: 2

Related Questions