package main
/*
#include <stdlib.h>
#include <stdio.h>
void print_string( FILE *stream, char *text) {
printf("Input pointer is %p\n", (void *) stream);
printf("Expected stderr is %p\n", (void *) stderr);
fprintf(stream, text);
}
*/
import "C"
import (
"unsafe"
"syscall"
)
func main() {
text := C.CString("test123\n")
defer C.free(unsafe.Pointer(text))
// fileStream := (*C.FILE)(C.stderr) // this works
fileStream := (*C.FILE)(unsafe.Pointer(uintptr(syscall.Stderr)))
defer C.free(unsafe.Pointer(fileStream))
C.print_string(fileStream, text)
}
Throws an access violation exception.
I'm wanting to do this because a C library I want to use fprintf's the errors so I want to be able to create a Go file to read the errors from that.
You can't do
For two reasons:
syscall.Stderris system-dependent: on UNIX-like systems (including macOS) it's merely an integer number (2), while on Windows it's aHANDLE(which can as well beNULLif the application is not attached to a console, but we digress).FILE*is a (pointer to a) type provided by the C standard library, and Go does not use it to work with files.So there's simply no way to type-convert (or type cast, if you like)
syscall.Stderrto aFILE*. The fact you've defeated the Go's type system using the usual tricks so that the attempted conversion compiles means nothing: your code basically makes a pointer (an address of memory) with the value 2, and if this happens on x86, you merely get an expected fault when attempting to read memory at address 0x02. On different platforms it may have more funny results but I think you get the idea.But then the question is: why want that? Depending on what you're after, you have three options:
You might want to print to stderr from C code linked to your Go code, you can merely use the
stderrsymbol provided by the C standard library and directly callfprintfet al on it.You might want to obtain a file descriptor out of
os.Stderrby callingos.Stderr.Fd()and then doingfdopen(3)on the returned FD at the C side.You might want to use
os.Stderr(which is not the same thing assyscall.Stderr!) for output sort-of directly from the C side. To do that, you have to write and provide to your C side a set of wrapper Go functions.Note that the first two options above would not provide any synchronization with the Go side. I mean, if it's possible for your C code and your Go code to write to the process' stderr from different threads in parallel, you might get intermixed output.
They also do not protect from Go code reopening
os.Stderrto another file while not managing to have that new file reuse the standard stderr's file descriptor.