C# and generic code

Started by
21 comments, last by bytecoder 17 years, 9 months ago
Quote:Original post by SamLowry
Perhaps something like this?

*** Source Snippet Removed ***

Maybe turning Calculator into a struct might improve performance. Also, is there no way of defining a member of the Vector "metaclass", not Vector<T>? The static factory methods Create(...) do not need T to be specified.


thanks.

the actual usage looks clean enough, but whats under the hood makes baby jezus cry... more memory, less performance...

just using copy-paste and code refactoring is probably the easiest way out of this.
Advertisement
Or, y'know using the pre-made D3DX vector/matrix stuff.
Quote:Original post by Telastyn
Or, y'know using the pre-made D3DX vector/matrix stuff.


unfortunately there is also use for vectors outside of graphical programming.

that said (warning, noob question ahead): isnt it possible to mix all .net languages together, given that they all use the same intermediate code? couldnt i just link in a c++/whatever vector class?
Quote:Original post by Eelco
Quote:Original post by Telastyn
Or, y'know using the pre-made D3DX vector/matrix stuff.


unfortunately there is also use for vectors outside of graphical programming.

that said (warning, noob question ahead): isnt it possible to mix all .net languages together, given that they all use the same intermediate code? couldnt i just link in a c++/whatever vector class?


It depends on how intermediate code (IC) is generated. If a C# generic is compiled to an IC generic, then it could very well be that both systems (C#'s and IC's) share the same limitations.

One could of course define it's own smarter language which will instantiate classes itself instead of relying on the IC. Hence, if you'd use Vector<int> in your code, it would be compiled to Vector_int, as if generated by copy paste with the correct substitutions. It would be very efficient, but other languages would not be able to use the Vector<T> class, only Vector_int, Vector_float, ... and whatever other classes you had your smart language generate explicitly.

I'm not an expert on how .NET works in the lower levels, so I might be plain wrong about this, but I believe my proposed "solution" would work regardless of what happens below.
Quote:Original post by Eelco
that said (warning, noob question ahead): isnt it possible to mix all .net languages together, given that they all use the same intermediate code? couldnt i just link in a c++/whatever vector class?


Adding to this what I think happens:

The issue is that C++ templates are code substitution templates - they do not get issued to IL in their uninstantiated form - the compiler basically generates a version of the vector class with the types substituted whenever you use the template. As such, while the instantiated versions may be visible to other languages, you can't grab the uninstantiated definition and create your instances at will.

The C# (managed) generics on the other hand are a bit weirder - there is support in the IL to define the types without an actual instantiation, so a generic type defined in C# and compiled into a class library can be instantiated in vb with whatever type desired. C++ is a bit less strongly typed - every program redefines the whole template and because of binary compatibility can call into a library that also defined the type. In the managed world you can't do this - a type is associated to the assembly that declares it, so even if you re-declare an identical type you can't assign instances from another assembly to it.
Quote:Original post by SamLowryBut I'll have to admit: O'Caml suffers from the same problem as C#: you won't be able to make one vector-"class" that would be perfectly parameterizable, working with integers and on floats (I already welcome jdh30 to this thread, ready to contradict this).


How's this:

module Make(Elt : sig	      type t	      val zero : t	      val one : t	      val ( ~: ) : t -> t	      val ( +: ) : t -> t -> t	      val ( -: ) : t -> t -> t	      val ( *: ) : t -> t -> t	      val ( /: ) : t -> t -> t	      val sqrt : t -> t	    end) = struct  type t = Elt.t list  open Elt  open List  let ( ~| ) = map ( ~: )  let ( +| ) = map2 ( +: )  let ( -| ) = map2 ( -: )  let ( *| ) s = map (( *: ) s)  let ( *.| ) = fold_left2 (fun t a b -> t +: a *: b) zero  let length2 r = r *.| r  let length r = sqrt(length2 r)end;;


Cheers,
Jon.
Quote:Original post by bytecoder
Actually, you can, you just have to use variants.
type number = Int of int | Float of float



You're implementing dynamic typing, which is slow and unnecessary.

Quote:
Of course, this requires wrapping all your constants with the correct type constructor and it's not very fast--one of the reasons I dislike OCaml.


Of course it isn't fast, you're writing lots of pointless code that gets executed.

Quote:
edit:
Oh, and did I mention that you'd also either have to 1) redefine the built-in operators (+ - * /) to accept either ints or floats, or pattern match everywhere in your code.


Not true. Just parameterise your vector over the type of its elements.

