Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

dynamic_cast issues: typeid object is not equal, but name is equal

I found that dynamic_cast didn't work in a situation where I expected it to, and looking at the typeid of the objects at runtime has made the situation even less clear. I just want a cast from base to derived, and I can't figure out why it's not working.

I have a class structure something like this:

class BoundaryCondition {
public:
    virtual void DoSomething() = 0;

    virtual ~BoundaryCondition() { /* * */ }
}

class ReflectingBc : BoundaryCondition {
public:
    virtual void DoSomething();
}

class MarshakBc : BoundaryCondition {
public:
    virtual void DoSomething();

    MarshakBc(double value);

    void changeValueLaterOn(double value);
private:
    double value_;
}

I have (essentially) a std::vector<BoundaryCondition*> that represents boundary conditions in parts of the problem. I want to be able to take that vector and, for all MarshakBc objects inside it, call changeValueLaterOn. So I have a loop that looks like

for (std::vector<BoundaryCondition*>::iterator bc = bcPtrs_.begin();
        bc != bcPtrs_.end(); ++bc)
{
    if (std::string(typeid(MarshakBc).name()) == std::string(typeid(**bc).name()) )
    {
        std::cerr << "SAME! ";
    }

    if (typeid(MarshakBc) != typeid(**bc))
    {
        std::cerr << "NOT SAME ";
    }
    MarshakBc* thisBc = dynamic_cast<MarshakBc*>( &( **bc ) );
    if (thisBc == NULL) {
        std::cerr << "...nothing\n";
        continue;
    }
    thisBc->changeValueLaterOn( 1.23);
    std::cerr << "...set!\n";
}

If my vector contains a ReflectingBc*, then a MarshakBc*, my output looks like:

NOT SAME ...nothing
SAME! NOT SAME ...nothing

Am I misunderstanding something about dynamic_cast and typeid?

[The actual situation is more complicated than this because the definition of BoundaryCondition is in a different translation unit than the above code, and templates and such are involved, but the above code is very representative of what I'm doing and the result I'm getting.]


More details

Here is my actual routine, which is used inside a functor, and LoAnisoBc is the derived class and BoundaryConditionT is the base class:

template<class SnTraits_T, class LoTraits_T>
void FillLoAnisoBcs<SnTraits_T, LoTraits_T>::operator() (
        const BoundaryFaceT& bf,
        BoundaryConditionT& bc)
{
    std::cerr << "Want " << typeid(LoAnisoBc).name() << "\n";
    std::cerr << "Chkg " << typeid(bc).name() << "\n";

    if (std::string(typeid(LoAnisoBc).name()) == std::string(typeid(bc).name()) )
    {
        std::cerr << " SAME!";
    }

    if (!(typeid(LoAnisoBc) == typeid(bc))) {
        std::cerr << "...nothing\n";
    }

    // if we're not an "anisotropic BC", don't do anything
    LoAnisoBc* anisoBc = dynamic_cast<LoAnisoBc*>( &bc );
    if (anisoBc == NULL) {
        std::cerr << "...nothing\n";
        return;
    }

    anisoBc->setFCoeff( fCoeff_ );
    std::cerr << "; set fCoeff = " << fCoeff_ << "\n";
}

And here's the output

Want N6detLib17cellDiffusionOned28AnisotropicBoundaryConditionE
Chkg N6detLib17cellDiffusionOned27ReflectingBoundaryConditionE
NOT SAME...nothing
Want N6detLib17cellDiffusionOned28AnisotropicBoundaryConditionE
Chkg N6detLib17cellDiffusionOned28AnisotropicBoundaryConditionE
SAME! NOT SAME...nothing

So the bcPtrs_ structure and the boundary conditions are in one dynamic library (so it's one module in Python), and the instantiation of FillLoAnisoBcs is in another dynamic library. Erik suggests this as the probable issue and I agree.

like image 488
Seth Johnson Avatar asked Apr 13 '11 16:04

Seth Johnson


1 Answers

typeid behaves strangely when you're crossing library boundaries - See e.g. When can typeid return different type_info instances for same type? for some cases. In particular, on linux you will need -rdynamic to ensure that type info objects from a library aren't removed, and thus inaccessible to the executable using the library.

My best guess at an explanation:

In the executable, where you see the LoAnisoBc definition, a type_info instance for LoAnisoBc exists. In the library, where LoAnisoBc "belongs", another type_info instance exists. type_info's operator== is likely implemented as a simple pointer comparison. So, when you get typeid of the static LoAnisoBc expression you get the executable instance, whereas the reference produces the library instance - same name but not same instance.

like image 103
Erik Avatar answered Oct 25 '22 11:10

Erik