I have not updated in a little while as I have been busily (re)learning C++ in my already-scarce free time. Seems there is a lot of work around for C++ types, and it’s always good to have options. Anyway, on with the show!
To use a more complex data structure than a primitive type such as a string with the Coherence grid, there are three options:
- A native Coherence class. This would be the option if developing from scratch an application that would only ever run on Coherence (e.g. using Coherence’s own
String
type rather thanstd::string
from the C++ standard library). - Retrofitting an existing class to work with the
Managed
template. This is probably the easiest method. - Writing a new (de)serializer to work with an existing class. This is the lowest level but highest performance method.
While I am actually developing from scratch here, “systems integration” is my middle name†, so for the purposes of this example I am going to go with option 2 as I consider it to be a more representative scenario of “real work”. The complete code for this example is on GitHub.
The interesting thing about the C++ object definition is that it contains no Coherence-specific code, but it must have:
- A public or protected zero-parameter constructor
- A copy constructor
- A
==
operator, for equality comparison - A
<<
operator (e.g. for use withcout
) - A hash function, to return a unique identifier
Some of these a class is likely to have anyway. In addition there are two functions, to (de)serialize the objects. These are Coherence-specific, but are “free” so can be implemented independently of the class. I have done some research as to whether it is possible to generate these on-the-fly at runtime as I do with object-types for Oracle AQ and have concluded that it is not; therefore each class corresponding to an OCaml record (or other type) must be coded in C++ at compile time, and this is in addition to the code required to marshal the object to and from OCaml. The reason is of course so that objects in Coherence can be created and transparently accessed by clients in C++, Java and .NET. The good news is that the structure is quite regular and both should be quite amenable to code generation, perhaps from parsing the source code to the original existing class, if available. For this trivial example, storing and retrieving a record of type message
in the grid, the C++:OCaml SLOC ratio is ~10:1, but the C++ is very simple‡.
open Cohml open Printf open Log_message type message = {msg_id : int; msg_priority : int; msg_subject : string; msg_body : string} external coh_put_message: coh_ptr -> message -> unit = "caml_put_message" external coh_get_message: coh_ptr -> int -> message = "caml_get_message" let print_message m = log_message (sprintf "id=%d, priority=%d, subject='%s', body='%s'\n" m.msg_id m.msg_priority m.msg_subject m.msg_body) let () = let test_msgs = [{msg_id = 1; msg_priority=3; msg_subject="test"; msg_body="hello, world!"}] in let c = coh_getcache "message_cache" in List.iter (coh_put_message c) test_msgs; print_message (coh_get_message c 1);
I had hoped to present a query in this example too (e.g. messages of a certain priority) but looking at the documentation:
After completing any of the above approaches, a data object is ready to be stored within the Coherence cluster. You can perform get and put based operations with your objects. However, to make use of more advanced features of Coherence: such as queries or entry processors; or if you use a key that is not a simple type; or when you use a cache loader and cache store, you must write some Java code.
Emphasis mine. I have all the C++ written, I just need to do that bit, which should be straightforward enough (and again like the C++ can probably be code-generated for the most part). Still, it adds complexity to the solution, which will make the OCaml a harder sell unless what we are actually doing with the cached objects is much better implemented in OCaml than C++, e.g. the sorts of algorithms that functional programming languages are well-suited for, or the kind of prototyping that a very high level language is well suited for, on already-defined objects (so perhaps the Java code backing the C++ code is already done for us!).
Unfortunately however I must conclude that a pure-OCaml interface to Coherence is impossible. However I am confident that my hybrid approach is going to be useful for “real work”, and better than SWIG in this particular case.
UPDATE: Perhaps I can get “close enough” with a PofExtractor.
† Actually it’s Aurelius
‡ Too much to post – see GitHub