cgo use question

104 views
Skip to first unread message

Scott Cotton

unread,
Sep 29, 2018, 6:08:43 PM9/29/18
to golang-nuts
Howdy Gophers,

I've a got cgo question.

Suppose I want to use cgo in 2 packages and have one C function used in C code in both packages.

Is there a way to do this with the go tool only?  I am trying to avoid a build that needs make or the like to build a c library,
or copying and renaming the C functions.

My attempts so far have all yielded link errors.  Either the C function is undefined in one of the two Go packages (at build or runtime, depending on how I use "extern") or I get multiple symbol definitions.

Scott

Simon Ritchie

unread,
Sep 30, 2018, 7:57:23 AM9/30/18
to golang-nuts
Write a separate package to handle the C stuff and call it from the other two.

I built a wrapper for a c++ library: https://github.com/goblimey/rplidar_sdk_go. The c++ code used various features that defeated cgo, so I ended up with a multi-layered solution.

One thing I discovered is that cgo has a nasty habit of rewriting the Go code that calls C code, including trampling on your comments, so keeping that code in a separate layer is a good idea.

Scott Cotton

unread,
Sep 30, 2018, 8:08:28 AM9/30/18
to simonri...@gmail.com, golang-nuts
Thanks

In my case, believe it or not -- and for a very particular use case,
I cannot call the shared C functionality from Go, it must be called
from C. And I want to avoid telling consumers to use make to create a
C library, so that go get and modules will work.

It looks to me like your solution, at least from the README calls
make, and I can't find an example (though I may well have missed
something) of shared package specific cgo C code outside of a C
library being called by C in another package.

Can you clarify if or how your suggestion will meet my requirements (1
-- go tool only, no make, 2 -- cgo C function called by C in more than
one package)?

Scott
> --
> You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
> To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/ruLltGrPWBg/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.



--
Scott Cotton
http://www.iri-labs.com

Scott Cotton

unread,
Sep 30, 2018, 8:20:41 AM9/30/18
to golang-nuts
Hi all,

For info, short of a solution for linking cgo specific C functions cross package, I think one can do this with C function pointers, which at least is a close approximation of directly calls.

Best,
Scott
> To unsubscribe from this group and all its topics, send an email to golang-nuts+unsubscribe@googlegroups.com.

Scott Cotton

unread,
Sep 30, 2018, 11:15:59 AM9/30/18
to golang-nuts
Hi again,

As a followup, it seems the same or similar problems occur with assigning function pointers across Go packages via cgo.

When trying to assign a C function "fC" defined via cgo in package A to a C function pointer "fCPtr" in package B, even at runtime via Go, I get:

Undefined symbols for architecture x86_64:

  "_fC", referenced from:

      __cgohack_fC in _cgo_main.o

     (maybe you meant: _fCPtr, __cgohack_fC , __cgohack_fCPtr )



when building B.  A builds just fine.  It seems I just can't reference one cgo C function across packages, which means I either need to make a C library separately or copy+rename or use static functions fC  tand have go get and go modules work.


hmm...

