Reference

Command line

cppgo gen, build, check, version and help: every flag and exit code.

The cppgo command has five subcommands. Three of them (gen, build, check) share one flag set; they differ in what they do with the result.

cppgo reads a C++ header — the .hpp file that declares a library’s classes and functions — and writes a Go package that calls the compiled library. The compiled library is a shared object: a .so file on Linux, which the system loader maps into the process at run time. The C++ that a direct call cannot carry is compiled into a second shared object, the shim, which cppgo generates and builds. A binding is one C++ declaration exposed as a Go identifier.

The block below is the usage cppgo help prints. An angle-bracketed word stands for a value you supply; each subcommand’s own heading has a command you can run as written.

$ cppgo gen   -header <path> -package <name> [-library <file>] [-out <dir>]
$ cppgo build -header <path> -package <name> -scan <go-package> [-out <dir>]
$ cppgo check -header <path> -package <name> [-library <file>] [-out <dir>]
$ cppgo version
$ cppgo help [command]

Exit codes

CodeMeaning
0the command succeeded
1generation, verification, or the build of the shim failed
2the command line itself was wrong

A wrong command line prints the usage to stderr and exits 2. An error prints one line naming the problem and the fix, and a failed generation writes no output. A warning names the declaration and the fix, leaves that declaration out of the package, and does not stop the run: the generated files are written and the command exits 0. The per-binding report and the success text go to stdout; every diagnostic goes to stderr.

cppgo gen

Reads the headers, writes the Go package and the C++ side, and builds the shim.

$ cppgo gen -header <path> [-header <path>]... -package <name>
            [-library <file>]... [-out <dir>] [-compile-commands <path>]
            [-bind <qualified>]... [-exclude <qualified>]...
            [-instantiate '<specialization>']...
            [-rename '<signature>=<GoName>']... [-status <qualified>]...
            [-retain <qualified>]...
            [-alias '<C++ template>=<std template>']...
            [-proxy '<declaration>@<index>=<C++ type>']...
            [-cxx <compiler>]
            [-check] [-q]
FlagDefaultMeaning
-header <path>requireda C++ header to read; repeatable. Every public declaration in the header is bound unless -bind or -exclude narrows the set
-package <name>nonethe Go package name. There is no default: a missing or invalid name is an error
-library <file>noneyour shared object. The shim is linked against it, and when it is a readable file cppgo checks that every function it binds directly is exported by it — that is, listed in its dynamic symbol table, the names the loader can resolve. Repeatable: a slice that crosses several modules of one library names each, the shim records them all as dependencies, and the first is the one the package loads. Omit it for a header-only library
-out <dir>.the output directory
-compile-commands <path>discoveredcompile_commands.json, or the directory that holds it, to take the header’s flags from. That file is the compilation database: it records the compiler flags each source file of the project is built with
-bind <qualified>everything in the headeradd a root: the namespace, class, member or function to start selecting from; repeatable. A root is one declaration and everything below it
-exclude <qualified>noneremove a root; repeatable. An entry that matches nothing is an error
-instantiate '<specialization>'noneemit a concrete template specialization: a template with its type argument supplied, e.g. metrics::Accumulator<double>; repeatable. The header must make the specialization visible, e.g. template class ::metrics::Accumulator<double>;
-rename '<signature>=<GoName>'noneresolve a Go name collision; repeatable
-status <qualified>nonea declaration whose 32-bit return is a status code — 0 means success and any other value a failure: the Go signature returns only an error, and a non-zero value becomes it; repeatable
-retain <qualified>nonea declaration whose callback parameter the callee stores and calls again during later calls made from Go. The Go binding keeps the function registered and returns a registration the caller closes, and every binding of the package becomes callback-capable, because any call may deliver a record. The header cannot say a pointer is stored, so the generator has to be told; repeatable
-alias '<C++ template>=<std template>'noneread a library’s spelling of a standard type as that type: cv::Ptr=std::shared_ptr makes cv::Ptr<T> a smart pointer instead of an unknown record; repeatable
-proxy '<declaration>@<index>=<C++ type>'nonecarry a parameter the library declares as an array proxy (InputArray, OutputArray) as the container the entry names: the shim builds the container, constructs the proxy around it, calls, and copies back. A trailing & makes it in-out; repeatable
-cxx <compiler>g++, then clang++the C++ compiler for the generated side
-checkoffreport whether the generated files are up to date instead of writing them
-qoffsuppress the per-binding report and the confirmation message
-scan, -scan-dir—accepted and ignored: they are build’s input, and gen writes no concrete template code

Output, under -out:

FileWhat it is
zz_cppgo.gen.gothe Go API and the code that loads the C++ side
zz_cppgo_amd64.sthe assembly cppgo generated for the calls: one trampoline per binding, the function that moves the arguments into the C registers and calls the resolved address, except for a binding that carries a Go callback into C++, which enters C++ from Go instead and has none; plus cppgoFree, the trampoline for the cppgo_free helper that releases a standard-library result the shim copied out, such as a std::string or a std::vector
cppgo_shim/zz_cppgo_shim.hdeclarations for the C++ file
cppgo_shim/zz_cppgo_shim.cppthe C++ source cppgo writes
libcppgo_<package>.sothe shim cppgo builds and links against yours

