Thread: Typesystem

  1. #1
    Registered User
    Join Date
    Nov 2006
    Posts
    519

    Typesystem

    Hi,

    what I always wondered about: Why does 'typedef' not create ... a type?

    Code:
    int main()
    {
    typedef int T1;
    typedef int T2;
    
    T1 t1;
    T2 t2;
    
    t1 == t2;
    }
    The compiler acts as if I had just compared int. no error, no warning at highest warning level, although it would be really easy to do and help to avoid a whole class of programming errors. A try to pass mPolygon.getArea_squaremeters() to setPriceOfIceCream_dollars() could be found at compile time and programmers could get rid of putting units on symbols.

    Is this maybe going to be fixed by new standard?

  2. #2
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by pheres
    Why does 'typedef' not create ... a type?
    Because it creates an alias for a type.

    Quote Originally Posted by pheres
    The compiler acts as if I had just compared int. no error, no warning at highest warning level, although it would be really easy to do and help to avoid a whole class of programming errors.
    The problem is that sometimes you really do want the typedef to create an alias of the type, and you can already create new types with say, the class and struct keywords.

    Quote Originally Posted by pheres
    Is this maybe going to be fixed by new standard?
    I do not think so.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  3. #3
    Registered User
    Join Date
    Nov 2006
    Posts
    519
    Code:
    struct AreaT :public DoubleOperators
    {
     double mArea;
    }
    would do the job. The operators could probably derived from a base class which forwards them to the operators for the build in types.

    But the Question is: Would the resulting compiled code be slower than code which uses plain double?

  4. #4
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by pheres
    would do the job. The operators could probably derived from a base class which forwards them to the operators for the build in types.
    I suppose that if you wanted to be funny/paranoid, you could do something like this:
    Code:
    template<typename T, typename Tag>
    class Number;
    
    template<typename T, typename Tag>
    bool operator==(const Number<T, Tag>& lhs, const Number<T, Tag>& rhs);
    
    template<typename T, typename Tag>
    class Number
    {
    public:
        Number(T n = T()) : n(n) {}
    
        Number& operator+=(const Number& other)
        {
            n += other.n;
            return *this;
        }
    
        friend bool operator== <>(const Number<T, Tag>& lhs, const Number<T, Tag>& rhs);
    
        // various other operators
        // ...
    private:
        T n;
    };
    
    // ...
    
    template<typename T, typename Tag>
    inline bool operator==(const Number<T, Tag>& lhs, const Number<T, Tag>& rhs)
    {
        return lhs.n == rhs.n;
    }
    
    struct AreaTypeTag {};
    typedef Number<double, AreaTypeTag> AreaType;
    
    struct PriceTypeTag {};
    typedef Number<double, PriceTypeTag> PriceType;
    
    int main()
    {
        AreaType area;
        PriceType price;
        if (area == area)
        {
            // okay
        }
    
        if (area == price)
        {
            // compile time error
        }
    
        area += area; // okay
    
        area += price; // compile time error
    }
    Quote Originally Posted by pheres
    But the Question is: Would the resulting compiled code be slower than code which uses plain double?
    A quick assembly output comparison with my version shows that it could well be a no - the assembly output was exactly the same (once I removed the error causing code) as if I had directly typedef'ed int to AreaType and PriceType.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  5. #5
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    That would defeat one very important purpose of typedefs, which is to create an easier-to-use name for some other complicated type.

    Suppose I had a map which takes pairs of vectors of integers to a set of integers:

    Code:
    std::map< std::pair< std::vector<int>, std::vector<int> >, std::set<int> >
    Suppose I'm constantly iterating over this map. It really sucks to constantly have to type out:

    Code:
    for( std::map< std::pair< std::vector<int>, std::vector<int> >, std::set<int> >::iterator i...
    I'd really rather write:

    Code:
    for( MyMap::iterator i...
    I don't want to change the identity of the type, I just want a better name for it.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  6. #6
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by laserlight View Post
    I suppose that if you wanted to be funny/paranoid, you could do something like this:
    I don't think that's funny or paranoid. That kicks ass. I'd call it "Unit" instead of "Tag" though.

    You could extend the idea further, and allow multiplication of differing units, which would synthesize a new unit which is a product of the individual units.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  7. #7
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    Quote Originally Posted by pheres View Post
    what I always wondered about: Why does 'typedef' not create ... a type?
    It comes from the less typesafe roots of C I think.
    You can do it in D though.

    Your derivation example may be the same speed, or it may be marginally slower. It's hard to know without comparing the assembly of equivalent examples. I would hope at least that the compiler does EBO.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  8. #8
    Registered User
    Join Date
    Nov 2006
    Posts
    519
    Thanks, particularly the template example is interesting. Think I really like it, also the idea about forming new units with operators. It's not just paranoid. If you compute a lot of geometry and physics stuff with lot of people / 3rd party libraries involved, units become a real problem. The IDE is clever enough to show you signatures, comments, return types, call graphs and all that while browsing/editing code. But if someone forgot to document some unit you can lose a lot of time figuring it out or worse, just assume the wrong thing. Of course things can get lot more complex than the ice cream example. A thaler for every time I searched a degree vs. radian kind of run time error and I would be a rich man And these errors are hard to find some times. You see a picture showing some result of complex calculations which is somehow...unexpected. It could be everything.

    What really would kick ass would be to teach the compiler to automatically convert units (e.g. meters to kilometers) while passing unit afflicted parameters.

    btw: What does EBO stand for? None of googles proposals matches context of compiler optimization.

  9. #9
    The larch
    Join Date
    May 2006
    Posts
    3,573
    What really would kick ass would be to teach the compiler to automatically convert units (e.g. meters to kilometers) while passing unit afflicted parameters.
    I wouldn't expect the compiler to know about all the units out there, but with a special library I don't see why such implicit conversions wouldn't be possible:

    Code:
    #include <iostream>
    
    class Meter;
    
    class KiloMeter
    {
        double len;
    public:
        KiloMeter(double len): len(len) {}
        KiloMeter(const Meter& meter);
        double length() const { return len; }
    };
    
    std::ostream& operator<<(std::ostream& os, const KiloMeter& km)
    {
        return os << km.length() << " km";
    }
    
    class Meter
    {
        double len;
    public:
        Meter(double len): len(len) {}
        Meter(const KiloMeter& km): len(km.length() * 1000) {}
        double length() const { return len; }
    };
    
    std::ostream& operator<<(std::ostream& os, const Meter& m)
    {
        return os << m.length() << " m";
    }
    
    void PrintKm(const KiloMeter& km) {
        std::cout << km << '\n';
    }
    
    void PrintM(const Meter& m) {
        std::cout << m << '\n';
    }
    
    int main()
    {
        KiloMeter kms(1.4);
        Meter ms(4572);
        PrintKm(kms);
        PrintKm(ms);
        PrintM(kms);
        PrintM(ms);
    }
    
    KiloMeter::KiloMeter(const Meter& meter):
        len(meter.length() / 1000)
    {}
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  10. #10
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    Quote Originally Posted by pheres View Post
    Code:
    struct AreaT :public DoubleOperators
    {
     double mArea;
    }
    would do the job. The operators could probably derived from a base class which forwards them to the operators for the build in types.
    The problem with this is that you still cannot use math library functions on your AreaT.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  11. #11
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    Quote Originally Posted by laserlight View Post
    I suppose that if you wanted to be funny/paranoid, you could do something like this:
    Code:
    template<typename T, typename Tag>
    class Number;
    
    template<typename T, typename Tag>
    bool operator==(const Number<T, Tag>& lhs, const Number<T, Tag>& rhs);
    
    template<typename T, typename Tag>
    class Number
    {
    public:
        Number(T n = T()) : n(n) {}
    
        Number& operator+=(const Number& other)
        {
            n += other.n;
            return *this;
        }
    
        friend bool operator== <>(const Number<T, Tag>& lhs, const Number<T, Tag>& rhs);
    
        // various other operators
        // ...
    private:
        T n;
    };
    
    // ...
    
    template<typename T, typename Tag>
    inline bool operator==(const Number<T, Tag>& lhs, const Number<T, Tag>& rhs)
    {
        return lhs.n == rhs.n;
    }
    
    struct AreaTypeTag {};
    typedef Number<double, AreaTypeTag> AreaType;
    
    struct PriceTypeTag {};
    typedef Number<double, PriceTypeTag> PriceType;
    
    int main()
    {
        AreaType area;
        PriceType price;
        if (area == area)
        {
            // okay
        }
    
        if (area == price)
        {
            // compile time error
        }
    
        area += area; // okay
    
        area += price; // compile time error
    }
    This works for addition, subtraction, and comparison, but not for multiplication, division, or other math functions. There is a unit change for those operations, so price/area makes sence, but the result is neither a price, nor an area.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  12. #12
    Registered User
    Join Date
    Nov 2006
    Posts
    519
    Quote Originally Posted by King Mir View Post
    The problem with this is that you still cannot use math library functions on your AreaT.
    yes.

    what about

    Code:
    struct AreaT :public DoubleOperators
    {
     double mArea;
      double& get(){ return mArea; }
    }
    
    sqrt( AreaT(2.0).get() );

  13. #13
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    Quote Originally Posted by pheres View Post
    yes.

    what about

    Code:
    struct AreaT :public DoubleOperators
    {
     double mArea;
      double& get(){ return mArea; }
    }
    
    sqrt( AreaT(2.0).get() );
    Code like that is used sometimes.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  14. #14
    The larch
    Join Date
    May 2006
    Posts
    3,573
    But if you have to do that, wouldn't you lose all the safety that you are after?

    Code:
    Radian r;
    Degree d;
    sqrt(r.get() * d.get());
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  15. #15
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by King Mir
    This works for addition, subtraction, and comparison, but not for multiplication, division, or other math functions. There is a unit change for those operations, so price/area makes sence, but the result is neither a price, nor an area.
    I suppose the easy way out is to break the abstraction slightly with a get() member function like what pheres proposed (except that it should return by value only, in my opinion), and trust the user to do the right thing, e.g., overload operator/ to take a PriceType and AreaType and return a PricePerAreaType.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

Popular pages Recent additions subscribe to a feed