CGO and #define

3,120 views
Skip to first unread message

Myron Alexander

unread,
Nov 13, 2009, 8:00:43 AM11/13/09
to golang-nuts
How is CGO supposed to handle #defines from a header file?

eg:

#define SOME_CONSTANT_HERE 1234

I tried to use the C.SOME_CONSTANT_HERE notation and it creates a
symbol of type C.int linked to nothing.

package spikecgo

// #include <sqlite3.h>
import "C"

import (
//"unsafe";
"fmt";
)

func PrintNum () {
//var ver C.int;
//ver = C.SQLITE_VERSION_NUMBER;

ver := C.SQLITE_VERSION_NUMBER;

ver = C.sqlite3_libversion_number ();

//var gover int32;

//gover := ver;

fmt.Printf ("SQLite version is : %d", ver);
}

I created a simple main program called runner that just calls the
function PrintNum.

The program compiles fine but when I run it:

./runner: symbol lookup error: ./runner: undefined symbol:
SQLITE_VERSION_NUMBER

Am I doing something wrong or does CGO not handle #define constants?

Will CGO handle #defines in a future release?

Thanks,

Myron

Anselm R Garbe

unread,
Nov 13, 2009, 8:31:07 AM11/13/09
to Myron Alexander, golang-nuts
2009/11/13 Myron Alexander <myron.a...@gmail.com>:
> How is CGO supposed to handle #defines from a header file?

I think there is no CPP support in cgo.

> #define SOME_CONSTANT_HERE  1234

Try using const int's for those constants to get symbols.

Cheers,
Anselm

Myron Alexander

unread,
Nov 13, 2009, 8:51:10 AM11/13/09
to golang-nuts
On Nov 13, 3:31 pm, Anselm R Garbe <garb...@gmail.com> wrote:
> Try using const int's for those constants to get symbols.

Seems to be the only option. The problem with cgo goes deeper than the
#defines. It cannot handle opaque structs such as the one sqlite uses:

typedef struct sqlite3 sqlite3;

When I compile with cgo, I get:

dwarf.Type sqlite3 reports unknown size

So I think I will have to write a "simple" C wrapper and pass that
through cgo.

Regards,

Myron.

Anselm R Garbe

unread,
Nov 13, 2009, 8:57:07 AM11/13/09
to Myron Alexander, golang-nuts
2009/11/13 Myron Alexander <myron.a...@gmail.com>:
> On Nov 13, 3:31 pm, Anselm R Garbe <garb...@gmail.com> wrote:
>> Try using const int's for those constants to get symbols.
>
> Seems to be the only option. The problem with cgo goes deeper than the
> #defines. It cannot handle opaque structs such as the one sqlite uses:
>
> typedef struct sqlite3 sqlite3;
>
> When I compile with cgo, I get:
>
> dwarf.Type sqlite3 reports unknown size

Yes I noticed that with Xlib too, which is why I'm wrapping Xlib in
some C wrapper in order to make it work.

> So I think I will have to write a "simple" C wrapper and pass that
> through cgo.

Yup.

Cheers,
Anselm

Eden Li

unread,
Nov 13, 2009, 11:10:32 AM11/13/09
to Myron Alexander, golang-nuts
On Fri, Nov 13, 2009 at 5:51 AM, Myron Alexander
<myron.a...@gmail.com> wrote:
> Seems to be the only option. The problem with cgo goes deeper than the
> #defines. It cannot handle opaque structs such as the one sqlite uses:
>
> typedef struct sqlite3 sqlite3;
>
> When I compile with cgo, I get:
>
> dwarf.Type sqlite3 reports unknown size

This is due to issue 126: http://code.google.com/p/go/issues/detail?id=126

Not sure how it'll be fixed, but the kludge-patch for cgo listed in
the bug may work for this case.

Myron Alexander

unread,
Nov 13, 2009, 8:22:48 PM11/13/09
to golang-nuts
On Nov 13, 6:10 pm, Eden Li <eden...@gmail.com> wrote:
> This is due to issue 126:http://code.google.com/p/go/issues/detail?id=126
>
> Not sure how it'll be fixed, but the kludge-patch forcgolisted in
> the bug may work for this case.

Thanks for the link. That may work for the opaque structs.

Because of the #defines, I still have to create a simple C wrapper; no
defines, no conditional compilation and using types that cgo can
handle. I wrote a small program to test the concept and it worked.
Unfortunately, it probably means that a C compiler must be installed
to compile a program linked to the library. I will have to investigate
the package mechanism some more.

In the meantime, if you are interested, this is what I did.

Simple wrapper :

sqlite3_wrapper.h
------------------
#ifndef __SQLITE3_WRAPPER_H__
#define __SQLITE3_WRAPPER_H__

//--------------------------------------------------------------------------------------------------
// Section: Constants
//--------------------------------------------------------------------------------------------------

/*
* Return values for sqlite3
*/

