Way back in the day, I did some work programming in C++. But to be honest, I never really learnt C++; I learnt MFC and enough ATL to do COM. And then I decided to focus on Oracle and left it there. Nonetheless, I know enough about C++ to use it as a basis for OCaml bindings for Oracle Coherence, via SWIG. As ever, the code is on GitHub. This includes scripts for starting Coherence correctly for this project (in server/
), and for testing that it is basically operational (in cpptest/
).
I had hoped that the interface file declaring the C++ methods that we need for a very simple get/put on the Coherence grid would be as simple as the includes for cpptest.cpp
:
%module cohml %{ #include "coherence/lang.ns" #include "coherence/net/CacheFactory.hpp" #include "coherence/net/NamedCache.hpp" #include <iostream> %} %include "coherence/lang.ns" %include "coherence/net/CacheFactory.hpp" %include "coherence/net/NamedCache.hpp"
But it turned out not to be that straightforward. Trying various permutations of -importall
and -I
, I couldn’t find a way to get a clean SWIG compile, so I adopted a workaround of using a shared object as a proxy between SWIG and Coherence. Starting with a header file:
#ifndef COHML_H #define COHML_H #include "coherence/lang.ns" #include "coherence/net/CacheFactory.hpp" #include "coherence/net/NamedCache.hpp" #include <iostream> using namespace coherence::lang; using coherence::net::CacheFactory; using coherence::net::NamedCache; class Cohml { private: NamedCache::Handle hCache; String::View vsRet; // to keep the C string in scope public: Cohml(char* cn); void put(char* k, char* v); const char* getCString(char* k); ~Cohml(); }; #endif
And the actual implementation:
#include "coherence/lang.ns" #include "coherence/net/CacheFactory.hpp" #include "coherence/net/NamedCache.hpp" #include <iostream> #include "cohml.hpp" using namespace coherence::lang; using coherence::net::CacheFactory; using coherence::net::NamedCache; Cohml::Cohml(char* cn) { String::View vsCacheName = cn; hCache = CacheFactory::getCache(vsCacheName); } void Cohml::put(char* k, char* v) { String::View vsKey = k; String::View vsVal = v; hCache->put(vsKey, vsVal); } const char* Cohml::getCString(char* k) { String::View vsKey = k; vsRet = cast<String::View>(hCache->get(vsKey)); return vsRet->getCString(); } Cohml::~Cohml() { CacheFactory::shutdown(); }
Now using a new SWIG interface, which is an annotated version of the header:
%module cohml %{ #include "cohml.hpp" %} class Cohml { public: Cohml(char* cn); void put(char* k, char* v); const char* getCString(char* k); ~Cohml(); };
Next we use a rather elaborate Makefile for such a small amount of code (also suppressing unnecessary warnings for the sake of this example):
COHERENCE_HOME_CPP=/opt/coherence-cpp CPP_INCL=-I$(COHERENCE_HOME_CPP)/include CPP_LIB=-L$(COHERENCE_HOME_CPP)/lib cohml: cohml.o cohml_wrap.cxx.o swig.cmo cohml.cmo ocamlmktop -custom -ccopt $(CPP_LIB) -ccopt -lcoherence cohml.o cohml_wrap.cxx.o swig.cmo cohml.cmo -o $@ cohml.cmo: cohml.cmi ocamlc -w -11 -c cohml.ml cohml.cmi: cohml_wrap.cxx.o ocamlc -c cohml.mli cohml_wrap.cxx.o: cohml_wrap ocamlc -c -ccopt -xc++ -ccopt $(CPP_INCL) -ccopt -w cohml_wrap.cxx.c swig.cmi: swig.mli ocamlc -c $< swig.cmo: swig.cmi swig.ml ocamlc -w -20 -c swig.ml cohml_wrap: cohml.i swig -c++ -ocaml cohml.i cp cohml_wrap.cxx cohml_wrap.cxx.c swig.mli: swig -ocaml -co $@ swig.ml: swig -ocaml -co $@ cohml.o: cohml.cpp g++ -g3 -fPIC -c $(CPP_INCL) -I. $(CPP_LIB) -lcoherence $< clean: rm -f *.o *.cmo *.cmi *.cxx.c *.ml* *_wrap.cxx cohml
Finally running it in a custom OCaml toplevel:
I wanted to try SWIG for this, as I had never used it before, and I am unlikely to use it again! I suppose unless confronted with an absolute mountain of pre-existing C++. The generated OCaml interface, once I finally managed† to get one, is horrible, e.g. new_Cohml
, rather than behaving like a normal object, method calls as strings and the additional import of the Swig
module, not to mention the convoluted build process. At this level of complexity it’s entirely feasible to do it all by hand using OCaml’s C interface with which I am already quite familiar, in fact it would have been quicker and easier.
This is not actually very useful yet for “real work” (well, what I want to do anyway), until I can also do MapListener (change notification) or Continuous Query – I would like to build these “on the fly” on the OCaml side, like I do for object types in AQ. Otherwise it will need a new C++ stub for every different data type/query predicate‡. Having said that, I do advocate freely mixing languages where it makes sense, and it’s possible that the C++ could be automagically generated in many cases. Maybe with SWIG, but I’d prefer not to.
Before pressing on I will completely rewrite this in style similar to OCI*ML… Nevertheless, this is sufficient to prove that OCaml can get a foothold in a Coherence grid. Hopefully this post will also serve as a warning to anyone following in my footsteps!
† To be fair, Coherence is probably pretty exotic, even as C++ goes, and SWIG is designed for languages like Tcl/Tk rather than OCaml
‡ In which case I will go back and learn it properly!
Pingback: OCaml bindings for Coherence (2) | So I decided to take my work back underground
Pingback: Putting it all together: PubSub for OCaml with Coherence | So I decided to take my work back underground
Pingback: Real World OCaml | So I decided to take my work back underground