C# and typedef

Started by
130 comments, last by TangentZ 19 years, 11 months ago
I''ve been thinking about typedef as D is said to have. I don''t know D, so I would apreciate if someone corrects me if she does.

In D, let''s say you do:

typedef int speed;
speed sp1 = 34;
speed sp2 = abs(sp2)

I suppose D allows line 2. What happens with line 3? If D allows it, it means speed has conversion from and to int. Then it''s useless as a new type. If it doesn''t, then the functionality of D''s typedef is very low. Also, if D handles line 3, how does it behave around:

void doSomethingToEachInt(int* p, int count)

Altough, p should be corrected to speed, count shouldn''t.

TIA,
Bruno
Advertisement
quote:Original post by Xai
Is C lying to you when you do this:

typedef int DatabaseID;
DatabaseID id;

maybe ... any yet the only real world case where this lie ever bites anyone is in the operator / function overloading arena (the lack of first level status also prevents usefull casting, but that is a missing ability, not a misleading and incorrect lie). And the compiler tells you when the lie has struck - and that is the point at which you refactor the class into a real type if you need to.


this is exactly the reason why a DECLARE_HANDLE() macro exists in the win32 api, wich creates a struct HANDLENAME { int __unused; }. because typedef lies.

just imagine what you could do if you could ++hwnd;.. urgh.

you can, though, ++hwnd.__unused; .. but thats less errorprone.



If that''s not the help you''re after then you''re going to have to explain the problem better than what you have. - joanusdmentia

davepermen.net
If that's not the help you're after then you're going to have to explain the problem better than what you have. - joanusdmentia

My Page davepermen.net | My Music on Bandcamp and on Soundcloud

quote:Original post by Br1
I''ve been thinking about typedef as D is said to have. I don''t know D, so I would apreciate if someone corrects me if she does.

In D, let''s say you do:

typedef int speed;
speed sp1 = 34;
speed sp2 = abs(sp2)

I suppose D allows line 2. What happens with line 3? If D allows it, it means speed has conversion from and to int. Then it''s useless as a new type. If it doesn''t, then the functionality of D''s typedef is very low. Also, if D handles line 3, how does it behave around:

void doSomethingToEachInt(int* p, int count)

Altough, p should be corrected to speed, count shouldn''t.

TIA,
Bruno


just for your interest.. The D Page. so you can check yourself. i''m currently not sure. sp1 is valid, sp2 not, i guess.. that can change soon, if we get some sort of parameter deduction for templates.. currently, it could be done like this:

speed s2 = abs!(speed)(s1); or more generic
speed s2 = abs!(typeof(s1))(s1); wich will possibly soon look like
speed s2 = abs?(s1); hopefully.



If that''s not the help you''re after then you''re going to have to explain the problem better than what you have. - joanusdmentia

davepermen.net
If that's not the help you're after then you're going to have to explain the problem better than what you have. - joanusdmentia

My Page davepermen.net | My Music on Bandcamp and on Soundcloud

quote:Original post by davepermen
just imagine what you could do if you could ++hwnd;.. urgh.

you can, though, ++hwnd.__unused; .. but thats less errorprone.
Is that really a serious concern? No sensible programmer could do either one by accident.
I find this conversation a little muddling.

Surely there are legitimate cases to create a type that behaves just like a built-in type. Say you''re writing a word processor and you''ve created a class called Stream that implements a big editable array of characters (you''re planning on using one for the main "story" of your document, another one for each header or footer, another for each footnote, etc.) This class is going to have a bunch of methods that take arguments that are indicies into the stream and other arguments that are "distances" in the stream. They''re all really just unsigned ints, so you could write one such method as ...

/* Replace a run of characters in this stream with a run of characters copied from elsewhere in the stream. */
void Replace(
uint where,
uint howmany,
uint source_where,
uint source_howmany);

Viewed through any sort of abstraction there are really two different "int" types there - some of them ("where") are indices into the array and others ("howmany") are deltas between indices.

Compare this wih the following ...

typedef uint CharacterIndex;
typedef uint CharacterCount;
void Replace(
CharacterIndex iReplace,
CharacterCount cReplace,
CharacterIndex iSource,
CharacterCount cSource);

Surely this is more self-documenting and hence less error-prone. Whenever I declare a variable (say in some function that calls this one) that''s going to be used as a character index I can actually declare it to _be_ a CharacterIndex.

That said, I think you _can_ do this (or nearly this) in C#, by using enums (which have an underlying type). This is also convenient because it gives a graceful place to define a few special values for each of these types. As in ...

enum CharacterIndex : uint
{
First = 0x00000000
Last = 0xFFFFFFFD, // special value meaning "end of stream"
Max = 0xFFFFFFFE, // special value meaning "end of stream + 1"
Nil = 0xFFFFFFFF, // special value for "not really a valid index"
};

