0

I'm interfacing with C code in Go using cgo, and I need to call a C function with a pointer to the underlying value in an Interface{} object. The value will be any of the atomic primitive types (not including complex64/complex128), or string.

I was hoping I'd be able to do something like this to get the address of ptr as an unsafe.Pointer:

unsafe.Pointer(reflect.ValueOf(ptr).UnsafeAddr())

But this results in a panic due to the value being unaddressable.

A similar question to this is Take address of value inside an interface, but this question is different, as in this case it is known that the value will always be one of the types specified above (which will be at most 64 bits), and I only need to give this value to a C function. Note that there are multiple C functions, and the one that will be called varies based off of a different unrelated parameter.

I also tried to solve this using a type switch statement, however I found myself unable to get the address of the values even after the type assertion was done. I was able to assign the values to temporary copies, then get the address of those copies, but I'd rather avoid making these copies if possible.

Will Da Silva
  • 6,386
  • 2
  • 27
  • 52
  • You cannot take the address of an interface dynamic value. That’s simply how interfaces work. If you need a pointer, put a pointer in the interface. – JimB Aug 28 '19 at 17:47
  • I can't control what gets put into the interface. I'm interfacing with code from a project I have no control over. – Will Da Silva Aug 28 '19 at 17:50
  • The you need to copy the value somewhere it can be addressed. Trying to get the address out of the interface is only going to get you undefined behavior, since it’s not allowed in the language spec. – JimB Aug 28 '19 at 17:56

1 Answers1

2

interface{} has own struct:

type eface struct {
    typ *rtype
    val unsafe.Pointer
}

You have no access to rtype directly or by linking, on the other hand, even though you'll copy whole rtype, it may be changed (deprecated) at future.

But thing is that you can replace pointer types with unsafe.Pointer (it may be anything else with same size, but pointer is much idiomatic, because each type has own pointer):

type eface struct {
    typ, val unsafe.Pointer
} 

So, now we can get value contained in eface:

func some_func(arg interface{}) {
   passed_value := (*eface)(unsafe.Pointer(&arg)).val
   *(*byte)(passed_value) = 'b'
}

some_var := byte('a')

fmt.Println(string(some_var)) // 'a'

some_func(some_var)
fmt.Println(string(some_var)) // 'a', it didn't changed, just because it was copied

some_func(&some_var)
fmt.Println(string(some_var)) // 'b'

You also might see some more usages at my repo: https://github.com/LaevusDexter/fast-cast

Sorry for my poor English.

Laevus Dexter
  • 472
  • 4
  • 5
  • The fact that val is always a pointer is not guaranteed by the language. That has changed in the past, and doing this is unsafe in general. – JimB Aug 28 '19 at 19:04
  • 1
    1) this follows the pointer rules: https://godoc.org/unsafe#Pointer that promise compatibility with future versions; despite how topic starter tried to get pointer from uintptr using reflect.Value.Pointer (which tagged in future deprecation list). 2) i don't think that interface struct may ever been changed, at least up to go2 (because it is really smells like breaking changes). Now this struct is how interfaces works. 3) it won't be changed without mention. go 1.13(see notes) is already breaks unruled things. 4) so topic starter works with cgo which already meant to be unsafe in general. – Laevus Dexter Aug 28 '19 at 19:39
  • There's no tabs, there's no newlines for comments. What's wrong? – Laevus Dexter Aug 28 '19 at 19:41
  • @JimB I'm asking to do something inherently unsafe (working with cgo/unsafe.Pointer), so I don't really care if this solution isn't perfectly safe. If it breaks, I'll just update my code. For now, this is the most performant and clean way to solve my problem. – Will Da Silva Aug 28 '19 at 19:43
  • @WillDaSilva, that’s fine as long as you’re aware that it’s unsafe and undefined. This isn’t relevant to the unsafe pointer rules, it’s the fact the the interface internals are not a public interface and there is no compatibility guarantee (as I said, it has changed in the past) – JimB Aug 28 '19 at 20:16
  • @JimB can you provide link? I can't find recent commit related to interface structure changes. Last one been at 2009 when golang just got released. – Laevus Dexter Aug 28 '19 at 20:36
  • Also that is already not "undefined" if you aware how it works and it works the way that golang developers defined for using unsafe everywhere in own code. It even sounds weird because meant that whole language runtime is unsafe by your words. Just it's less documented than another code. – Laevus Dexter Aug 28 '19 at 20:48
  • I’m fairly certain it was after 1.0, and it wasn’t a structural change but the compiler would pack small values into the interface. I’m not sure why it matters when it was changed though, breaking the language spec is unsafe and undefined, period. If you have some rare case where those values can’t be preallocated and the copy somehow effects you (copying a pointer sized values is usually faster than a dereferrence), then go ahead and accept the lack of memory safety at your own risk. – JimB Aug 28 '19 at 20:50
  • And that isn’t what “undefined” means. Yes, the language runtime _is_ by definition “unsafe” of course, because it’s the implementation of the language spec. “Undefined” means "not defined in the language specification”. Many languages have undefined behavior that you can observe in the implementation, but use of that behavior very often learns to bugs. – JimB Aug 28 '19 at 20:55
  • That's why it is called "unsafe", people should care about safety by their own, but that's still defined and usable unless people abuse spoonfeeding without understanding. That commit been exactly a decade ago and at the moment nothing presages changes. There's no even related issues what can make some point about. – Laevus Dexter Aug 28 '19 at 21:07
  • Ok, i got your point. Just i trying to say that code provided in answer has nothing "undefined", because there's only 2 fields with type and value pointers and struct itself doesn't presage to changes. It's still unsafe but defined code because works same way as regular interface except unsafe representation. – Laevus Dexter Aug 28 '19 at 21:13
  • Sorry for mistypes over the place, i have to change but SO doesn't seems that allows that – Laevus Dexter Aug 28 '19 at 21:19
  • Sorry, but this is literally the common definition of “undefined”. This is not defined in the language spec, is not guaranteed by any means, and a Go implementation (there are multiple) may implement interfaces in any way it chooses, provided it behaves according to the spec. – JimB Aug 29 '19 at 13:43