7 Replies - 13398 Views - Last Post: 25 November 2009 - 07:37 AM Rate Topic: -----

#1 xenoslash   User is offline

  • D.I.C Head

Reputation: 3
  • View blog
  • Posts: 89
  • Joined: 19-August 09

Determining the base class from a derived class

Post icon  Posted 22 November 2009 - 08:59 PM

Here is the outline of the program I made
base class= Shape
derived class from class Shape= TwoDimensionalShape, ThreeDimensionalShape
derived from TwoDimensionalShape=Square, Rectangle
from Three= Cube, Cuboid

Here is my code

shape.h

#ifndef SHAPE_H #define SHAPE_H  class Shape { public: 	virtual double getArea()=0; 	virtual void print()=0;  private:  };  #endif


TwoDimensionalShape has no member function added

#ifndef TWODIMENSIONALSHAPE_H #define TWODIMENSIONALSHAPE_H  #include "Shape.h"  class TwoDimensionalShape: public Shape { public:  private:  };  #endif


ThreeDimensionalShape only has an extra member function, getVolume()


#ifndef THREEDIMENSIONALSHAPE_H #define THREEDIMENSIONALSHAPE_H  #include "Shape.h"  class ThreeDimensionalShape :public Shape { public: 	virtual double getVolume()=0;  private:  };  #endif


test program

#include "Square.h" #include "Rectangle.h" #include "Cube.h" #include "Cuboid.h" #include "Shape.h"  #include <cstring> #include <vector> #include <typeinfo> #include <iostream> using namespace std;   int main() {  vector <Shape *> shapePtr (4);  shapePtr[0]=new Square(5); shapePtr[1]=new Rectangle(5,4); shapePtr[2]=new Cube(5); shapePtr[3]=new Cuboid(1,2,3);  for (size_t i=0; i<shapePtr.size(); i++) 	{ 	if (strcmp(typeid(*shapePtr[i]).name(),"class TwoDimensionalObject")==0) 				{ 		cout<<typeid(*shapePtr[i].name())<<endl; 		cout<<shapePtr[i]->getArea()<<endl; 				} 	else if (strcmp(typeid(*shapePtr[i]).name(),"class ThreeDimensionalObject")==0) 				{ 		cout<<typeid(*shapePtr[i].name())<<endl; 		cout<<shapePtr[i]->getVolume()<<endl; 				} 	}  return 0; }

How am I supposed to determine if lets say square is derived from TwoDimensionalObject??
and also, since I didn't provide the member function getVolume() for class Shape , this code won't work
cout<<shapePtr[i]->getVolume()<<endl;


Ugh darn, why can't I use the code tag?? I just copy pasted my whole code in it, but all the newlines are gone!

This post has been edited by NickDMax: 25 November 2009 - 07:54 AM


Is This A Good Question/Topic? 0
  • +

Replies To: Determining the base class from a derived class

#2 athlon32   User is offline

  • D.I.C Regular
  • member icon

Reputation: 117
  • View blog
  • Posts: 363
  • Joined: 20-August 08

Re: Determining the base class from a derived class

Posted 22 November 2009 - 09:16 PM

Well, remember that inheritance follows an 'IS A' model. So if you wanna know if something 'IS A' something else, one quick and dirty way to check is to cast something into something else, and check it with typeid() if is a something else. for example:

#include <iostream>
#include <typeinfo>

class Base {};

class Derived : public Base {};

class nonRelatedClass {};

int main(void)
{
	Base b;
	Derived d;
	nonRelatedClass n;
	if(typeid((Base)d) == typeid(b))
		std::cout << "Derived is a Base";
	// this won't work since d is not a 'nonRelatedClass'
	if(typeid((nonRelatedClass)d == typeid(n)))
		std::cout << "Derived is a nonRelatedClass";
	std::cin.get();
}


Like i said, quick and dirty :) Hope I helped :D
Was This Post Helpful? 0
  • +
  • -

#3 xenoslash   User is offline

  • D.I.C Head

Reputation: 3
  • View blog
  • Posts: 89
  • Joined: 19-August 09

Re: Determining the base class from a derived class

Posted 22 November 2009 - 09:26 PM

I got it!!! woot2!!
Thanks, but just a question here

 if(typeid((nonRelatedClass)d == typeid(n)))

do you mean " typeid((nonRelatedClass)d) == ..."
I think you miss a )

anyways, here is my solution

since I don't know how to use the code tag, I manually typed
 

hope this works
 
#include "Square.h"
#include "Rectangle.h"
#include "Cube.h"
#include "Cuboid.h"
#include "Shape.h"
#include "ThreeDimensionalShape.h"
#include "TwoDimensionalShape.h"