maybe I'll copy the C code and make the functions static file members.  If that's the best solution, it is disappointing :(


Scott

Simon Ritchie

unread,
Sep 30, 2018, 11:55:05 AM9/30/18
to golang-nuts
Aagh! I misunderstood the question. Apologies for that.

There may still be a grain of usefulness in my answer. There are limits to the complexity of the C code that you can call with cgo. (It’s even worse if you are calling C++, as I was.) You can magic away a lot of those problems by writing an extra layer of C that hides the complexities.

However, this probably involves having make around to bind everything together. I agree that this is a nuisance, because your user may not have make, particularly if they are using Windows. There is a version of gnu make available for that, but using it just led me to a different world of pain.

Have you considered having two separate pieces of software, one in C and one in Go and linking them together with grpc? That would allow you to ship your solution as two binaries.

Scott Cotton

unread,
Sep 30, 2018, 12:39:37 PM9/30/18
to simonri...@gmail.com, golang-nuts
On Sun, 30 Sep 2018 at 17:55, Simon Ritchie <simonri...@gmail.com> wrote:
>
> Aagh! I misunderstood the question. Apologies for that.
>

I could have phrased the question more clearly also, sorry.


> There may still be a grain of usefulness in my answer. There are limits to the complexity of the C code that you can call with cgo. (It’s even worse if you are calling C++, as I was.) You can magic away a lot of those problems by writing an extra layer of C that hides the complexities.
>
> However, this probably involves having make around to bind everything together. I agree that this is a nuisance, because your user may not have make, particularly if they are using Windows. There is a version of gnu make available for that, but using it just led me to a different world of pain.
>

I found a workaround finally. Basically:
- in package A, when #including f.h via cgo, I add directive #cgo -DA
- in f.h, I only declare the functions if A is defined
#ifdef A
void f();
#endif
- in f.h, I put a pointer to f in a struct.
typedef struct S {
void (*f)();
} S;

this struct def is not guarded by the #ifdef
- I populate the fn pointer in package A at runtime for instances of struct S.

Then in package B
- I pass the *C.S struct to C via cgo function call.
- I #include "f.h" from package A without -DA
- I pass the *C.S struct to C via cgo function call, and get the
function pointers out of it to call.

Everything builds and runs fine without link errors.



> Have you considered having two separate pieces of software, one in C and one in Go and linking them together with grpc? That would allow you to ship your solution as two binaries.
>

Yes, but in this case the shared C functionality is tiny, <100LOC it
would be a shame. Also, ironically enough, the end goal of the
contorted cgo usage above is to bypass cgo->go at runtime for a very
particular use case which needs it (definitely not recommended in
general). It would be silly to make a thin C shim and distribute
separately as an external C dependency for the purpose of using more
pure Go and less cgo :)

Thanks in any event for your help, it was also informative to me.

Scott



> --
> You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
> To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/ruLltGrPWBg/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.

Tamás Gulácsi

unread,
Sep 30, 2018, 11:01:07 PM9/30/18
to golang-nuts
You could export the C function in a Go wrapper in package A, and use that.
Maybe another exported Go wrapper is needed in package B, I don't know.

Scott Cotton

unread,
Oct 1, 2018, 3:40:21 AM10/1/18
to tgula...@gmail.com, golang-nuts
Yes, but as noted,  I can't do Go wrappers in this case.

On Mon, 1 Oct 2018 at 05:01, Tamás Gulácsi <tgula...@gmail.com> wrote:
You could export the C function in a Go wrapper in package A, and use that.
Maybe another exported  Go wrapper is needed in package B, I don't know.

--
You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/ruLltGrPWBg/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Tamás Gulácsi

unread,
Oct 1, 2018, 4:41:57 AM10/1/18
to golang-nuts
Sorry, I don't understand this.

Why can't you `//export` a Go function in package A - a function which calls your cgo function?

And why can't you `//export` a Go function in package B - a function which calls the exported Go function in package A ???

Scott Cotton

unread,
Oct 1, 2018, 5:11:25 AM10/1/18
to tgula...@gmail.com, golang-nuts
Hi,

My use case is very very particular and unlikely to be related to most cgo use cases.

The use case requires that there is no C calling of Go.   the requirements arise from a special set of circumstances, including
1. if C calls Go, then it is on a thread created in C
2. the thread above and expectations by the OS of what the call should not do are such that go's runtime treatment of cgo->go on the foreign thread is undesirable.

So I need to call C without calling by Go //export in this very particular case.

Scott


 

--
You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/ruLltGrPWBg/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Gulácsi Tamás

unread,
Oct 1, 2018, 5:52:40 AM10/1/18
to w...@iri-labs.com, golang-nuts
Got it, thanks!
Reply all
Reply to author
Forward
0 new messages