PDA

View Full Version : Why can't I make dynamic_cast work properly?



pir
30th May 2006, 23:20
Hi!

I'm having problem with using RTTI when compiling with gcc 4.0.2.
Is there any flags I should use when compiling? (tried -frtti)
Read somewhere that I should include <typeinfo> in the headers... but in which headers? All headers
in the project?

I thought that it was turned in as default. But when I use dynamic_cast it lets me cast to wathever...

take this for example:



class Thing ( base class, polymorphic )

class Contour class Polygon ( subclasses of Thing )





Thing *thing = new Contour ( );

/* This should work */
Contour *con = dynamic_cast<Contour*) ( thing );
if ( con == NULL )
std::cout<<"con==NULL"<<std::endl;

/* this should return a NULL pointer */
Polygon *pol = dynamic_cast<Polygon*) ( thing );
if ( pol == NULL )
std::cout<<"pol==NULL"<<std::endl;


No of the two returned casts are NULL. From what I understand, the second cast should be NULL since I try to cast a Contour to a Polygon.

This kind of things just makes me sad...
pir

e8johan
31st May 2006, 07:21
It is probably something in you class declarations, could you post the headers please.

Beluvius
31st May 2006, 09:14
Hi,

As far as I can see from you message, the structure is:

class Thing;

class Polygon: public Thing;

class Contour: public Polygon;

The dynamic_cast of a thing to a Polygon is valid AND the dynamic_cast of a thing to a Contour is valid if the thing is originally an instance of a Contour. Simply put: a Contour can be seen as a Contour and as a Polygon. So your code is correct, and it is possible that both dynamic_casts result in a non-NULL pointer.

Hope this helps,
Beluvius

pir
31st May 2006, 09:26
Hi!

Here are the headers. There seems like I did a poor job explaining the class tree. So here is the actual tree. The class I called Polygon is actually called PolygonMesh and Contour is called ContourMesh.
There are two classes in between called Geometrical and Renderable.

Thing <-- Geometrical <-- Renderable <-- ContourMesh

Thing <-- Geometrical <-- Renderable <-- PolygonMesh

The headers of Thing, ContourMesh ( previously called Contour ) and PolygonMesh ( previously called Polygon ) is posted bellow. Since I don't know what you're looing for I've tried to remove as little as possible.

pir

Base class Thing:



#ifndef THING_HEADERFILE041205
#define THING_HEADERFILE041205

#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <vector>

namespace thing {

class Thing {

public:

Thing ( std::string sName=Thing::sm_sNAME );

virtual Thing *copy ( ) const = 0;

virtual ~Thing ( );

bool operator== ( const Thing& thing ) const;

bool static equal ( const Thing &thing1, const Thing &thing2 );

friend std::ostream& operator<< ( std::ostream& o,const Thing& thing );

virtual Thing* pickout_component ( int id );

virtual Thing* get_component ( int id );

virtual bool insert_component ( Thing *thing );

virtual bool insert_component_at ( Thing *thing, unsigned int pos );

virtual bool remove_component_at ( unsigned int pos );

void select ( );

void unselect ( );

bool is_selected ( ) const;

bool family_selected ( ) const;

const Thing* get_parent ( ) const;

Thing* get_parent ( );

void set_parent ( Thing* newParent );

virtual int size ( ) const;

virtual const Thing* at ( int nIndex ) const;

virtual Thing* at ( int nIndex );

virtual std::vector<Thing*> get_ThingComponents ( );

private:

static unsigned int sm_nNumCreatedThings;

int m_nId;

std::string m_sName;

bool m_bSaved;

bool m_bSelected;

Thing *m_tParent;

void defaultvalues ( );

static const std::string NAME;

static const bool SAVED;

}; // class Thing

}//namespace thing

#endif // #ifndef THING_HEADERFILE041205



Header of ContourMesh:




#ifndef CONTOURMESH_HEADERFILE041205
#define CONTOURMESH_HEADERFILE041205

#include <vector>
#include <list>
#include "../Renderable/Renderable.h"
#include "./Contour.h"
#include "./contourmeshlib.h"

#include "../FORTRAN__/fortran.h"

#include <GL/glu.h>

