mercredi 17 juin 2020

Specializing a templated overriden function / Avoid type slicing

I have started to write some classes to compute the transformation along a kinematic chain. I'm having a templated parent class KinematicChainSegment and multiple specialized implementations of it (e.g. for revolute or prismatic joints). To give a concrete, minimum example - this how I would like it to look:

#include <Eigen/Dense>

template<typename T>
class KinematicChainSegment {
    public:
        KinematicChainSegment() {};
        virtual ~KinematicChainSegment() {};

        virtual Eigen::Matrix<T, 4, 4> getTransformationMatrix() const = 0;
        virtual KinematicChainSegment<T> inverse() const = 0;
};

template<typename T>
class StaticLink : public KinematicChainSegment<T> {
    public:
        StaticLink(const Eigen::Matrix<T, 4, 4>& transformation = Eigen::Matrix<T, 4, 4>::Identity())
        : KinematicChainSegment<T>(),
          _transformationMatrix(transformation) {
        }

        virtual ~StaticLink() {};

        virtual Eigen::Matrix<T,4,4> getTransformationMatrix() const override {
            return _transformationMatrix;
        }

        virtual StaticLink<T> inverse() const override {
            return StaticLink<T>(_transformationMatrix.inverse());
        }

    protected:
        Eigen::Matrix<T,4,4> _transformationMatrix;
};

However, when compiling this example I get the error: invalid abstract return type ‘KinematicChainSegment<T>’, and changing the return type of StaticLink<T>::inverse() to KinematicChainSegment<T> results in error: invalid abstract return type ‘KinematicChainSegment<T>’, so I ended up with the following piece of code:

#include <Eigen/Dense>
#include <iostream>

template<typename T>
class KinematicChainSegment {
    public:
        KinematicChainSegment() {};
        virtual ~KinematicChainSegment() {};

        virtual Eigen::Matrix<T, 4, 4> getTransformationMatrix() const {
            std::cout << "Calling KinematicChainSegment<T>::getTransformationMatrix(). This method should be overwritten by any derivative class." << std::endl;
            return Eigen::Matrix<T, 4, 4>::Identity();
        }

        virtual KinematicChainSegment<T> inverse() const {
            std::cout << "Calling KinematicChainSegment<T>::inverse(). This method should be overwritten by any derivative class." << std::endl;
            return KinematicChainSegment<T>();
        };
};

template<typename T>
class StaticLink : public KinematicChainSegment<T>   {
    public:
        StaticLink(const Eigen::Matrix<T, 4, 4>& transformation = Eigen::Matrix<T, 4, 4>::Identity())
        : KinematicChainSegment<T>(),
          _transformationMatrix(transformation) {
        }

        virtual ~StaticLink() {};

        virtual Eigen::Matrix<T,4,4> getTransformationMatrix() const override {
            return _transformationMatrix;
        }

        virtual KinematicChainSegment<T> inverse() const override {
            return StaticLink<T>(_transformationMatrix.inverse());
        }

    protected:
        Eigen::Matrix<T,4,4> _transformationMatrix;
};

int main(int argc, char** argv) {
    Eigen::Matrix<float,4,4> transform;
    transform << 1, 0, 0, 1,
                 0, 1, 0, 2,
                 0, 0, 1, 3,
                 0, 0, 0, 1;

    KinematicChainSegment<float> link = StaticLink<float>(transform);
    std::cout << "link: " << link.getTransformationMatrix() << std::endl;

    std::cout << "inverse link: " << link.inverse().getTransformationMatrix() << std::endl;
}

Unfortunately this comes now with a bunch of type slicing issues, as you can see on the programs output below:

link: Calling KinematicChainSegment<T>::getTransformationMatrix(). This method should be overwritten by any derivative class.
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
inverse link: Calling KinematicChainSegment<T>::inverse(). This method should be overwritten by any derivative class.
Calling KinematicChainSegment<T>::getTransformationMatrix(). This method should be overwritten by any derivative class.
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1

I guess I might have a too Java-like understanding of object inheritance... What is the best practice to resolve an issue like this? From what I found so far it seems that the only option is to use pointers both, all return types and the used object instances... Or is there any other way of making this work?

Aucun commentaire:

Enregistrer un commentaire