#include <cstring>
#include <vector>
#include <typeinfo>
#include <iostream>
using namespace std;


int main()
{

vector <Shape *> shapePtr (4);

shapePtr[0]=new Square(5);
shapePtr[1]=new Rectangle(5,4);
shapePtr[2]=new Cube(5);
shapePtr[3]=new Cuboid(1,2,3);

for (size_t i=0; i<shapePtr.size(); i++)
	{
	ThreeDimensionalShape *derivedPtr= dynamic_cast<ThreeDimensionalShape *>(shapePtr[i]);

	if (derivedPtr==0)
		{
		cout<<typeid(*shapePtr[i]).name()<<endl;
		cout<<shapePtr[i]->getArea()<<endl;
		}

	else 
		{
		
		cout<<typeid(*derivedPtr).name()<<endl;
		cout<<derivedPtr->getVolume()<<endl;
		}
	}

return 0;
}




I actually thought that the code won't compile, but it worked!
Hmm, since this is the opposite of downcasting, shouldn't it be called "upcasting"??
And, what does typeid() do actually?
Was This Post Helpful? 0
  • +
  • -

#4 athlon32   User is offline

  • D.I.C Regular
  • member icon

Reputation: 117
  • View blog
  • Posts: 363
  • Joined: 20-August 08

Re: Determining the base class from a derived class

Posted 22 November 2009 - 09:34 PM

I wouldn't call it upcasting. Basically, it just follows that 'IS A' principle. Since Derived is a Base, you can make a Base out of it :)

typeid() is an operator (not function, common misconception ;)), that takes an expression or type and returns a reference to a type_info object. You can compare these objects to one another to do all kinds of things ^^

Hope I helped :)
Was This Post Helpful? 1
  • +
  • -

#5 Bench   User is offline

  • D.I.C Lover
  • member icon

Reputation: 945
  • View blog
  • Posts: 2,464
  • Joined: 20-August 07

Re: Determining the base class from a derived class

Posted 23 November 2009 - 08:10 AM