enum
{
Sqlite3_Ok = 0, /* Successful result */
Sqlite3_Error = 1, /* SQL error or missing database */
Sqlite3_Internal = 2, /* NOT USED. Internal logic error in
SQLite */
Sqlite3_Perm = 3, /* Access permission denied */
Sqlite3_Abort = 4, /* Callback routine requested an abort */
Sqlite3_Busy = 5, /* The database file is locked */
Sqlite3_Locked = 6, /* A table in the database is locked */
Sqlite3_Nomem = 7, /* A malloc() failed */
Sqlite3_Readonly = 8, /* Attempt to write a readonly database
*/
Sqlite3_Interrupt = 9, /* Operation terminated by
sqlite3_interrupt()*/
Sqlite3_Ioerr = 10, /* Some kind of disk I/O error occurred
*/
Sqlite3_Corrupt = 11, /* The database disk image is malformed
*/
Sqlite3_Notfound = 12, /* NOT USED. Table or record not found */
Sqlite3_Full = 13, /* Insertion failed because database is
full */
Sqlite3_Cantopen = 14, /* Unable to open the database file */
Sqlite3_Protocol = 15, /* NOT USED. Database lock protocol error
*/
Sqlite3_Empty = 16, /* Database is empty */
Sqlite3_Schema = 17, /* The database schema changed */
Sqlite3_Toobig = 18, /* NOT USED. Too much data for one row */
Sqlite3_Constraint = 19, /* Abort due to contraint violation */
Sqlite3_Mismatch = 20, /* Data type mismatch */
Sqlite3_Misuse = 21, /* Library used incorrectly */
Sqlite3_Nolfs = 22, /* Uses OS features not supported on host
*/
Sqlite3_Auth = 23, /* Authorization denied */
Sqlite3_Format = 24, /* Auxiliary database format error */
Sqlite3_Range = 25, /* 2nd parameter to sqlite3_bind out of
range */
Sqlite3_Notadb = 26, /* File opened that is not a database
file */
Sqlite3_Row = 100, /* sqlite3_step() has another row ready
*/
Sqlite3_Done = 101 /* sqlite3_step() has finished executing
*/
};


/**
* Open a SQLite database and pass the connection value back to the
caller.
*
* @param filename -
* SQLite database file name
*
* @param connection -
* SQLite connection handle passed back to the caller.
*
* @returns
* Sqlite3_Ok - when the connection could be opened.
* Any other value is an error.
*/
int DbOpen (const char* filename, void **connection);

int DbClose (void *connection);

int DbStmtPrepare (void *connection, const char* statement, void
**stmtHandle);

int DbStmtStep (void *stmtHandle);

int DbStmtClose (void *stmtHandle);

#endif
------------------


sqlite3_wrapper.c
------------------
#include <string.h>
#include <sqlite3.h>
#include "sqlite3_wrapper.h"

int DbOpen (const char* filename, void **connection)
{
int rc = sqlite3_open (filename, (sqlite3**)connection);
return rc;
}

int DbClose (void *connection)
{
int rc = sqlite3_close ((sqlite3*)connection);
return rc;
}

int DbStmtPrepare (void *connection, const char* statement, void
**stmtHandle)
{
int rc =
sqlite3_prepare_v2 (
connection, statement, strlen (statement), (sqlite3_stmt**)
stmtHandle, NULL);

return rc;
}

int DbStmtStep (void *stmtHandle)
{
int rc = sqlite3_step ((sqlite3_stmt*)stmtHandle);
return rc;
}

int DbStmtClose (void *stmtHandle)
{
int rc = sqlite3_finalize ((sqlite3_stmt*)stmtHandle);
return rc;
}

------------------

spikecgo.go:
------------------
package spikecgo

// #include "sqlite3_wrapper.h"
import "C"

import (
"unsafe";
"fmt";
)

var db unsafe.Pointer;
var stmt unsafe.Pointer;

func OpenDatabase () {
//C.sqlite3_open (C.CString (":memory:"), &db);
//rc := C.DbOpen (C.CString (":memory:"), &db);

rc := C.DbOpen (C.CString ("mytestdb.db"), &db);
fmt.Printf ("Database open returned : %d\n", rc);

rc = C.DbStmtPrepare (db, C.CString ("create table boom (bam int)"),
&stmt);
fmt.Printf ("Database open returned : %d\n", rc);

rc = C.DbStmtStep (stmt);
fmt.Printf ("Database returned : %d\n", rc);

rc = C.DbStmtClose (stmt);
fmt.Printf ("Database returned : %d\n", rc);

rc = C.DbClose (db);
fmt.Printf ("Database close returned : %d\n", rc);
}
------------------

runner.go:
------------------
package main

import (
//"fmt";
"spikecgo";
)


func main () {
spikecgo.OpenDatabase ();
}
------------------

Makefile:
------------------
# Copyright 2009 The Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.

include $(GOROOT)/src/Make.$(GOARCH)

TARG=spikecgo

# Can have plain GOFILES too, but this example doesn't.

CGOFILES=\
spikecgo.go

CGO_LDFLAGS=sqlite3_wrapper.o -lsqlite3

# To add flags necessary for locating the library or its include
files,
# set CGO_CFLAGS or CGO_LDFLAGS. For example, to use an
# alternate installation of the library:
# CGO_CFLAGS=-I/home/rsc/gmp32/include
# CGO_LDFLAGS+=-L/home/rsc/gmp32/lib
# Note the += on the second line.

CLEANFILES+=runner

include $(GOROOT)/src/Make.pkg

runner: sqlite3_wrapper.o install runner.go
$(GC) runner.go
$(LD) -o $@ runner.$O

sqlite3_wrapper.o: sqlite3_wrapper.c
gcc -m32 -fPIC -O2 -o sqlite3_wrapper.o -c sqlite3_wrapper.c
------------------
Reply all
Reply to author
Forward
0 new messages