namespace contourmesh{

class ContourMesh: public renderable::Renderable{

public:

ContourMesh ( std::string name=sm_sNAME );

ContourMesh ( Contour *first, Contour *last, std::string name=sm_sNAME );

ContourMesh ( const ContourMesh& contmesh );

ContourMesh *copy ( ) const;

~ContourMesh ( );

void delete_contours ( );

const ContourMesh& operator= ( const ContourMesh& contmesh );

friend std::ostream& operator<< ( std::ostream& o, const ContourMesh& cmesh );

bool operator== ( const ContourMesh& cmesh ) const;

static bool equal ( const ContourMesh& pm1, const ContourMesh& pm2 );

Contour* operator[] ( int i );

const Contour *operator[] ( int i ) const;

static std::vector<ContourMesh*> create_meshes(Contour *firstcontour, Contour *lastcontour);

static std::vector<ContourMesh*> divide_mesh(const ContourMesh *cmesh);

void copy_creation_data_to(std::vector<ContourMesh*> &pr_vCD) const;

void insert_contours(Contour *first, Contour *last);

Contour* get_firstcontour ( );

int num_contours ( ) const;

int num_points ( ) const;


bool& selectable ( );

void draw_shape ( GLfloat pr_fWorldScale=1 );

static unsigned int import_into_oneMesh(std::string filename, ContourMesh &cmesh);

static void export_ContourMesh(ContourMesh &cmesh, std::string filename);

virtual bool
cut ( std::vector<Renderable*> *pr_vInside,
std::vector<Renderable*> *pr_vOutside,
bool pr_bOptimizeEdges, bool pr_bThreeDenvelope,
GLfloat **curve, unsigned int ncurve,
GLfloat pr_fWorldRotationX,
GLfloat pr_fWorldRotationY,
GLfloat pr_fWorldRotationZ,
GLfloat pr_fWorldTranslationX,
GLfloat pr_fWorldTranslationY,
GLfloat pr_fWorldTranslationZ,
GLfloat pr_fWorldScaleX,
GLfloat pr_fWorldScaleY,
GLfloat pr_fWorldScaleZ );

void update_boundingbox ( bool bCenterPivots=false );


protected:

std::vector<Contour*> contours;

private:

const static std::string NAME;

static std::string sm_sNAME;

static unsigned int sm_nNumCreatedContourMeshes;

static bool sm_bClassIsSelectable;

void copyContourMesh ( const ContourMesh &cm );

void insert_contour ( Contour &contour );

static void copy_creation_data ( ContourMesh &to,
const ContourMesh &from );

static void check_along ( Contour *tmpcontour, int *along );

static void check_thickness ( ContourMesh &cmesh,
float *new_val, float old, int along );


}; // class ContourMesh


}// namespace contourmesh
#endif // #ifndef CONTOURMESH_HEADERFILE041205


header of PolygonMesh:


#ifndef POLYGONMESH_HEADERFILE021205
#define POLYGONMESH_HEADERFILE021205

#include <stdlib.h>
#include <GL/gl.h>
#include <math.h>
#include <iostream>
#include <list>
#include <vector>
#include <algorithm>

#include "../Renderable/Renderable.h"
#include "./polygonmeshlib.h"


namespace polygonmesh {

class PolygonMesh: public renderable::Renderable {
public:


PolygonMesh ( std::string name = sm_sNAME );

PolygonMesh ( unsigned int *P_arr, GLfloat *V_arr, GLfloat *N_arr,
unsigned int numvert, unsigned int numpol,
std::string name = sm_sNAME );

PolygonMesh ( const PolygonMesh& pmesh );

PolygonMesh *copy ( ) const;

~PolygonMesh ( );


static std::vector<PolygonMesh*> create_meshes ( GLfloat *vertice_array,
GLfloat *normal_array,
unsigned int *polygon_array,
unsigned int numvertices,
unsigned int numpolygons );


static std::vector<PolygonMesh*> divide ( PolygonMesh &bigMesh );

static PolygonMesh* merge ( const std::vector<PolygonMesh*> &meshes );

const PolygonMesh& operator= ( const PolygonMesh& pmesh );

friend std::ostream& operator<< ( std::ostream& o, const PolygonMesh& pmesh );

bool operator== ( const PolygonMesh& pmesh ) const;

static bool equal ( const PolygonMesh& pm1, const PolygonMesh& pm2 );

void draw_shape ( GLfloat pr_fWorldScale=1 );

void copy_creation_data_to ( std::vector<PolygonMesh*> pr_vMD ) const;

GLfloat* get_vertice_array ( );

const GLfloat* get_vertice_array ( ) const;

void set_vertice_array ( GLfloat *newarray );

GLfloat* get_normal_array ( );

const GLfloat* get_normal_array ( ) const;

void set_normal_array ( GLfloat *narr );

bool* get_visible_array ( );

const bool* get_visible_array ( ) const;

void set_visible_array ( bool *varr );

unsigned int* get_polygon_array ( );

const unsigned int* get_polygon_array ( ) const;

void set_polygon_array ( unsigned int *narr );

unsigned int get_number_of_vertices ( ) const;

unsigned int& get_number_of_vertices ( );

unsigned int get_number_of_polygons ( ) const;

unsigned int& get_number_of_polygons ( );

float& get_average ( );

float get_average ( ) const;

float& get_rms ( );

float get_rms ( ) const;

float& get_sigma ( );

float get_sigma ( ) const;

float& get_threshold ( );

float get_threshold ( ) const;

float& get_scale_factor ( );

float get_scale_factor ( ) const;

int& get_sourceid ( );

int get_sourceid ( ) const;

std::string& get_filename ( );

std::string get_filename ( ) const;

bool& selectable ( );

bool selectable ( ) const;

void update_boundingbox ( bool bCenterPivots=false );


protected:

GLfloat *vertice_array;

GLfloat *normal_array;

bool *visible_array;

unsigned int *polygon_array;

unsigned int number_of_vertices;

unsigned int number_of_polygons;


private:

float average;

float rms;

float sigma;

float threshold;

float scale_factor;

int sourceid;

std::string filename;

static bool sm_bClassIsSelectable;

const static std::string NAME;

static std::string sm_sNAME;

static unsigned int sm_nNumCreatedPolygonMeshes;

void copyPolygonMesh ( const PolygonMesh& pmesh );

void zerodefaults ( );

void init_arrays ( int nvertices, int npolygons );

static void copy_creation_data ( PolygonMesh &to, const PolygonMesh &from );

};// class PolygonMesh


}// namespace polygonmesh

