12

すべてのプリミティブ型に 2 次元ベクトル クラスが必要です。

現在、最高のランタイム パフォーマンスを保証し、多くのユーティリティ関数を使用できるようにするために、プリミティブ (Vector2Int、Vector2Float、Vector2Long など) ごとに個別のクラスを用意する必要があります。

コピーと貼り付けを繰り返すだけで、変更が必要な場合は、すべてのクラスとすべてのユーティリティ関数で忘れずに変更する必要があります。

C++ テンプレートのようなものを作成できるものはありますか (または作成する方法はありますか)?

これがどのように機能するかを示すために、小さな概念を作成しました。

// compile is a keyword I just invented for compile-time generics/templates

class Vector2<T> compile T : int, float, double, long, string
{
    public T X { get; set; }
    public T Y { get; set; }

    public T GetLength() 
    {
        return Math.Sqrt(Math.Pow(X, 2) + Math.Pow(Y, 2));
    }
}

// during compilation, code will be automatically generated
// as if someone manually replaced T with the types specified after "compile T : "
/*
    VALID EXAMPLE (no compilation errors):

    autogenerated class Vector2<int>
    {
        public int X { get; set; }
        public int Y { get; set; }

        public int GetLength() 
        {
            return Math.Sqrt(Math.Pow(X, 2) + Math.Pow(Y, 2));
        }
    }



    UNVALID EXAMPLE (build failed, compilation errors):

    autogenerated class Vector2<string>
    {
        public string { get; set; } // ok
        public string { get; set; } // ok

        public string GetLength() 
        {
            return Math.Sqrt(Math.Pow(X, 2) + Math.Pow(Y, 2)); // error! string cannot be used with Math.Pow()
                                             // and Math.Sqrt doesn't accept string type
        }
    }
*/

これを実装する賢い方法はありますか、それとも完全に不可能ですか?


分かりづらくて申し訳ありませんが、何が問題なのか説明させてください。

通常の C# ジェネリックの使用を検討してください。GetLength() メソッドはコンパイルされません。これは、使用するすべての型 (int、float、double、long) が、Math.Pow() がパラメーターとして受け入れる必要があるインターフェイスを共有する必要があるためです。

「T」トークンを文字通り型名に置き換えると、この問題が解決され、柔軟性が向上し、手書きコードのパフォーマンスが達成され、開発がスピードアップします。


C#コードを書くことでC#コードを生成する独自のテンプレートジェネレーターを作成しました:) http://www.youtube.com/watch?v=Uz868MuVvTY

4

2 に答える 2

7

残念ながら、C# のジェネリックは C++ のテンプレートとは大きく異なります。これを実現するには、さまざまなタイプの共有インターフェイス ( などIArithmetic) が存在する必要があります(これは非常に要求されていましたが、実装されていません) *。これは現在のフレームワークにはありません。

ただし、これはコード生成とT4 テンプレートを介して実行できますが、共有の「テンプレート」に基づいて各タイプのコードを生成する必要があります。

*注意: 接続要求は、少なくとも一時的にブロックされているようです。

于 2012-09-09T21:37:52.220 に答える
5

この問題に対する 2 つの解決策:

  1. 抽象クラスまたはインターフェイスの計算機 [t] を作成し、関心のある型に実装します。電卓のインスタンスをベクトル クラスに渡して、数学演算に使用できるようにします。

  2. 式ツリーを使用すると、静的コンストラクターで add、pow などのメソッドを持つ静的クラスの計算機 [t] を実際に作成できます。動的式をコンパイルし、静的メソッドでこれらのコンパイル済みラムダを呼び出すことができます。このアプローチでは、タイプごとに計算機を実装したり、それを渡したりする必要はありません (静的であるため)。

例えば:

public static class Calculator<T> {

   public static readonly Func<T, T, T> Add;
   public static readonly Func<T, T, T> Pow;

   static Calculator() {
       var p1 = Expression.Parameter(typeof(T));
       var p2 = Expression.Parameter(typeof(T));
       var addLambda = Expression.Lambda<Func<T, T, T>>(Expression.Add(p1, p2), p1, p2);
       Add = addLambda.Compile();

       // looks like the only Pow method on Math works for doubles
       var powMethod = typeof(Math).GetMethod("Pow", BindingFlags.Static | BindingFlags.Public);
       var powLambda = Expression.Lambda<Func<T, T, T>>(
           Expression.Convert(
               Expression.Call(
                   powMethod,
                   Expression.Convert(p1, typeof(double)),
                   Expression.Convert(p2, typeof(double)),
               ),
               typeof(T)
           ),
           p1,
           p2
       );
       Pow = powLambda.Compile();
   }
}

// and then in your class

T a, b;
var sum = Calculator<T>.Add(a, b);
var pow = Calculator<T>.Pow(a, b);
于 2012-09-09T21:53:55.700 に答える