Skip to content

Design decisions

Maxim Kupriianov edited this page Sep 19, 2016 · 9 revisions

Project architecture overview

The cgogen project consists of 3 parts: translator, generator and parser in order of importance. The first part was the translator that is responsible for name conversions from stuff_like_this_id() into StuffLikeThisID(), for type conversions from unsigned char * to []byte, from MyClass ** into **MyClass or even []*MyClass.

Translator does a lot of things, some parts of its engine are done right (name substitution, rule patterns, hints/tips for type conversions), other ones are done poorly because things turned out to be different in practice when I finished the first prototype and moved to experiments, however the quality is decent so I won't bother to rewrite that part.

Generator is mostly fine, however I don't like how pointer and references are handled, a better model from translator would fix that, but that's not so critical. The generator is based on fmt.Sprintf mechanism and that helped a lot to avoid code bloat. Also it's very flexible, for an example, when Go 1.6 announced pointer rules, the required changes were including switching all allocations to C.malloc calls, have a reference count object, implementing pointer borrowing and automatic frees with Finalizers, do nested allocations while unpacking slices like [][]string and so on. I added all this to generator in a couple of days without a single hack. Thanks to the dynamic helpers mechanism, see gen_bindings.go if curious.

Parser is actually a wrapper for cznic/cc C99 compiler frontend, by coincidence it has been under active development at the same time as I started this project (summer 2015), it was in alpha state and was unstable as hell. So I freezed the version that was stable enough and wrote the translator models based on the capabilities of CC at that moment. There was a lot stuff to guess. Then Jan has completed his CC project and I switched to the upstream master, at cost of having rewriting half of the translator logic, that's the true reason why its models are in so miserable state now. Parser being configured partly from the configuration manifest and partly from predefined options in predefined.go.

All three modules are doing their best-outcome guesswork to handle the common cases in the most common ways, however when some configuration is needed, cgogen consults the configuration manifest for the project that usually has tree sections corresponding to the modules: GENERATOR, PARSER and TRANSLATOR sorted in order of contents size.

The main executable provides the glue for all of three modules, it does config parsing, runs external utils like in-house pkg-config lightweight clone to discover more headers, manages the output buffers, formats the code using imports.Process after generation is done and so on.

Workflow overview

$ cgogen -h
╔═╗╔═╗╔═╗┌─┐┌─┐┌┐┌
║  ║ ╦║ ║│ ┬├┤ │││
╚═╝╚═╝╚═╝└─┘└─┘┘└┘

Usage: cgogen package1.yml [package2.yml] ...
See http://cgogen.com for examples and documentation.

Options:
  -ccdefs
    	Try to steal built-in defines from a hosted C-compile, CC env variable must be set.
  -debug
    	Enable some debug info.
  -fancy
    	Enable fancy output in the term. (default true)
  -nocgo
    	Do not include a cgo-specific header in resulting files.
  -out dir
    	Specify a dir for the output.