Which now leaves me writing code (say to fix spelling errors) like ...

CharacterIndex ciError = CharacterIndex.First;
CharacterCount ccError = CharacterCount.Nil;
CharacterIndex ciCorrection = CharacterIndex.Nil;
CharacterIndex ccCorrection = CharacterCount.Nil;

while (LocateNextSpellingError(ref ciError, ref ccError))
{
if (stmDictionary.OneObviousCorrection(ciError, ccError, ref ciCorrection, ref ccCorrection))
{
Replace(ciError, ccError, ciCorrection, ccCorrection);
}
}

In the "more strongly typed" world the compiler now has a chance to enforce rules that would otherwise only be present in the documentation. For example, if I screwed up the call and wrote ...

Replace(ciError, ciCorrection, ccError, ccCorrection);

... I''d get a compiler error instead of the run-time bugs you''d get were they all just uints.

Another example - a (really good) profiler could now answer a question like "How many times during a large operation did I push a CharacterIndex and then a CharacterCount onto the stack - should I perhaps consider creating a CharacterRange class that contains those two."

- Peter
Sorry about that. It would appear that all of the initial spaces were stripped out of my previous post. That make sense, actually, since how often in a forum about programming would anyone want to include code snippets. Grumble. Perhaps there''s some trick I''m unaware of?
With [code][/code] tags. How is it that so many people keep asking this, I knew about it right away. Do people not read FAQs etc. anymore?
quote:Original post by tangentz
Hmmm... I thought I was pretty clear on what I''m trying to do.

I''m designing a kind of Container. It contains a specific
type of values. Whether it''s byte, int, long, float, double,
I haven''t decided yet. I want to be able to use a "typedef"
defined by the container so that I write outside code and
declare varibles of that type.

This way, when I later decide to change the type, I can do
so without changing outside code I''ve already written.

In C++, that''s extremely easy.

class MyContainer    {    public:        typedef int value_type;        std::vector<value_type> values;    };// end// Outside codeMyContainer::value_type myVar = 5;   // No problemMyContainer.add(myVar);


I don''t know any more elegant than that. Extremely simple
and effective. I can change "int" to "long" or "short"
without changing the outside code.

*THAT* is what I want.

If you think that''s a useless example, think OpenGL vertices.
They can be stored as GL_BYTE, GL_INT, GL_FLOAT, GL_DOUBLE..etc.
I want to test with different data types.

Why is it so hard in C#? Hmm...

And, no, I''m not asking for templates (generics). My
container will use only one specific type when I''m done.

I don''t know, wyrd, do you have a good solution to my problem?

<HR>
Kami no Itte ga ore ni zettai naru!



If you really want this behaviour then this code seems to do the job though you probably are better off using the .net collection classes.
using System;namespace NoTypedef{  using UnderlyingType = System.Int64; //or whatever you want    /// <summary>  /// Very simple container class  /// </summary>  class ContainerClass  {    /// <summary>    /// The container''s value type.    /// </summary>    public struct ValueType    {      private UnderlyingType underlyingType;      public ValueType(UnderlyingType init)      {        underlyingType = init;      }         public static implicit operator ValueType(UnderlyingType init)      {        ValueType val = new ValueType(init);        return val;      }    }        private ValueType[] m_containedValues;    private int m_currentIndex;        public ContainerClass(int maxSize)    {      m_containedValues = new ValueType[maxSize];      m_currentIndex = 0;    }        public bool Add(ValueType val)    {            if( m_currentIndex >= m_containedValues.Length )      {        //ran out of room        return false;      }      // copy the contents and move our position on one.      m_containedValues[m_currentIndex] = val;      m_currentIndex++;       return true;    }  }  class MainClass  {    /// <summary>    /// The main entry point for the application.    /// </summary>    [STAThread]    static void Main(string[] args)    {            ContainerClass container = new ContainerClass(5);      ContainerClass.ValueType val = 72;      if( container.Add(val) )      {        Console.WriteLine("Added value to container");      }      else      {        Console.WriteLine("Failed to add value to container");      }             }  }  }

quote:Original post by Zahlman
With [code][/code] tags. How is it that so many people keep asking this, I knew about it right away. Do people not read FAQs etc. anymore?


Which FAQ says to use "code" tags and not "source" tags?



--
Dave Mikesell Software
To AP: thanks for the suggestion. It does look like it can
emulate typedef in C#. Very nice. Now, I wonder how much
overhead is there to provide a wrapper ValueType instead
of simply using the actual type? Or is it negligible?

I would have used the System.Collections classes but it
doesn''t have everything for every situation. For instance,
why is LinkedList nowhere to be found? Hmmm?? Very strange.


Kami no Itte ga ore ni zettai naru!
神はサイコロを振らない!

This topic is closed to new replies.

Advertisement