2

I have a base class (robot) and depending on the arguments passed to the program I want to downcast the robot pointer to a specific derived class. the robot class has virtual functions which are defined in each of the derived classes.

So far I can create a pointer of the base class, and within a conditional statement, create a derived class which the base is then dynamically cast to. Within the conditional the pointer works as intended but after leaving the conditional statement the derived class is out of scope and the functions go back to virtual. Is there a way I can keep the pointer to the derived class?

class vehicles{
    public:
        virtual void print_state(int state_ind, ofstream& file){}
};

class hovercraft : public vehicles{
    public:
        hovercraft(
            int L_in, int W_in, 
            double start_x, double start_y, 
            double goal_x, double goal_y)
        {
            L = L_in;
            W = W_in;
            start_state.x = start_x;
            start_state.y = start_y;
            goal_state.x = goal_x;
            goal_state.y = goal_y;
            Tree.push_back(start_state);
        }

        void print_state(int state_ind, ofstream& file){
            state s = Tree[state_ind];

            file<<s.phi<<","<<
                s.u<<","<<
                s.v<<","<<
                s.r<<","<<
                s.x<<","<<
                s.y<<"\n";
        }

    private:
        struct state{
            double g=0;
            double u=0;
            double v=0;
            double r=0;
            double x=0;
            double y=0;
            double phi=0;
            int p=0;
        };

        int L,W;
        state start_state, goal_state, state_sample;
        vector<state> Tree;
    };

int main(int argc, char* argv[])
{   
    ifstream infile;
    string vehicle_name;
    vehicles* vehicle;
    int start, goal;
    for(int i=0; i < argc; i++){
        if (string(argv[i])=="-m"){
            infile.open (argv[i+1]);
        } else if (string(argv[i])=="-v"){
            vehicle_name = string(argv[i+1]);
        } 
    }

    ofstream file_out;
    file_out.open ("state.csv");
    if(vehicle_name == "hovercraft"){
        int L, W;
        double start_x, start_y, goal_x, goal_y;
        infile >> L;
        infile >> W;
        infile >> start_x;
        infile >> start_y;
        infile >> goal_x;
        infile >> goal_y;

        hovercraft my_vehicle(L,W, start_x, start_y, goal_x, goal_y);
        hovercraft* vehicle = dynamic_cast<hovercraft*>(&my_vehicle);
    } else {
        cout<<"Invalid Vehicle: "<<vehicle_name<<"\n Exiting...\n";
        return 0;
    }

    vehicle->print_state(0);
}

This is simplified from the actual code but I think it captures the issue. The result I want is for print to work the same inside the if statement and after it but after it will always print "no vehicle".

mgw10
  • 23
  • 4
  • 5
    Here's a useful rule of thumb. If you think you need to downcast, chances are, you are doing something wrong. – n. m. could be an AI Oct 30 '19 at 05:00
  • I.e. you should be using a virtual function instead. – user207421 Oct 30 '19 at 06:28
  • You're not downcasting anywhere - your casts are to the same type that the pointer already has. Do you have a problematic example that does any meaningful casts? – molbdnilo Oct 30 '19 at 08:44
  • I edited the post to try to show more specifically what I am doing. I show a print state after the if statement but in the actual code at that point I would do motion planning for the selected robot. – mgw10 Oct 30 '19 at 13:23

1 Answers1

2

You're overloading the variable rptr inside the scope of your if-statements, which hides the one declared at the top of main. Your final rptr->print() is therefore undefined behavior, because you're calling a virtual function on an uninitialized pointer.

Simply remove the overloading as follows:

robot *rptr = nullptr;

if (*argv[1] == 'h')       // <-- also fixed this (yours was broken)
{
    rptr = new hovercraft();
    rptr->print();
}
else if (*argv[1] == 'q')  // <-- also fixed this (yours was broken)
{
    rptr = new quadrotor();
    rptr->print();
}
else
{
    std::cout << "Invalid vehicle input" << std::endl;
}

if (rptr)
{
    rptr->print(); 
}

You might also want to consider using std::shared_ptr or std::unique_ptr (along with std::make_shared or std::make_unique) to correctly manage the lifetime of dynamic memory.

paddy
  • 60,864
  • 6
  • 61
  • 103