5

I'm porting a C++ application to C#, and have run across templates. I've read up a little on these, and I understand that some templates are akin to .Net generics. I read the SO answer to this case which nicely summed it up.

However, some uses of c++ templating don't seem to be directly related to generics. In the below example from Wikipedia's Template metaprogramming article the template seems to accept a value, rather than a type. I'm not quite sure how this would be ported to C#?

template <int N>
struct Factorial 
{
    enum { value = N * Factorial<N - 1>::value };
};

template <>
struct Factorial<0> 
{
    enum { value = 1 };
};

// Factorial<4>::value == 24
// Factorial<0>::value == 1
void foo()
{
    int x = Factorial<4>::value; // == 24
    int y = Factorial<0>::value; // == 1
}

Clearly for this example I could do:

public int Factorial(int N){
    if(N == 0) return 1;
    return Factorial(N - 1);
}

but this seems to me to be a refactoring to a function, rather than a port to semantically similar code.

Community
  • 1
  • 1
Iain Sproat
  • 5,210
  • 11
  • 48
  • 68
  • Why would you use a classic recursive function as a c# generics? Try reading this article http://msdn.microsoft.com/en-us/library/bb549151.aspx for usage of Func – nemke Jan 19 '11 at 13:37
  • 1
    The problem most probably will not be with constants (vs. types) as parameters to templates, but with specializations and partial specializations of classes or functions if they are currently used in your code base. Without some particular example, answer will need to be broad and probably not really useful. I am voting to close for that reason, feel free to add specific questions about some piece of code that you have problems converting. – David Rodríguez - dribeas Jan 19 '11 at 13:38
  • 1
    @nemke - In this example, the Factorial template expands out at compile time - thus no work is actually done at run time. The OP wants to port the code without having to translate too much into different types of C# call - so would prefer a generic-based solution if possible. Unfortunately, it's not. @David - very good point too – Andras Zoltan Jan 19 '11 at 13:40
  • BTW, *semantically* the recursive function and the template perform the same operation, it is in the *implementation* that they differ: the template is resolved at compile time and expanded to a constant, while the equivalent C# code is executed at runtime. Anyway that is probably the best translation you can do now. But this is just a toy example... – David Rodríguez - dribeas Jan 19 '11 at 13:42

4 Answers4

5

Unfortunately .Net generics can only accept types. C++ Templates take other values that are considered constant expressions by the compiler, because they are effectively just macros that expand to more code.

This means that your idea of turning the code into a method call is the best bet. You could make the method call return a type with a .Value Property (following your example) thus keeping the ported code similar to the template:

return Factorial(N-1).Value;
Andras Zoltan
  • 41,961
  • 13
  • 104
  • 160
  • There is the additional point that the C# and C++ compilers are fundamentally different in that C++ is littered with functionality that allows code-writing at compile time (macros and templates) whereas the C# team deliberately stayed clear of this - citing readability as their reason. C# might be moving closer to it - but it'll never be the same. – Andras Zoltan Jan 19 '11 at 13:43
  • +1 Generics in C# are just a convenience, according to the MSDN link everything is done dynamically anyway, so all you gain is compile-time type check (which is a good enough thing) but not compile-time calculations (which is what you example is doing). – MatiasFG Jan 19 '11 at 13:51
  • @MatiasFG - exactly the point - generics are dynamically compiled but type checking for their code is done at compile time of the generic type. I do miss not having the template functionality in C# having started to do Template Meta-Programming in C++ just before becoming a C# developer full time - however generics are very very cool in their own right and I'm glad we have them! – Andras Zoltan Jan 19 '11 at 14:52
5

In the below example … the template seems to accept a value, rather than a type.

This isn’t your biggest problem. In fact, this could theoretically be solved in C# by using a Church numeral or Peano representation relying on nested generic types.1

However, your problem is that C# doesn’t allow template specialization. Template specialization is responsible in your example for defining that the factorial of 0 is 1, rather than the same as for all other numbers. C# doesn’t allow doing that.

So there is no way to specify a base case in a recursive template (generic) definition and hence no recursion. C# generics aren’t Turing complete, whereas C++ templates are.


1 Something like this:

class Zero { }

class Successor<T> : Zero where T : Zero { }

// one:
Successor<Zero>
// two:
Successor<Successor<Zero>>
// etc.

Implementing operations on these numbers is left as an exercise to the reader.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
1

The short answer is that, not everything that can be done in C++ templates can be done in C# generics. In the case of templates which accept non type values, each situation will have to be handled and re factored appropriately on a case by case basis.

Wes P
  • 9,622
  • 14
  • 41
  • 48
0

This is as close as I could think of:

public class Factorial<T>
    where T : IConvertible 
    {
        public T GetFactorial(T t)
        {
            int int32 = Convert.ToInt32(t);
            if (int32 == 0)
                return (T) Convert.ChangeType( 1, typeof(T));
            return GetFactorial( (T) Convert.ChangeType(int32-1, typeof(T)) );
        }
    }

The problem is you cannot define generics and limit it to ValueTypes. This will work for byte, Int16 and Int32. Also for small values of Int64.

Aliostad
  • 80,612
  • 21
  • 160
  • 208
  • To clarify, the problem is that you cannot restrict to numeric values. You could put some constraints on there for struct and IComparable, but that's hardly enough to determine numeric/non-numeric. – Wes P Jan 19 '11 at 13:58