Thread: Some help with "templates"

  1. #1
    Registered User
    Join Date
    Oct 2006
    Location
    UK/Norway
    Posts
    485

    Some help with "templates"

    Hallo,

    I am trying to build a generic debug class for printing information about different objects to the screen, and also change them if wanted, but I am having some problems with adding the ability to change them.

    Here is my DebugItemClass.

    The problem is in the update function, the code does not compile as c# does not like it when you try to assign other types of classes to the T object. Any ideas on what the correct way to do this is?
    Code:
    abstract class GenericDebugMenuItem
        {
            public abstract void Update(int value);
            public abstract void Draw(GameRender render, Vector2 position);
        }
    
        class DebugMenuItem<T> : GenericDebugMenuItem
        {
            T m_item;
            string m_text;
            float m_inputScale;
            DebugItemValueType m_type;
    
            bool m_selected;
            public bool Selected
            {
                get { return m_selected; }
                set { m_selected = value; }
            }
    
            public DebugMenuItem(ref T item, string text, float inputScale, DebugItemValueType type)
            {
                m_item = item;
                m_text = text;
                m_inputScale = inputScale;
                m_type = type;
    
                m_selected = false;
            }
    
            public override void Draw(GameRender render, Vector2 position)
            {
                string output = m_text + m_item.ToString();
    
                if (m_selected)
                    render.AddTextToRender(output, position, Color.Yellow);
                else
                    render.AddTextToRender(output, position, Color.Red);
            }
    
            public override void Update(int value)
            {
                switch (m_type)
                {
                    case DebugItemValueType.E_double:
                        m_item = m_item + (value * m_inputScale);
                        break;
                    case DebugItemValueType.E_bool:
                        if (value == -1)
                            m_item = false;
                        else if (value == 1)
                            m_item = true;
                        break;
                }
            }
        }

    Edit:
    I also have an other question, how can I store the pointer to the object that is passed it? Object are normally passed by value, but I want to store the pointer, so that if I change the value it gets updated where it was used.
    Last edited by h3ro; 03-03-2010 at 03:31 PM.

  2. #2
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    This wouldn't work in any language. T refers to a specific type. It does not mean you can switch that type to bool or int on the fly - it means the entire generic will be using type T. It is not the same as a C++ template....in the purest sense....but it is very close.

    Code:
    if (value == -1)
                            m_item = false;
                        else if (value == 1)
                            m_item = true;
    Code:
    class DebugMenuItem<T> : GenericDebugMenuItem
        {
            T m_item;
    ...
    Why are you assigning boolean values to m_item when m_item is of type T? You are assuming m_item is a bool in one part of Update, but in other functions and in Update itself you are assuming it is a generic type.

    Due to the syntax of C# I think you are missing a key feature of generics. I'll illustrate with C++ code.

    Code:
    template <typename T>
    void MyClass<T>::Foo()
    {
    }
    This syntax shows you that at compile time T will be replaced with the data type being specified. If you did this:

    Code:
    MyClass<float> floatClass;
    T is now float everywhere it is used in the template. Now going back to your class you are saving m_item as type T but then forcing it to bool later. If m_item is of type T and yet T will be determined at compile time then how can you assign boolean values to a type when you don't know what the type is?

    In C# generics are not done at compile time but the typename T nomenclature is used identical to how it is in C++ so the comparison works here.
    Last edited by VirtualAce; 03-03-2010 at 05:39 PM.

  3. #3
    Registered User
    Join Date
    May 2003
    Posts
    1,619
    I think his goal is really to achieve template specialization in C# -- that is, he wants to have different code for DebugMenuItem<double>.Update() versus DebugMenuItem<bool>.Update().

    Unfortunately C# doesn't support C++'s level of template specialization, but you can mimic it:

    How to do template specialization in C# - Stack Overflow
    You ever try a pink golf ball, Wally? Why, the wind shear on a pink ball alone can take the head clean off a 90 pound midget at 300 yards.

  4. #4
    Registered User
    Join Date
    Oct 2006
    Location
    UK/Norway
    Posts
    485
    Thanks to both of you for your reply. Im starting to see that this is probably not something c# is encouraging you to do.

    I got a solution working, but its very nasty and I was wondering if someone could help me make the syntax a bit better?
    It has to follow this rules:
    Must be safe code.
    Must allow me to change the value of the item passed in in its original place.
    Must work with properties.

    Code:
    public delegate void SetFunctor(object obj);
        public delegate object GetFunctor();
        class DebugItem
        {
            SetFunctor m_set;
            GetFunctor m_get;
            string m_text;
            DebugItemValueType m_type;
    
            public DebugItem( SetFunctor setter, GetFunctor getter, string text, DebugItemValueType type)
            {
                m_set = setter;
                m_get = getter;
                m_text = text;
                m_type = type;
            }
    
            public void Update(int value)
            {
                switch (m_type)
                {
                    case DebugItemValueType.E_bool:
                        if (value == -1)
                            m_set(false);
                        else if (value == 1)
                            m_set(true);
                        break;
                }
            }
        }
    And here is how I create a instance:
    Code:
    DebugItem test = new DebugItem( delegate(Object boolVal) { Active = (bool)boolVal; },
                                    delegate() { return Active; },
                                    "some string",
                                    DebugItemValueType.E_bool);
    Any ideas?

  5. #5
    Anti-Poster
    Join Date
    Feb 2002
    Posts
    1,401
    If you're working with classes under your control, I'd probably create an interface with all the methods necessary to do your debugging and then implement the interface on your classes. This keeps all the debugging logic right there with the classes.

    In looking at your approach, I'm not 100% certain I understand, but here's my take on it:
    Code:
    class DebugItem<T>
    {
        public delegate void SetFunctor(T obj);
        public delegate T GetFunctor();
    
        SetFunctor m_set;
        GetFunctor m_get;
        string m_text;
    
        public DebugItem(SetFunctor setter, GetFunctor getter, string text)
        {
            m_set = setter;
            m_get = getter;
            m_text = text;
        }
    
        internal SetFunctor Set { get { return m_set; } }
        internal GetFunctor Get { get { return m_get; } }
    }
    
    static class DebugItemSpecialization
    {
        public static void Update(this DebugItem<bool> debugItem, int value)
        {
            if (value == -1)
                debugItem.Set(false);
            else if (value == 1)
                debugItem.Set(true);
        }
    }
    
    class TestClass
    {
        bool m_isEnabled;
    
        public bool IsEnabled { get { return m_isEnabled; } set { m_isEnabled = value; } }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            TestClass test = new TestClass();
            DebugItem<bool> debugItem = 
                    new DebugItem<bool>((bool b) => test.IsEnabled = b,
                                                          () => test.IsEnabled,
                                                          "test");
            debugItem.Update(1);
        }
    }
    If I did your homework for you, then you might pass your class without learning how to write a program like this. Then you might graduate and get your degree without learning how to write a program like this. You might become a professional programmer without knowing how to write a program like this. Someday you might work on a project with me without knowing how to write a program like this. Then I would have to do you serious bodily harm. - Jack Klein

Popular pages Recent additions subscribe to a feed