3

I am trying to write generic Vector2 type which would suite float, double, etc. types and use arithmetical operations. Is there any chance to do it in C#, F#, Nemerle or any other more or less mature .NET language?

I need a solution with

  • (1)good performance (same as I would have writing separate Vector2Float, Vector2Double, etc. classes),
  • (2)which would allow code to look nice (I do not want to emit code for each class in run-time)
  • (3)and which would do as much compile time checking as possible.

For reasons 1 and 3 I would not like to use dynamics. Right now I am checking F# and Nemerle.

UPD: I expect to have a lot of mathematical code for this type. However, I would prefer to put the code in extension methods if it is possible.

UPD2: 'etc' types include int(which I actually doubt I would use) and decimal(which I think I might use, but not now). Using extension methods is just a matter of taste - if there are good reasons not to, please tell.

ironic
  • 8,368
  • 7
  • 35
  • 44
  • What does this type do? Just store data? Or does it have methods too? What do the methods do? ... that matters. – Simon Whitehead Feb 25 '14 at 12:46
  • What are the "etc" types? – Jon Feb 25 '14 at 12:49
  • What would a Vector2 type be? – poke Feb 25 '14 at 12:49
  • *“I would prefer to put the code in extension methods if it is possible”* – **why**? – poke Feb 25 '14 at 12:51
  • possible duplicate of [C# generic constraint for only integers](http://stackoverflow.com/questions/32664/c-sharp-generic-constraint-for-only-integers). In short: it's not possible. Look into the suggestions there for a workaround (but it won't be elegant, rest assured) – Jeroen Vannevel Feb 25 '14 at 12:59
  • 1
    You might find this article useful: http://tomasp.net/blog/fsharp-generic-numeric.aspx/ :-) – Tomas Petricek Feb 25 '14 at 15:16
  • 1
    BTW: This is a perfectly valid question and not a duplicate of the C# question mentioned. F# has a few things that make this actually much nicer than in C# :-) – Tomas Petricek Feb 25 '14 at 15:18
  • @TomasPetricek, actually, originally it was a C# question with slight mention of other languages, but then discussion went into F# direction, so I edited it a little... not sure whether it was completely right to do... – ironic Feb 25 '14 at 15:32
  • @ironic Improving your question is perfectly fine! – Tomas Petricek Feb 25 '14 at 16:57
  • 1
    @ironic: Take a look at [my answer here](http://stackoverflow.com/a/22425077/1864167), it should solve this problem in C#. – Jeroen Vannevel Mar 17 '14 at 23:09

6 Answers6

8

As mentioned by Daniel, F# has a feature called statically resolved type arguments which goes beyond what you can do with normal .NET generic in C#. The trick is that if you mark function as inline, F# generates specialized code automatically (a bit like C++ templates) and then you can use more powerful features of the F# type system to write generic math.

For example, if you write a simple add function and make it inline:

let inline add x y = x + y;;    

The type inference prints the following type:

val inline add :
  x: ^a -> y: ^b ->  ^c
    when ( ^a or  ^b) : (static member ( + ) :  ^a *  ^b ->  ^c)

You can see that the inferred type is fairly complex - it specifies a member constraint that requires one of the two arguments to define a + member (and this is also supported by standard .NET types) The good thing is that this can be fully inferred, so you will rarely have to write the ugly type definitions.

As mentioned in the comments, I wrote an article Writing generic numeric code that goes into more details of how to do this in F#. I don't think this can be easily done in C# and the inline functions that you write in F# should only be called from F# (calling them from C# would essentially use dynamic). But you can definitely write your generic numerical computations in F#.

Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
  • As far as I know, this is the only way. [Boo](http://boo.codehaus.org/) supports something similar, but C# / VB.Net etc don't. – The Fiddler Mar 03 '14 at 07:40
3

This more directly addresses your previous question. You can't put a static member constraint on a struct, but you can put it on a static Create method.

[<Struct>]
type Vector2D<'a> private (x: 'a, y: 'a) =
  static member inline Create<'a  when 'a : (static member (+) : 'a * 'a -> 'a)>(x, y) = Vector2D<'a>(x, y)
Daniel
  • 47,404
  • 11
  • 101
  • 179
1

C# alone will not help you in achieving that, unfortunately. Emitting structs at run-time wouldn't help you much either since your program couldn't statically refer to them.

If you really can't afford to duplicate the code, then as far as I know, "offline" code generation is the only way to go about this. Instead of generating the code at runtime, use AssemblyBuilder and friends to create an on-disk assembly with your Vector2 types, or generate a string of C# code to be fed to the compiler. I believe some of the native library wrappers take this route (ie OpenTK, SharpDX). You can then use ilmerge if you want to merge those types to one of your hand-coded libraries.

I'm assuming you must be coming from a C++ background where this is easily achieved using templates. However, you should ask yourself whether you actually need Vector2 types based on integral, decimal and other "exotic" numeric types. You probably won't be able to parameterize the rest of your code based on a specific Vector2 either so the effort might not be worth it.

Trillian
  • 6,207
  • 1
  • 26
  • 36
1

Look into inline functions and Statically Resolved Type Parameters.

Daniel Fabian
  • 3,828
  • 2
  • 19
  • 28
  • 1
    @ironic - They can be, though it's unusual. See my answer [here](http://stackoverflow.com/a/5905776/82959). – kvb Feb 25 '14 at 14:39
  • @kvb, I still cannot resolve this problem: http://stackoverflow.com/questions/21992377/f-generic-struct-constructor – ironic Feb 25 '14 at 14:53
  • @ironic: Does the type need to be a struct? It's a little easier to implement the constraints if you don't have to worry about a default constructor. – Daniel Feb 25 '14 at 15:09
  • @Daniel, I prefer it to be struct for 2 reasons - first it is usually stated that structs are a little faster and second, with structs I can do tricks pinning arrays of them and doing some unsafe processing – ironic Feb 25 '14 at 15:14
  • @ironic - Try using a default constructor instead of an explicit one. – kvb Feb 25 '14 at 20:12
0

As I understand you a strict type in the compile time , but you don't care what happens in the runtime. Nemerle language currently doesn't support this construction as you want. But it supports macros and allows you writing DSLs to generate arbitrary code. For instance you can do some macro which analyzes this code and transforms it to the correct type.

def vec = vector { [1,2] };

Assuming we have or create a type VectorInt the code could be translated to

def vec = VectorInt(1,2);

Of course you can write any code inside and transform it to any code you want :)

Operators can be implemented as usual operators of the class. Nemerle also allows you to define any operators like F#.

NN_
  • 1,593
  • 1
  • 10
  • 26
-2

make use of Generics , this makes is also type safe

more info on generics : http://msdn.microsoft.com/en-us/library/512aeb7t.aspx

But you also have availible datastructures such as List and Dictionary

Sounds like you want operator overloading, there are a lot of examples for this. There is not realy a good way to only allow decial, float and such. The only thing you can do is restrict to struct, but thats not exactly what you want.

lordkain
  • 3,061
  • 1
  • 13
  • 18
  • 2
    Unless I'm mistaking, it's not possible to restrict generic arguments to number-types only. Can you expand on a solution that takes this in account (considering all you did here was link to a generic generics tutorial). – Jeroen Vannevel Feb 25 '14 at 12:57
  • Sorry for not being specific enough - I need a solution that would allow compiler to know that my type parameter 'T' has static operations +,-,/,* and in result make corresponding IL instructions. – ironic Feb 25 '14 at 12:59