Browse thread
objects: the lack of method overloading (looking for a way around it)
-
philippe
- philippe
[
Home
]
[ Index:
by date
|
by threads
]
[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
| Date: | -- (:) |
| From: | philippe <kelvin.bitnick@g...> |
| Subject: | Re: objects: the lack of method overloading (looking for a way around it) |
philippe, use parametrized classes. -- philippe On Mon, Oct 4, 2010 at 7:32 PM, philippe <kelvin.bitnick@gmail.com> wrote: > Hello, > > I'm just beginning a real, usefull usage of the object system in ocaml. > I'm a bit stalled on one thing, it's the lack of method overloading like in C++. > My need is in a case of building a rather simple pipeline pattern: > each instance of an element (a node) in the pipeline can get a > numerical value from the previous node, do some computation on it, and > store it locally in a hash for caching/efficiency, as returning it > using the #get method. > > The real need see most of the time a 1D array of float (a vector) in > input and output of each node. > but sometime a Complex.t array. > > So I need some kind of polymorphism for allowing : > > float array -> float array > Complet.t array -> float array > float array -> Complex.t array > Complex.t array -> Complex.t array > > Here's a shorter (a bit dumb) demo code using int -> int. > > (I would much prefer avoid to store float in the real part of a > complex array - in the real (genuine) code the array may be long, > 100'000 elements and more, it's code a bit sensitive about > performances). > > Look to the comment MY QUESTION in the definition of the process > method in the class node_plus > > THANK YOU ! > > -- > Philippe Strauss > > --- 8< --- > > open Common > open Log > > (* the real code has more usefull type here *) > type node_parameters = Plus1 | Plus2 > > (* a class for storing parameters usefull for the whole pipeline chain *) > class parameters = > object(self) > (* useless here - usefull in the real code *) > val mutable sf = 44100 > (* a value we will add in a demo node *) > val mutable add = 1 > method get_sf = sf > method set_sf sfreq = > sf <- sfreq ; > method get_add = add > method set_add ntoadd = > add <- ntoadd > (* this is for the cache part of each node: we compute a hash > of parameter to distinguish hash entries btwn parameters change *) > method get_parh np (node_id: int) = > let md5 s = Digest.to_hex (Digest.string s) in > match np with > (* the real code doesn't use always (sf, add) but > parameter influing the processing of a node *) > | Plus1 -> md5 (Marshal.to_string (sf, add) [Marshal.No_sharing]) > | Plus2 -> md5 (Marshal.to_string (sf, add) [Marshal.No_sharing]) > end > > (* main code of a node: a boileplate around a hash to cache computed value *) > class virtual node_virt prev parm hn = > object(self) > (* instance of a previous node in the pipeline *) > val previous = prev > (* instance of the parameters *) > val parms = parm > (* the hash for caching *) > val hres = Hashtbl.create hn > (* a serial, bumped up plus one on each cache miss *) > val mutable id = 0 > method get_id = id > method inc_id = > id <- id + 1 > (* for passing to (parms #get_parh (self #ptyp ())) in the > real code, when adding a result into the hash *) > method virtual ptyp: unit -> node_parameters > (* the real computation is defined in a non virtual method > later, as simply as possible *) > method virtual process: int -> int > (* process and store: call process, then store in cache. > called on a cache miss *) > method private pstore: (int * int) = > let prid, data = prev #get in > (* processing *) > let new_data = self #process data in > (* we're in a cache miss: bump the serial +1 *) > self #inc_id ; > let myid = self #get_id in > Hashtbl.add hres (parms #get_parh Plus1 myid) new_data ; > (myid, new_data) > method get = > try > (* memoize id to avoid race cond *) > let myid = self #get_id in > (* here again in the real code, we lookup the hash > using (parms #get_parh (self #ptyp ()) myid) *) > let data = Hashtbl.find hres (parms #get_parh Plus1 myid) in > (myid, data) > with Not_found -> Printf.printf "node_virt #get: cache > miss!\n%!"; self #pstore > end > > (* a trivial node: add parm #get_add to the integer we receive from > the previous node *) > class node_plus prev parm hn = > object(self) > inherit node_virt prev parm hn > method ptyp () = Plus1 > (* > MY QUESTION: what is the best way to allow not only int -> > int for this process method, > but also: int -> float, float -> int, float -> float ? > The method minimizing the code written in this non virtual > node definition, since I may end with 15 or 30 > differents derived class of node_virt like this single one ? > *) > method process n = > n + (parm #get_add) > end > > (* a front of line, head node having a get method of the same type > than those derived from node_virt *) > class node_head parm = > object(self) > val parms = parm > method get = > (0, 1) > end > > > let () = > let p = new parameters in > p #set_sf 44100 ; (* useless indeed, just for demo *) > p #set_add 2 ; (* usefull *) > let n0 = new node_head p in > let n1 = new node_plus n0 p 3 in > Printf.printf "%d\n" (snd (n0 #get)) ; > Printf.printf "%d\n" (snd (n1 #get)) ; > Printf.printf "%d\n" (snd (n1 #get)) ; > p #set_add 1 ; > Printf.printf "%d\n" (snd (n1 #get)) >