how to make a simple class that is either an int or a float

Discussion in 'C++' started by Ben, Jun 12, 2004.

  1. Ben

    Ben Guest

    Hi all,

    I'm not yet good at thinking the right way in c++ so although I could
    solve this problem, I'm not sure if they way I'm thinking of is the best
    way to do it. I need a data type or class or something that can hold
    either an int, or a float, knows which one it is holding, and will allow
    me to do comparisons with instances of it without the code which asks
    for the comparison having to know which one it is. So maybe I could do
    it something like this (haven't compiled it so there are probably little
    errors but hopefully you get the gist):

    class Int_or_float {
    int i;
    float f;
    bool is_int;
    public:
    bool Int_or_float::eek:perator==( Int_or_float &a ) {
    if( this->is_int ) return this->i == a.i;
    else return this->f == a.f;
    }
    }

    I could do it more simply perhaps like this:

    class Int_or_float {
    public:
    int i;
    float f;
    bool is_int;
    }

    bool equal( Int_or_float x, y ) {
    if( x.is_int ) return x.i == y.i;
    else return x.f == y.f;
    }

    I realise neither of these will behave well if you compare one storing
    an int with one storing a float.

    So what are the pros and cons of each method? Is there an even simpler
    method? I hoped I might be able to use a union but I can't think how. I
    suppose if I want to use the STL I have no choice but to use the first
    way?

    Thanks in advance,

    Ben
    Ben, Jun 12, 2004
    #1
    1. Advertising

  2. Ben

    Siemel Naran Guest

    "Ben" <> wrote in message

    > I'm not yet good at thinking the right way in c++ so although I could
    > solve this problem, I'm not sure if they way I'm thinking of is the best
    > way to do it. I need a data type or class or something that can hold
    > either an int, or a float, knows which one it is holding, and will allow
    > me to do comparisons with instances of it without the code which asks
    > for the comparison having to know which one it is. So maybe I could do
    > it something like this (haven't compiled it so there are probably little
    > errors but hopefully you get the gist):
    >
    > class Int_or_float {
    > int i;
    > float f;
    > bool is_int;
    > public:
    > bool Int_or_float::eek:perator==( Int_or_float &a ) {
    > if( this->is_int ) return this->i == a.i;
    > else return this->f == a.f;
    > }
    > }
    >
    > I could do it more simply perhaps like this:
    >
    > class Int_or_float {
    > public:
    > int i;
    > float f;
    > bool is_int;
    > }
    >
    > bool equal( Int_or_float x, y ) {
    > if( x.is_int ) return x.i == y.i;
    > else return x.f == y.f;
    > }
    >
    > I realise neither of these will behave well if you compare one storing
    > an int with one storing a float.
    >
    > So what are the pros and cons of each method? Is there an even simpler
    > method? I hoped I might be able to use a union but I can't think how. I
    > suppose if I want to use the STL I have no choice but to use the first
    > way?
    >
    > Thanks in advance,


    This is actually a complicated problem. The first way is preferred as it
    using private variables and encapsulation. But instead of a flag indicating
    the type of an object, one should prefer to use virtual functions. The
    standard solution is something like:

    class Variable {
    public:
    virtual ~Variable() = 0;
    virtual std::auto_ptr<Variable> clone() const = 0;
    };

    class Int : public Variable {
    public:
    Int(int data);
    std::auto_ptr<Variable> clone() const;
    private:
    int d_data;
    };

    To handle comparing arbitrary types, one can use double dispatch. This is
    when a (non-member usually) function is virtual in both its arguments. C++
    does not provide native support for this, but you can build it yourself.
    Look it up in the books or the internet. Here are the basics:

    First, provide 4 operator== functinos, to compare Int to Int, Int to Double,
    etc.

    Basically you create a 2 by 2 matrix, or map, or whatever.

    m["Int"]["Int"] maps to the function compare an Int to Int.
    m["Int"]["Double"] maps to the function compare an Int to Double.

    When the user calls operator==(variable1, variable2), look up the
    appropriate function to call in the matrix map, then call it. If no
    function found you throw an exception, or choose your favorite error
    handling routine.

    The advantage of this approach is that you can add new types, and all you
    need is to derive a new class and provide the new equal functions, and store
    these in the matrix.

    But in your case you might not need double dispatch. Instead you could
    provide a function asFloat() in the base variable class.

    class Variable {
    public:
    virtual ~Variable() = 0;
    virtual std::auto_ptr<Variable> clone() const = 0;
    virtual double asDouble() const = 0;
    };

    inline
    bool operator==(const Variable& lhs, const Variable&) {
    return lhs.asDouble() == rhs.asDouble();
    }
    Siemel Naran, Jun 12, 2004
    #2
    1. Advertising

  3. In article <1bKyc.53520$>,
    Siemel Naran <> wrote:

    >To handle comparing arbitrary types, one can use double dispatch. This is
    >when a (non-member usually) function is virtual in both its arguments. C++
    >does not provide native support for this, but you can build it yourself.
    >Look it up in the books or the internet. Here are the basics:
    >
    >First, provide 4 operator== functinos, to compare Int to Int, Int to Double,
    >etc.
    >
    >Basically you create a 2 by 2 matrix, or map, or whatever.
    >
    >m["Int"]["Int"] maps to the function compare an Int to Int.
    >m["Int"]["Double"] maps to the function compare an Int to Double.
    >
    >When the user calls operator==(variable1, variable2), look up the
    >appropriate function to call in the matrix map, then call it. If no
    >function found you throw an exception, or choose your favorite error
    >handling routine.
    >
    >The advantage of this approach is that you can add new types, and all you
    >need is to derive a new class and provide the new equal functions, and store
    >these in the matrix.


    There's an easier way to do double dispatch (especially for small sets
    of possible types) that avoids having to explicitly determine the type
    of objects by having a general method invoked on one class invoke a more
    specific method in the other:

    --------
    class Value
    {
    public:
    /*Return greater than, less than, or equal to 0 depending on whether
    other's value is greater than, less than, or equal to ours
    */
    virtual int compare(const Value& other) const =0;
    protected:
    virtual int compareWithInt(int other) const =0;
    virtual int compareWithDouble(double other) const =0;
    };

    class IntValue
    {
    private:
    int myInt;
    public:
    IntValue(int i):myInt(i) {}
    virtual int compare(const Value& other) const
    {return -other.compareWithInt(myInt);}
    protected:
    virtual int compareWithInt(int other) const
    {return other<myInt?-1:(myInt==other?0:1);}
    virtual int compareWithDouble(double other) const
    {return other<myInt?-1:(myInt==other?0:1);}
    };

    class DoubleValue
    {
    private:
    double myDouble;
    public:
    DoubleValue(int i):myDouble(i) {}
    virtual int compare(const Value& other) const
    {return -other.compareWithDouble(myDouble);}
    protected:
    virtual int compareWithInt(int other) const
    {return other<myDouble?-1:(myDouble==other?0:1);}
    virtual int compareWithDouble(double other) const
    {return other<myDouble?-1:(myDouble==other?0:1);}
    };

    --------

    (But for your specific problem, the idea upthread of just implementing
    asDouble and calling that in the comparison is probably better than
    using double dispatch.)


    dave

    --
    Dave Vandervies
    Disagreeable intelligent folk make my day.
    Of course, I'm biased.
    --Brenda
    Dave Vandervies, Jun 12, 2004
    #3
  4. Ben wrote:
    >
    > Hi all,
    >
    > I'm not yet good at thinking the right way in c++ so although I could
    > solve this problem, I'm not sure if they way I'm thinking of is the best
    > way to do it. I need a data type or class or something that can hold
    > either an int, or a float, knows which one it is holding, and will allow
    > me to do comparisons with instances of it without the code which asks
    > for the comparison having to know which one it is. So maybe I could do
    > it something like this (haven't compiled it so there are probably little
    > errors but hopefully you get the gist):
    >
    > class Int_or_float {
    > int i;
    > float f;
    > bool is_int;
    > public:
    > bool Int_or_float::eek:perator==( Int_or_float &a ) {
    > if( this->is_int ) return this->i == a.i;
    > else return this->f == a.f;
    > }
    > }
    >
    > I could do it more simply perhaps like this:
    >
    > class Int_or_float {
    > public:
    > int i;
    > float f;
    > bool is_int;
    > }
    >
    > bool equal( Int_or_float x, y ) {
    > if( x.is_int ) return x.i == y.i;
    > else return x.f == y.f;
    > }
    >
    > I realise neither of these will behave well if you compare one storing
    > an int with one storing a float.
    >
    > So what are the pros and cons of each method? Is there an even simpler
    > method? I hoped I might be able to use a union but I can't think how. I
    > suppose if I want to use the STL I have no choice but to use the first
    > way?
    >


    If you want a minimalistic class and lightweight objects,
    here is one sketch:

    class Value {
    enum Type {
    type_int,
    type_flt
    };

    union {
    int v_int_;
    float v_flt_;
    };
    int type_;

    bool cmp_equal_type(Value const& rhs) const {
    bool ret;
    switch(type_) {
    case type_int:
    ret = v_int_ == rhs.v_int_;
    break;
    case type_flt:
    ret = v_flt_ == rhs.v_flt_;
    break;
    default:
    ret = false;//assert() or remove the option
    }
    return ret;
    }

    typedef double promote_t; //system-dependent

    promote_t get_promoted() const {
    promote_t ret;
    switch(type_) {
    case type_int:
    ret = v_int_;
    break;
    case type_flt:
    ret = v_flt_;
    break;
    default:
    ret = 0;//assert() or remove the option
    }
    return ret;
    }

    public:
    Value(int v) : v_int_(v), type_(type_int) {}
    Value(float v) : v_flt_(v), type_(type_flt) {}

    bool operator ==(Value const& rhs) const {
    return rhs.type_ == type_? cmp_equal_type(rhs) :
    (get_promoted() == rhs.get_promoted());
    }

    //...
    };


    Denis
    Denis Remezov, Jun 13, 2004
    #4
  5. Ben

    JKop Guest

    Denis Remezov posted:

    > class Value {
    > enum Type {
    > type_int,
    > type_flt
    > };
    >
    > union {
    > int v_int_;
    > float v_flt_;
    > };



    What's the story with that? You haven't declared any variables/objects.

    -JKop
    JKop, Jun 13, 2004
    #5
  6. Ben

    Ben Guest

    Thanks very much for your suggestions folks...

    I can just about get my head round the second suggestion. The first
    suggestions is making me realise I have plenty more to learn - so I'll try!

    Cheers,

    Ben
    Ben, Jun 13, 2004
    #6
  7. "JKop" <> wrote in message
    news:q7Vyc.2114$...
    > Denis Remezov posted:
    >
    > > class Value {
    > > enum Type {
    > > type_int,
    > > type_flt
    > > };
    > >
    > > union {
    > > int v_int_;
    > > float v_flt_;
    > > };

    >
    >
    > What's the story with that? You haven't declared any variables/objects.
    >
    > -JKop


    It's called an anonymous union, v_int_ and v_flt_ are member variables of
    class Value.

    john
    John Harrison, Jun 13, 2004
    #7
  8. Ben

    Siemel Naran Guest

    "Dave Vandervies" <> wrote in message
    news:cafu2h$nr6
    > Siemel Naran <> wrote:


    > >To handle comparing arbitrary types, one can use double dispatch. This

    is
    > >when a (non-member usually) function is virtual in both its arguments.

    C++
    > >does not provide native support for this, but you can build it yourself.
    > >Look it up in the books or the internet. Here are the basics:
    > >
    > >First, provide 4 operator== functinos, to compare Int to Int, Int to

    Double,
    > >etc.
    > >
    > >Basically you create a 2 by 2 matrix, or map, or whatever.
    > >
    > >m["Int"]["Int"] maps to the function compare an Int to Int.
    > >m["Int"]["Double"] maps to the function compare an Int to Double.
    > >
    > >When the user calls operator==(variable1, variable2), look up the
    > >appropriate function to call in the matrix map, then call it. If no
    > >function found you throw an exception, or choose your favorite error
    > >handling routine.
    > >
    > >The advantage of this approach is that you can add new types, and all you
    > >need is to derive a new class and provide the new equal functions, and

    store
    > >these in the matrix.

    >
    > There's an easier way to do double dispatch (especially for small sets
    > of possible types) that avoids having to explicitly determine the type
    > of objects by having a general method invoked on one class invoke a more
    > specific method in the other:
    >
    > --------
    > class Value
    > {
    > public:
    > /*Return greater than, less than, or equal to 0 depending on whether
    > other's value is greater than, less than, or equal to ours
    > */
    > virtual int compare(const Value& other) const =0;
    > protected:
    > virtual int compareWithInt(int other) const =0;
    > virtual int compareWithDouble(double other) const =0;
    > };


    But this approach still suffers the disadvantage of not being as extensible.
    Suppose you want to add a new type like Complex or String. With my proposed
    code you just add your classes, new comparison functions, register these in
    the double dispatch registry. With your approach we have to change the base
    class to add new functions compareWithComplex and compareWithString.

    > (But for your specific problem, the idea upthread of just implementing
    > asDouble and calling that in the comparison is probably better than
    > using double dispatch.)


    Right, in certain special cases we don't need the full blown double
    dispatch.

    Though who knows. There's a case for a special function here. Maybe to
    compare two doubles we want to compare for approximatley equal, because two
    doubles calculated through different formula may be slightly different
    thanks to imperfect floating point math, example (1.0) and (1.0/3)*3.0 are
    really both 1 but may be 1.0 and 0.9999* because of floating point math.
    Siemel Naran, Jun 14, 2004
    #8
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Schnoffos
    Replies:
    2
    Views:
    1,199
    Martien Verbruggen
    Jun 27, 2003
  2. bd
    Replies:
    0
    Views:
    608
  3. Nick Coghlan
    Replies:
    0
    Views:
    470
    Nick Coghlan
    Dec 6, 2004
  4. k3n3dy
    Replies:
    15
    Views:
    977
    dan2online
    Apr 20, 2006
  5. Carsten Fuchs
    Replies:
    45
    Views:
    1,509
    James Kanze
    Oct 8, 2009
Loading...

Share This Page