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

cmd/cgo: unclear how to use //export and separate definition and declaration C code file #20970

Closed
krasi-georgiev opened this issue Jul 10, 2017 · 15 comments
Labels
Documentation FrozenDueToAge NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Milestone

Comments

@krasi-georgiev
Copy link

krasi-georgiev commented Jul 10, 2017

This part of the cgo doc is a bit unclear

Using //export in a file places a restriction on the preamble: since it is copied into two different C output files, it must not contain any definitions, only declarations. If a file contains both definitions and declarations, then the two output files will produce duplicate symbols and the linker will fail. To avoid this, definitions must be placed in preambles in other files, or in C source files.

I already have the declaration and the definition file in separate files, but I am still getting the duplicate symbols error.

What version of Go are you using (go version)?

go version go1.8 darwin/amd64

What operating system and processor architecture are you using (go env)?

GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/krasimir"
GORACE=""
GOROOT="/usr/local/go"
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GCCGO="gccgo"
CC="clang"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -gno-record-gcc-switches -fno-common"
CXX="clang++"
CGO_ENABLED="1"
PKG_CONFIG="pkg-config"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"

What did you do?

go run pa.h


package main

/*
#cgo pkg-config: portaudio-2.0
#include <portaudio.h>
#include <pa.h>
*/
import "C"

import (
	"fmt"
	"time"
	"unsafe"
	_ "os"
	_ "runtime"
	"reflect"
)

var (
inputParameters = new(C.PaStreamParameters)
stream = new(C.PaStream)
)

// DeviceInfo contains information for an audio device.
type DeviceInfo struct {
	index                    C.PaDeviceIndex
	Name                     string
	MaxInputChannels         int
	MaxOutputChannels        int
	DefaultLowInputLatency   time.Duration
	DefaultLowOutputLatency  time.Duration
	DefaultHighInputLatency  time.Duration
	DefaultHighOutputLatency time.Duration
	DefaultSampleRate        float64
}

type Stream struct {
	paStream            unsafe.Pointer
	inParams, outParams *C.PaStreamParameters
	in, out             *reflect.SliceHeader
	args                []reflect.Value
	callback            reflect.Value
	closed              bool
}

func main() {

	if err:=C.Pa_Initialize();err != C.paNoError {
		fmt.Println(err)
	}
	defer func(){
		if err := C.Pa_Terminate();err != C.paNoError {
			fmt.Println(err )
		}

		}()

	inputParameters.device = C.Pa_GetDefaultInputDevice(); /* default input device */
	if (inputParameters.device == C.paNoDevice) {
				fmt.Println("Error: No default input device.")
	}
	inputParameters.channelCount = 1
	inputParameters.sampleFormat = C.paFloat32
  inputParameters.suggestedLatency = C.Pa_GetDeviceInfo( inputParameters.device ).defaultLowInputLatency
  inputParameters.hostApiSpecificStreamInfo = nil

stream:=&Stream{}
		err := C.Pa_OpenStream(
                 &stream.paStream,
                 inputParameters,
                 nil,                  /* &outputParameters, */
                 C.double(44100),
                 C.paFramesPerBufferUnspecified,
                 C.paClipOff,      /* we won't output out of range samples so don't bother clipping them */
                 C.paStreamWrap,
                 unsafe.Pointer(stream.paStream) );
       if( err != C.paNoError ) {
				 fmt.Println(err)
				 }

       ;
       if err := C.Pa_StartStream( unsafe.Pointer(stream.paStream) ); err != C.paNoError {
				 fmt.Println(err)
				 }


			 for x:=0;x<20;x++{
				 time.Sleep(1*time.Second)
				//  x:=C.Pa_IsStreamActive(unsafe.Pointer(stream))

				//  fmt.Println(x)
			 }
      //  while( ( err = Pa_IsStreamActive( stream ) ) == 1 )
      //  {
      //      Pa_Sleep(1000);
      //      printf("index = %d\n", data.frameIndex ); fflush(stdout);
      //  }

}

func duration(paTime C.PaTime) time.Duration {
	return time.Duration(paTime * C.PaTime(time.Second))
}

//export streamCallback
func streamCallback(inputBuffer, outputBuffer unsafe.Pointer, frames C.ulong, timeInfo *C.PaStreamCallbackTimeInfo, statusFlags C.PaStreamCallbackFlags, userData unsafe.Pointer) {
	fmt.Println("called")
}





>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// pa.h file


int cb(const void *inputBuffer, void *outputBuffer, unsigned long frames, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData);
PaStreamCallback* paStreamWrap;





>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//pa.c file


#include <portaudio.h>

int cb(const void *inputBuffer, void *outputBuffer, unsigned long frames, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData) {
	streamCallback((void*)inputBuffer, outputBuffer, frames, (PaStreamCallbackTimeInfo*)timeInfo, statusFlags, userData);
	return paContinue;
}
PaStreamCallback* paStreamWrap = cb;

What did you expect to see?

successful run

What did you see instead?

# command-line-arguments
duplicate symbol _paStreamWrap in:
    $WORK/command-line-arguments/_obj/_cgo_export.o
    $WORK/command-line-arguments/_obj/pa.cgo2.o
ld: 1 duplicate symbol for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
@cherrymui
Copy link
Member