#endif //#ifndef POLYGONMESH_HEADERFILE041205

mcosta
31st May 2006, 18:46
Your code seems OK.

Where is the code that fails?

wysota
31st May 2006, 19:00
You might want to try with some simpler code to see if it works at all:


#include <iostream>
class Base {
public:
Base(){}
virtual ~Base(){}
};
class Subclass : public Base {
public:
Subclass():Base(){}
~Subclass(){}
};
class SubSubclass : public Subclass {
public:
SubSubclass():Subclass(){}
~SubSubclass(){}
};
class OtherSubclass : public Base {
public:
OtherSubclass():Base(){}
~OtherSubclass(){}
};

int main(){
Base *o = new SubSubclass();
OtherSubclass *test1 = dynamic_cast<OtherSubclass*>(o);
Subclass *test2 = dynamic_cast<Subclass*>(o);
if(test1) std::cout << "Test 1 failed" << std::endl;
if(!test2) std::cout << "Test 2 failed" << std::endl;
if(!test1 && test2) std::cout << "Dynamic cast ok" << std::endl;
return 0;
}

pir
31st May 2006, 22:59
You're right, its seems like the dynamic_cast is working... when I ran the test program above it printed : Test1 failed. So both casts makes produce a pointer which is not NULL.

Do I need to turn on rtti? If I do, which flag should I use and do I need to compile all code with this flag?
I tried a flag called -frtti ( I think it was called that ).

What should I do now? I have looked at gnus gcc/g++ manual website. But it would be a lie to say that their manuals are the answers to every problem...

pir

wysota
31st May 2006, 23:03
To be honest I just typed "g++ main.cpp -o test" to compile it and it passed both tests.

Here is my "g++ -v":

Using built-in specs.
Target: i586-mandriva-linux-gnu
Configured with: ../configure --prefix=/usr --libexecdir=/usr/lib --with-slibdir=/lib --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --disable-checking --enable-languages=c,c++,ada,f95,objc,java --host=i586-mandriva-linux-gnu --with-system-zlib --enable-long-long --enable-__cxa_atexit --enable-clocale=gnu --disable-libunwind-exceptions --enable-java-awt=gtk --with-java-home=/usr/lib/jvm/java-1.4.2-gcj-1.4.2.0/jre --enable-gtk-cairo --disable-libjava-multilib
Thread model: posix
gcc version 4.0.1 (4.0.1-5mdk for Mandriva Linux release 2006.0)

pir
31st May 2006, 23:57
Ok, my bad.
I used the same Makefile as for my project before. When I tried "g++ -o main.cpp test" it turned out ok. So the compiler is ok.

Here's the flags I use in my Makefile:
CXXFLAGS = -g -Wall -O0

Does those cause any problems?

I have some FORTRAN code in the project as well. I suppose I have to try to take it away. But is there somebody who know if the extern "C" trick turns dynamic_cast into a pile of shit? I use that to make C++ and FORTRAN to comunicate.

pir

wysota
1st June 2006, 01:13
No, it should only affect bindings. *SHOULD* :)

pir
1st June 2006, 10:15
Ok I found the problem... but not solved it.

The dynamic_cast doesn't work because I use FORTRAN libs when linking the library. There is one lib called -lcxa that mess dynamic_cast up. Anyone have any tips? The fortran compiler is a Intel 9.0.

I love FORTRAN, From Out Rectum Taken Rigmarole And Nogood

pir

wysota
1st June 2006, 15:37
Why don't you use gcc to compile your fortran gizmos? AFAIK gcc supports fortran. Maybe it'll work better.

pir
18th July 2006, 10:09
The FORTRAN code is written for the intel ifort compiler. I don't know much about FORTRAN, but there seems like the syntax for ifort FORTRAN is some kind of mix of FORTRAN90 and FORTRAN75 ( or what they are called )... anyway I don't think that it will compile on any other compiler than on ifort.

But fortunatelly typeid() works just fine, so I can use that in combination with static_cast... Will work for the moment. But I suppose I need to fix this.


thanks
pir

pir
18th July 2006, 17:17
If there is someone who cares about this problem... here is the solution.

check out the link:
http://yolinux.com/TUTORIALS/LinuxTutorialMixingFortranAndC.html

This is the part that could save a poor (SA)FORTRAN cursed soul some tears:

Use GNU g++ or Intel C++ compiler to compile and link with main():
g++ -o name-of-exe main_prog.cpp file1.o file2.o -L/opt/intel/fc/9.0/lib -lifport -lifcore -limf -Wabi -Wcast-align

If this can help only one person who has this problem, I'm more than happy.