Search code examples
c#.netvisual-studio-2008t4dsl-tools

T4 (Text Template Transformation Toolkit) for generating a set of types based on a list of basic types e.g. PointI32, PointF32 etc. in C#


Similar to Boost Preprocessor library for generating a set of types based on a list of basic types e.g. PointI32, PointF32 etc. in C++/CLI I am asking how to generate:

struct Point##TYPE_SUFFIX_NAME
{
    TYPE X
    { get; set; }
    TYPE Y;
    { get; set; }

    // Other code
};

for different basic (POD) data types e.g.:

PointF32, PointF64, PointI32 etc.

using T4 (Text Template Transformation Toolkit) in Visual Studio 2008 or later.

See http://www.olegsych.com/2007/12/text-template-transformation-toolkit/ and http://msdn.microsoft.com/en-us/library/bb126445.aspx


Solution

  • Well, I have the answer my self. I have created the following T4 include files:

    • SignedIntegersSuffices.ttinclude
    • UnsignedIntegersSuffices.ttinclude
    • IntegersSuffices.ttinclude
    • FloatsSuffices.ttinclude
    • BasicTypesSuffices.ttinclude

    and then the actual T4 template is PointTypes.tt. These files are simply added to a C# project and Visual Studio will detect the .tt and generate a matching PointTypes.cs file, whenever the .tt file is saved.

    These files are listed in the following.

    SignedIntegersSuffices.ttinclude

    <#+
    IEnumerable<KeyValuePair<string, string>> SignedIntegersSuffices()
    {
        var signedIntegersSuffices = new KeyValuePair<string, string>[] { 
            new KeyValuePair<string, string>("sbyte", "I8"),
            new KeyValuePair<string, string>("short", "I16"),
            new KeyValuePair<string, string>("int", "I32"),
            new KeyValuePair<string, string>("long", "I64")
        };
        return signedIntegersSuffices;
    }
    #>
    

    UnsignedIntegersSuffices.ttinclude

    <#+
    IEnumerable<KeyValuePair<string, string>> UnsignedIntegersSuffices()
    {
        var signedIntegersSuffices = new KeyValuePair<string, string>[] { 
            new KeyValuePair<string, string>("byte", "UI8"),
            new KeyValuePair<string, string>("ushort", "UI16"),
            new KeyValuePair<string, string>("uint", "UI32"),
            new KeyValuePair<string, string>("ulong", "UI64")
        };
        return signedIntegersSuffices;
    }
    #>
    

    IntegersSuffices.ttinclude

    <#@ include file="SignedIntegersSuffices.ttinclude" #>
    <#@ include file="UnsignedIntegersSuffices.ttinclude" #>
    <#+
        // Insert any template procedures here
        IEnumerable<KeyValuePair<string, string>> IntegersSuffices()
        {
            var integersSuffices = SignedIntegersSuffices().Concat(UnsignedIntegersSuffices());
            return integersSuffices;
        }
    #>
    

    FloatsSuffices.ttinclude

    <#+
        // Insert any template procedures here
        IEnumerable<KeyValuePair<string, string>> FloatsSuffices()
        {
            var floatsSuffices = new KeyValuePair<string, string>[] { 
                new KeyValuePair<string, string>("float", "F32"),
                new KeyValuePair<string, string>("double", "F64")
            };
            return floatsSuffices;
        }
    #>
    

    BasicTypesSuffices.ttinclude

    <#@ include file="IntegersSuffices.ttinclude" #>
    <#@ include file="FloatsSuffices.ttinclude" #>
    <#+
        // Insert any template procedures here
        IEnumerable<KeyValuePair<string, string>> BasicTypesSuffices()
        {
            var basicTypesSuffices = IntegersSuffices().Concat(FloatsSuffices());
            return basicTypesSuffices;
        }
    #>
    

    And finally the actual template file PointTypes.tt:

    <#@ template inherits="Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation" language="C#v3.5" debug="true" hostSpecific="true" #>
    <#@ output extension=".cs" #>
    <#@ Assembly Name="System.dll" #>
    <#@ Assembly Name="System.Core.dll" #>
    <#@ import namespace="System" #>
    <#@ import namespace="System.IO" #>
    <#@ import namespace="System.Diagnostics" #>
    <#@ import namespace="System.Linq" #>
    <#@ import namespace="System.Collections" #>
    <#@ import namespace="System.Collections.Generic" #> 
    <#@ include file="BasicTypesSuffices.ttinclude" #>
    namespace TextTemplatesTest
    {
    <#
        const string pointPrefix = "Point";
        foreach (var typeSuffix in BasicTypesSuffices())
        {
            string type = typeSuffix.Key;
            string suffix = typeSuffix.Value;
    #>
        public struct <#= pointPrefix #><#= suffix #>
        {
            <#= pointPrefix #><#= suffix #>(<#= type #> x, <#= type #> y)
                : this()
            {
                X = x;
                Y = y;
            }
    
            public <#= type #> X
            { get; set; }
            public <#= type #> Y
            { get; set; }
        }
    <#
        }
    #>
    }
    

    The output file PointTypes.cs will then look like this:

    namespace TextTemplatesTest
    {
        public struct PointI8
        {
            PointI8(sbyte x, sbyte y)
                : this()
            {
                X = x;
                Y = y;
            }
    
            public sbyte X
            { get; set; }
            public sbyte Y
            { get; set; }
        }
        public struct PointI16
        {
            PointI16(short x, short y)
                : this()
            {
                X = x;
                Y = y;
            }
    
            public short X
            { get; set; }
            public short Y
            { get; set; }
        }
        public struct PointI32
        {
            PointI32(int x, int y)
                : this()
            {
                X = x;
                Y = y;
            }
    
            public int X
            { get; set; }
            public int Y
            { get; set; }
        }
        public struct PointI64
        {
            PointI64(long x, long y)
                : this()
            {
                X = x;
                Y = y;
            }
    
            public long X
            { get; set; }
            public long Y
            { get; set; }
        }
        public struct PointUI8
        {
            PointUI8(byte x, byte y)
                : this()
            {
                X = x;
                Y = y;
            }
    
            public byte X
            { get; set; }
            public byte Y
            { get; set; }
        }
        public struct PointUI16
        {
            PointUI16(ushort x, ushort y)
                : this()
            {
                X = x;
                Y = y;
            }
    
            public ushort X
            { get; set; }
            public ushort Y
            { get; set; }
        }
        public struct PointUI32
        {
            PointUI32(uint x, uint y)
                : this()
            {
                X = x;
                Y = y;
            }
    
            public uint X
            { get; set; }
            public uint Y
            { get; set; }
        }
        public struct PointUI64
        {
            PointUI64(ulong x, ulong y)
                : this()
            {
                X = x;
                Y = y;
            }
    
            public ulong X
            { get; set; }
            public ulong Y
            { get; set; }
        }
        public struct PointF32
        {
            PointF32(float x, float y)
                : this()
            {
                X = x;
                Y = y;
            }
    
            public float X
            { get; set; }
            public float Y
            { get; set; }
        }
        public struct PointF64
        {
            PointF64(double x, double y)
                : this()
            {
                X = x;
                Y = y;
            }
    
            public double X
            { get; set; }
            public double Y
            { get; set; }
        }
    }
    

    Pretty simple and quite effective. Now, of course, this could have been done using generics etc, but that is not the point here. This is meant as a simple example of code generation for multiple basic types.

    Comments to improve this or similar are welcomed.