Try

// pa.h file

extern PaStreamCallback* paStreamWrap;

@krasi-georgiev
Copy link
Author

krasi-georgiev commented Jul 10, 2017

this changes the error to :

Undefined symbols for architecture x86_64:
  "_paStreamWrap", referenced from:
      __cgohack_paStreamWrap in _cgo_main.o
     (maybe you meant: __cgohack_paStreamWrap)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

@cherrymui
Copy link
Member

How do you build the package? Is pa.c placed in the same directory as pa.h and the Go code?

@krasi-georgiev
Copy link
Author

yes all files are in the same directory , I just run go run pa.go

@cherrymui
Copy link
Member

Run go build in the directory.
go run cannot find the C code.

@krasi-georgiev
Copy link
Author

go build or go build pa.go
# command-line-arguments
duplicate symbol _paStreamWrap in:
    $WORK/command-line-arguments/_obj/_cgo_export.o
    $WORK/command-line-arguments/_obj/pa.cgo2.o
ld: 1 duplicate symbol for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

without #include <pa.h>

could not determine kind of name for C.paStreamWrap

@bradfitz bradfitz changed the title cgo - unclear how to use //export and separate deifinition and declaration C code file cmd/cgo: unclear how to use //export and separate definition and declaration C code file Jul 10, 2017
@bradfitz bradfitz added this to the Go1.10 milestone Jul 10, 2017
@bradfitz bradfitz added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Jul 10, 2017
@cherrymui
Copy link
Member

cherrymui commented Jul 10, 2017

Did you still have extern in pa.h?
Try go build (but NOT go build pa.go) with #include <pa.h> and extern in pa.h.

@krasi-georgiev
Copy link
Author

go build
./pa.c:5:2: warning: implicit declaration of function 'streamCallback' is invalid in C99 [-Wimplicit-function-declaration]

@krasi-georgiev
Copy link
Author

krasi-georgiev commented Jul 10, 2017

@cherrymui did you mean:

extern void streamCallback(void *inputBuffer, void *outputBuffer, unsigned long frames, PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData);

the info I found suggests that I need to set the exported go function as extern right ?
and that renders similar error:

./pa.c:5:2: warning: implicit declaration of function 'streamCallback' is invalid in C99 [-Wimplicit-function-declaration]
# github.com/krasi-georgiev/noCursing/pa
duplicate symbol _paStreamWrap in:
    $WORK/github.com/krasi-georgiev/noCursing/pa/_obj/_cgo_export.o
    $WORK/github.com/krasi-georgiev/noCursing/pa/_obj/pa.cgo2.o
duplicate symbol _paStreamWrap in:
    $WORK/github.com/krasi-georgiev/noCursing/pa/_obj/_cgo_export.o
    $WORK/github.com/krasi-georgiev/noCursing/pa/_obj/pa.o
ld: 2 duplicate symbols for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

@cherrymui
Copy link
Member

@krasi-georgiev
No, I mean put extern PaStreamCallback* paStreamWrap.

go build
./pa.c:5:2: warning: implicit declaration of function 'streamCallback' is invalid in C99 [-Wimplicit-function-declaration]

This looks like only a warning, i.e. build succeeded. You should see the executable.

BTW, to suppress the warning, you need to declare streamCallback in pa.c.

@krasi-georgiev
Copy link
Author

yes that worked here are the final files

// pa.h
extern PaStreamCallback* paStreamWrap;


//pa.c

#include <portaudio.h>
void streamCallback( void* inputBuffer, void *outputBuffer, unsigned long frames, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData);

int cb(const void *inputBuffer, void *outputBuffer, unsigned long frames, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData) {
	streamCallback((void*)inputBuffer, outputBuffer, frames, (PaStreamCallbackTimeInfo*)timeInfo, statusFlags, userData);
	return paContinue;
}
PaStreamCallback* paStreamWrap = cb;

could you maybe explain what happens?
Do you think there is any way to simplify it?
Is there any way to make it work with go run ?

@cherrymui
Copy link
Member

could you maybe explain what happens?

By "declaration" in C, it means you need extern for declaring global variables. Without extern it is a definition.

Is there any way to make it work with go run ?

No. go run is meant to be used for quick testing. It only supports a single Go file, even not multiple Go files, nor Go + C. You can do "build+run" in one line as something like go build -o exe && ./exe.

Do you think there is any way to simplify it?

If pa.h is just a single line, you can write it directly in Go code, i.e. in Go code replace #include <pa.h> with extern PaStreamCallback* paStreamWrap; and drop pa.h.

@cherrymui
Copy link
Member

Now the problem is solved.
I think the documentation is clear: by "declaration" in C, it means extern is needed for declaring global variables. Do we close the issue?

@krasi-georgiev
Copy link
Author

krasi-georgiev commented Jul 10, 2017

I think maybe adding a small example in the docs would help others.

@ianlancetaylor
Copy link
Contributor

I don't think we want an example of this in https://golang.org/cmd/cgo. It's too esoteric and is really more about C than about Go. This is more like a detail for a blog post somewhere.

I'm going to close this issue.

@golang golang locked and limited conversation to collaborators Jul 12, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Documentation FrozenDueToAge NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Projects
None yet
Development

No branches or pull requests

5 participants