Important safety tips when handling json-c
We’ve been using json-c internally for parsing and generating JSON in my new project. It’s a pretty nasty interface to work with, so I’ve been considering putting a prettier face on it for C++ developers. Today I sat down to do that. Instead, I spent many hours allowing json-c to repeatedly win games of roshambo.
It started out simply enough, a simple class to wrap the json_object:
#include <string>
#include <json/json_object.h>
class JsonObject {
private:
json_object* obj;
JsonObject();
public:
static JsonObject parse(const std::string& json);
~JsonObject();
};
There wasn’t much to it at this point, but I had enough to set up my Makefile to check that everything compiled properly. Sadly…it did not. While the compilation step was successful, linking wasn’t so fortunate:
libjsonwrapper.so.1: undefined reference to `json_object_put(json_object*)’
libjsonwrapper.so.1: undefined reference to `json_tokener_parse(char*)’
I spent some time (and by some time I mean most of Sunday) futzing with the Makefile, making sure json-c was properly installed, compiling my own version of json-c, checking different hardware architectures and operating systems…all with no luck whatsoever. I even looked at some other code that we have that uses json-c, checking out the Makefile to see what that code was doing that I wasn’t.
At around 8:45pm I took a break and went for Sunday bowling (Homestead Lanes does a special on Sunday nights, it’s great…you should go sometime). When I got home, I dug back in. Still no dice.
So I went back to the other code we’d written that uses json-c and looked at some other things. Finally, I happened upon it:
#include <json/json.h>
It’s subtle, but including json.h instead of json_object.h makes all the difference in the world. At first I didn’t want to know why, I was just mad that it mattered at all. Obviously json.h is some aggregate header that keeps you from having to #include every little file you need. But clearly it’s also performing a little black magic along the way that does something to affect linking. Ready to lose my shit, I dug into the header:
#ifdef __cplusplus
extern "C" {
#endif
#include "bits.h"
#include "debug.h"
#include "linkhash.h"
#include "arraylist.h"
#include "json_util.h"
#include "json_object.h"
#include "json_tokener.h"
#ifdef __cplusplus
}
#endif
Whoomp there it is: extern “C”. If you include json.h it does the right thing and makes everything inside of json-c use C linkage when compiling C++. If you don’t include json.h and instead include one of the files that it includes…then nothing uses C linkage causing the linker to FAIL.
Thanks json-c for consuming a day of my life that I can never have back.