Quote:
The former would look something like this:
let (+|) x y = match (x, y) with        Int x, Int y -> Int (x + y)        | Int x, Float y -> Float ((float_of_int x) +. y)        | Float x, Int y -> Float (x +. (float_of_int y))        | Float x, Float y -> Float (x +. y);;...

And would have to be used like:
Float 10.0 +| Int 10


Did I mention I don't like OCaml?


Sheesh.

Cheers,
Jon.
Quote:Original post by jdh30
Quote:Original post by SamLowryBut I'll have to admit: O'Caml suffers from the same problem as C#: you won't be able to make one vector-"class" that would be perfectly parameterizable, working with integers and on floats (I already welcome jdh30 to this thread, ready to contradict this).


How's this:

module Make(Elt : sig	      type t	      val zero : t	      val one : t	      val ( ~: ) : t -> t	      val ( +: ) : t -> t -> t	      val ( -: ) : t -> t -> t	      val ( *: ) : t -> t -> t	      val ( /: ) : t -> t -> t	      val sqrt : t -> t	    end) = struct  type t = Elt.t list  open Elt  open List  let ( ~| ) = map ( ~: )  let ( +| ) = map2 ( +: )  let ( -| ) = map2 ( -: )  let ( *| ) s = map (( *: ) s)  let ( *.| ) = fold_left2 (fun t a b -> t +: a *: b) zero  let length2 r = r *.| r  let length r = sqrt(length2 r)end;;


Cheers,
Jon.


That would indeed be a module of parameterizable vectors. I did think of modules, but for some reason I saw problems where there actually weren't any.

One little annoyance would be vector literals, but I guess that with the necessary helper functions it could be made as simple as "vector [5; 6; 3]" (I really don't want to have to type [Elt.one +: Elt.one +: Elt.one +: Elt.one +: Elt.one; Elt.one +: Elt.one +: Elt.one +: Elt.one +: Elt.one +: Elt.one; Elt.one +: Elt.one +: Elt.one])

Another annoyance would be the sort-of dynamic typing on the size of the vector: there's no compile-time check you are adding a 2D-vector with another 2D-vector. Of course, you could just define separate modules, but that would lead to duplication, and I don't like duplication. A solution I see is to have a module per kind of vector, with a hidden "type t = V2D of Elt.t list", a factory function "Vector2D.create x y = V2D [x;y]" and functions which retrieve the Elt.t list and pass it to a general VectorN module, but that would still require a lot of rather useless code. I'm currently not creative enough to find a truly elegant solution.
Quote:Original post by SamLowry
That would indeed be a module of parameterizable vectors. I did think of modules, but for some reason I saw problems where there actually weren't any.

One little annoyance would be vector literals, but I guess that with the necessary helper functions it could be made as simple as "vector [5; 6; 3]" (I really don't want to have to type [Elt.one +: Elt.one +: Elt.one +: Elt.one +: Elt.one; Elt.one +: Elt.one +: Elt.one +: Elt.one +: Elt.one +: Elt.one; Elt.one +: Elt.one +: Elt.one])


No, the literals are already as simple as you can get:

[1; 2; 3]


Quote:
Another annoyance would be the sort-of dynamic typing on the size of the vector: there's no compile-time check you are adding a 2D-vector with another 2D-vector. Of course, you could just define separate modules, but that would lead to duplication, and I don't like duplication. A solution I see is to have a module per kind of vector, with a hidden "type t = V2D of Elt.t list", a factory function "Vector2D.create x y = V2D [x;y]" and functions which retrieve the Elt.t list and pass it to a general VectorN module, but that would still require a lot of rather useless code. I'm currently not creative enough to find a truly elegant solution.


Yes. I'd just write a compiler to generate the optimised, specialised OCaml code...

Cheers,
Jon.
Quote:Original post by jdh30
No, the literals are already as simple as you can get:
[1; 2; 3]


I had change in mind: if I had vector<float> and wanted to change it to vector<double>. If I wanted to do something similar in O'Caml, I'd have to change all literals, such as [0; 0; 0] to [0.; 0.; 0.]. In order to prevent this, I'd think you'd need some helper functions:
module type Number =  sig    type t    val from_int : int -> t    (* ... *)  end;;module Integer =  struct     type t = int     let from_int x = x  end;;module Float =  struct    type t = float    let from_int = float_of_int  end;;module Vector (Elt : Number) =  struct    let create coords =      List.map (Elt.from_int) coords  end;;let v = Vector.create [0;0;0]

This topic is closed to new replies.

Advertisement