Note that RTTI (Runtime type identification), which includes type identification via dynamic_cast and the typeid operator is not always enabled by default on all compilers (This seems to be mostly true with compilers which don't yet support C++03).

For those compilers, and for backwards compatibility, there are other techniques available for finding whether an object is convertible to a base class type, including this metaprogramming trick whereby the compiler will determine whether an object is convertible to to some class/type at compile time
template<typename BaseT, typename DerivedT>
inline bool isRelated(const DerivedT&)
{
    DerivedT derived();
    char test(const BaseT&); // sizeof(test()) == sizeof(char)
    char (&test(...))[2];    // sizeof(test()) == sizeof(char[2])
    struct conversion 
    { 
        enum { exists = (sizeof(test(derived())) == sizeof(char)) }; 
    };
    return conversion::exists;
} 
This trick works by comparing the sizeof the return type of the overloaded test() function signature (sizeof works at compile time so actual function definitions are not needed) with sizeof(char).
The function signature called derived() can be passed to test() at compile time to match one of the two overloaded functions - again, derived() doesn't need a function body since it will never actually be called.
A positive type match will examine test(const BaseT&), and a negative match will examine test(...), since the ellipsis will act as a catch-all net for anything which doesn't convert to BaseT (And its signature returns something bigger than char - in fact it returns char[2], which obviously must be bigger)


Here's an example using isRelated()
#include <iostream>

class base {};
class derived : public base {};
class unrelated {};

int main()
{
    base b;
    derived d;
    unrelated u;

    if( isRelated<base>( b ) )
        std::cout << "b is related to base" << std::endl;

    if( isRelated<base>( d ) )
        std::cout << "d is related to base" << std::endl;

    if( !isRelated<base>( u ) )
        std::cout << "u is not related to base" << std::endl;
} 

As an added bonus, this is actually more efficient than RTTI (e.g dynamic_cast or typeid), since type determination is happening at compile time, although it doesn't work the other way around, since the result of downcasting a base class pointer can only be known at run time.

This post has been edited by Bench: 23 November 2009 - 08:42 AM

Was This Post Helpful? 0
  • +
  • -

#6 EdwinNameless   User is offline

  • D.I.C Addict
  • member icon

Reputation: 128
  • View blog
  • Posts: 723
  • Joined: 15-October 09

Re: Determining the base class from a derived class

Posted 23 November 2009 - 10:53 AM

View PostBench, on 23 Nov, 2009 - 07:10 AM, said:

For those compilers, and for backwards compatibility, there are other techniques available for finding whether an object is convertible to a base class type, including this metaprogramming trick whereby the compiler will determine whether an object is convertible to to some class/type at compile time



Ooooooh, fancy. :o

I don't get the role of struct conversion. What's its use here? Wouldn't:

template<typename BaseT, typename DerivedT>
inline bool isRelated(const DerivedT&)
{
	DerivedT derived();
	char test(const BaseT&); // sizeof(test()) == sizeof(char)
	char (&test(...))[2];	// sizeof(test()) == sizeof(char[2])
	return (sizeof(test(derived())) == sizeof(char));
}



do the same?
Was This Post Helpful? 0
  • +
  • -

#8 Bench   User is offline

  • D.I.C Lover
  • member icon

Reputation: 945
  • View blog
  • Posts: 2,464
  • Joined: 20-August 07

Re: Determining the base class from a derived class

Posted 23 November 2009 - 12:22 PM

View PostEdwinNameless, on 23 Nov, 2009 - 05:53 PM, said:

I don't get the role of struct conversion. What's its use here?
You're absolutely right that the function works just fine without the struct; I did purposely overcomplicate it a little bit to show that there's potential to use the outcome of the compile-time conversion check as a compile-time constant.

In this example the only advantage of struct conversion is forcing a tiny bit of extra efficiency. An expression inside an enum is always calculated at compile time, so conversion::exists becomes a 0 or 1 compile time constant, instead of an expression which the compiler may or may not optimise out (Of course that depends on how bright the compiler is). And most likely it'd make no difference to the speed of the final code anyway.


But in more general terms there is a much better reason for doing this with a struct without the function at all (Drifting off topic here..), having a compile time constant is great for more advanced metaprogramming code - for example, getting rid of run time if statements and replacing them with so-called compile-time-"if" statements, which rely on compile time constants such as enums to work.

if this check were not wrapped in a function, and conversion stood alone as a template class, the value of exists could be used as another true/false template argument in order to optimise out some if/else statement to be calculated at compile time instead of at runtime

Here's a standalone version of conversion
template<typename BaseT, typename DerivedT>
class Conversion 
{ 
    static DerivedT derived();
    static char test(const BaseT&);
    static char (&test(...))[2];
public:
    enum { exists = (sizeof(test(derived())) == sizeof(char)) }; 
}; 

and here's a compile-time 'if' which uses Conversion::exists
#include <iostream>
#include <string>

template<bool> std::string relation()  //if <true>
{ 
    return "Types are related"; 
}
template<> std::string relation<false>() //if <false>
{
    return "Types are not related"; 
}

template<typename BaseT,typename DerivedT>
void say_relation(const BaseT&, const DerivedT&)
{
    typedef Conversion<BaseT,DerivedT> ConversionT;

    // pick relation<true>() or relation<false>()
    // - dependent on value of Conversion::exists.
    std::cout << relation<ConversionT::exists>() << std::endl;
}
int main()
{
    class base {} b;
    class derived : public base {} d;
    class unrelated {} u;

    say_relation( b, b );
    say_relation( b, d );
    say_relation( b, u );
} 
having two specialised classes or functions made distinct with template<bool> is a good way to force the compiler to pick from two options at compile time without needing to write if statements - sometimes doing this can really clean up code - such as the calls to say_relation in main() above.


In fact, if you wanted to get really clever, you could allow for the possibility that the arguments to say_relation may not always be (base, derived); yet still yield the correct result if (derived, base) were passed in reverse order instead.
template<typename BaseT,typename DerivedT>
void say_relation(const BaseT&, const DerivedT&)
{
    typedef Conversion<BaseT,DerivedT> ConvT;
    typedef Conversion<DerivedT,BaseT> InverseT;
    std::cout << relation<ConvT::exists?true:InverseT::exists>() << std::endl;
} 
int main()
{
    class base {} b;
    class derived : public base {} d;
    class unrelated {} u;

    say_relation( b, b );
    say_relation( b, d );
    say_relation( b, u );

    say_relation( b, b );  // base object is 2nd argument
    say_relation( d, b );  // - but 'say_relation()' still
    say_relation( u, b );  //   recognises the relationship
}  
Which is a nice way to make a more idiot-proof interface to say_relation, although I think this has now gone way above and beyond the original purpose of the thread already.

This post has been edited by Bench: 23 November 2009 - 12:38 PM

Was This Post Helpful? 1
  • +
  • -

#9 swaan79   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 25-November 09

Re: Determining the base class from a derived class

Posted 25 November 2009 - 07:37 AM

This is pretty cool stuff. I did however make a tiny alteration to make it work with abstract base classes.

template<typename BaseT, typename DerivedT>
class Conversion 
{ 
	static DerivedT& derived();	// Note the &
	static char test(const BaseT&);
	static char (&test(...))[2];
public:
	enum { exists = (sizeof(test(derived())) == sizeof(char)) }; 
};


Mark
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1