The first four are sources to commit; the .so is a build output. What each one contains is in Architecture.

Report. On stdout, one line per binding and one per template — a template’s line names the generic API and the number of bindings it carries — then the compiler line:

mathx::Vector::norm() const                  thunk  cppgo_thunk_49993cda6560     not noexcept: an exception would unwind into Go
mathx::Vector::scale(double) noexcept        direct _ZN5mathx6Vector5scaleEd     noexcept, not virtual, register class; the library exports the symbol
cppgo: built libcppgo_mathx.so with /usr/bin/g++

A binding’s four columns are the C++ declaration, its carrier, the symbol the call resolves, and why that carrier was chosen. A carrier is the path one call takes into C++: direct calls the mangled symbol — the name the C++ compiler stores for a declaration, encoding its namespace, class and parameter types — through the generated assembly; thunk calls a generated extern "C" wrapper compiled into the shim; reentrant is the path that lets C++ call a Go function back, and a call that passes no Go function never needs it. The last column is the reason: not noexcept means the declaration may throw a C++ exception, which cannot unwind through a Go frame, so the shim catches it and returns a status instead. Check the compiler line against the compiler that built your library.

The generated zz_cppgo.gen.go carries the exact invocation that produced it in its banner.

Run from examples/mathx after make lib builds its shared object, with cppgo on PATH:

$ cppgo gen -header include/mathx/vector.hpp -package mathx \
            -library build/libmathx.so -out .

The three lines above are excerpted from that command’s report, which lists every binding of the header and ends with the compiler line.

cppgo build

Phase two of the template flow. A C++ template is a recipe for a family of classes or functions; the C++ compiler writes the real one only where a program supplies the type argument, and that written-out class or function is a specialization. cppgo gen writes the Go API once, as a generic one: Go declarations that take the type argument as a Go type parameter. cppgo build reads the Go program, collects the type arguments it uses, and adds the matching C++ specializations before it compiles the shim.

$ cppgo build -header <path> [-header <path>]... -package <name>
              -scan <go-package> [-scan <go-package>]...
              [-scan-dir <dir>] [-library <file>] [-out <dir>]
              [-compile-commands <path>] [-instantiate '<specialization>']...
              [-rename '<signature>=<GoName>']... [-status <qualified>]...
              [-retain <qualified>]...
              [-alias '<C++ template>=<std template>']...
              [-proxy '<declaration>@<index>=<C++ type>']...
              [-cxx <compiler>] [-q]
FlagDefaultMeaning
-scan <package>nonea Go package pattern — a path with ... wildcards, as the go command accepts — whose uses of the generated generic API are collected; repeatable. The source is read, not run. Without it, build writes the generic API and adds no specialization
-scan-dir <dir>.the directory the patterns resolve against
the restas genthe same manifest flags

cppgo help build prints -scan without brackets, but the command does not refuse a run without it: it writes the generic API and collects no type argument. Pass the program’s packages so the shim gets its specializations.

If the program uses an instantiation the shim has no specialization for, the call returns an error naming the exact template ...; line to add; it never reaches a nil call.

Run from examples/full after make lib, with cppgo on PATH:

$ cppgo build -header include/metrics/metrics.hpp -package metrics \
              -library build/libmetrics.so \
              -compile-commands build/compile_commands.json \
              -out ./gen -status metrics::validate_name \
              -scan ./cmd/... -scan-dir .

The report gains one line per collected specialization, with the C++ line cppgo derives for it; the walk through it is in Bind a C++ template library.

cppgo check

Regenerates the sources in memory and compares them with the files on disk. It writes nothing and builds nothing.

$ cppgo check -header <path> [-header <path>]... -package <name>
              [-library <file>] [-out <dir>]

It accepts the same manifest flags as gen, plus -scan/-scan-dir when the package has templates: the generated C++ depends on the program’s instantiations, so check compares like with like.

ExitOutput
0<out>: generated files are up to date
1cppgo check: <out> is out of date (run "cppgo gen"): <file>: one entry per missing or stale file, comma-separated

The same command as the build example, with check in place of build, makes a CI gate:

$ cppgo check -header include/metrics/metrics.hpp -package metrics \
              -library build/libmetrics.so \
              -compile-commands build/compile_commands.json \
              -out ./gen -status metrics::validate_name \
              -scan ./cmd/... -scan-dir .
./gen: generated files are up to date

cppgo version

Prints cppgo <version> and a newline on stdout. cppgo -v and cppgo --version are the same command. The version is fixed when the command is built, with -ldflags "-X main.version=<version>", and is dev for an unmodified local build.

cppgo help

cppgo help prints the top-level usage. cppgo help <command> prints that command’s usage; <command> is one of gen, build, check, version and help. -h, -help and --help do the same after a subcommand, and always exit 0. An unknown topic prints cppgo: unknown command "<topic>" and the top-level usage to stderr, and exits 2.