Last summer, I built LinearGo, a Go wrapper for the famous machine learning library LIBLINEAR. Basically, I used CGO to bridge LIBLINEAR’s C library and Go. At first, I tried to build a 1-to-1 mapping for all of the data structure defined in C liblinear to hide the implementation details of C behind Go code.
However, things become complicated after Go 1.6 as they change their pointer policy for CGO. We can no longer pass something that contains a Go pointer which points to a Go pointer (WTF I just said!?). For example, a
Parameter contains a pointer to a
FeatureNode (an array of FeatureNode in fact). Before Go 1.6, I simply pass the pointer of
Parameter to C, contains a pointer to a
FeatureNode (a Go struct). Things like this, which a Go pointer contains a Go pointer, is not allowed anymore in Go 1.6. Therefore, I’m forced to figure out some other way to get around this.
The solution I came up with is passing plain C data type only (things like
double, or pointers to them), and then re-construct the struct in C. Therefore, I removed the corresponding Go structure and wrote some C helper function which I can call from Go.
For example, I have a
call_train helper function, the signature of which is
struct model* call_train(
And the real
train function looks like this
struct model* train(const struct problem *prob, const struct parameter *param);
call_train, I manually construct
param and then call the real
train with these arguments.
Luckily, after some monkey coding, LinearGo is now supporting Go 1.6. Moreover, I believe the current approach is more stable than previous one because we only work on simple data type when bridging while leave the complicated structure in pure C.