Templating for polymorphism

25 views
Skip to first unread message

Robin Rofallski

unread,
Apr 10, 2024, 3:44:49 PMApr 10
to Ceres Solver
I'm working on a C++ project where I use templates to easily switch between several interior orientation classes for bundle adjustment.

Combined with ceres solver, I've come across this situation where I need to create an object of the template type, however this isn't possible due to an error:

> C2665 - no overloaded function could convert all the argument types

Is there a workaround to create an object of the templated type?

The working example should reproduce my problem though it doesn't do much and make much sense. The inheritance doesn't seem to play a role here but is the reason for templating in the first place.

#define GLOG_NO_ABBREVIATED_SEVERITIES
#define NOMINMAX
#pragma warning(disable : 4996) //Windows compiler specific with Ceres version 2.1.0
#include <ceres/ceres.h>

template<typename T>
class Base
{
public:
    Base()
    {
        X = static_cast<T>(0.0);
        Y = static_cast<T>(0.0);
    }
    Base(const T* io_array)
    {
        this->X = io_array[0];
        this->Y = io_array[1];
    }
    T doMath()
    {
        return X * X + Y * Y;
    }
protected:
    T X, Y;
};

template<typename T>
class Derived : public Base<T>
{
public:
    Derived() : Base()
    {
        Z = static_cast<T>(0.0);
    }
    Derived(const T* io_array) : Base<T>(io_array)
    {
        this->Z = io_array[2];
    }
    T doMath()
    {
        return this->X * this->X + this->Y * this->Y + this->Z * this->Z;
    }
protected:
    T Z;
};

template<typename IO>
struct cRelOri {
    cRelOri(const double var)
    {
        this->var = var;
    }
        template <typename T>
    bool operator()(const T* const io_array, T* residual) const
    {
        IO io(io_array); //Error C2665
        T res = io.doMath();

        //This works:
        //Base<T> io(io_array);
        //T res = io.doMath();

        //This also works:
        //Derived<T> io(io_array);
        //T res = io.doMath();
        residual[0] = res;
       
        return true;
    }

private:
    double var;
public:
    static ceres::CostFunction* Create(double& var1) {
        return (new ceres::AutoDiffCostFunction<cRelOri, 1, 3>
            (new cRelOri(var1)));
    }
};

int main()
{
    double io_array[3] = { 1.0, 2.0, 3.0 };
    double var = 4.2;
    // Create problem
    ceres::Problem problem;
    ceres::CostFunction* cost_function_ro = cRelOri<Derived<double>>::Create(var);
    problem.AddResidualBlock(cost_function_ro, NULL, io_array);
   
    ceres::Solver::Options options;
    ceres::Solver::Summary summary;
    ceres::Solve(options, &problem, &summary);

    return 0;
}


I've tried using pointers of a base class and then `dynamic_cast`'ing them to `IO`, which doesn't throw an error directly, but then strangely cannot convert identical (template) types any more to use the pointer.

I'm not sure if Ceres is the origin of this problem or if it's just something that's not supposed to work. I can't reproduce this problem with "standard classes" and get the feeling there might be something specific to ceres?

Any help is highly appreciated!

William Rucklidge

unread,
Apr 12, 2024, 12:52:58 AMApr 12
to ceres-...@googlegroups.com
In order to do autodifferentiation, to compute gradients, Ceres is going to call templated versions of operator() with different values for T - in particular, it will call with T=double and with T=Jet<double, N>. This means that you'll have to create versions of Base or Derived that are templated on the T used for the operator() call. Think about using a Derived<double> inside a call to operator() that's got T=Jet<double, N> - the line
        IO io(io_array); //Error C2665
is taking a Jet<double, N>* and passing it to something expecting a double*, which is why you're getting an error.

--
You received this message because you are subscribed to the Google Groups "Ceres Solver" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ceres-solver...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/ceres-solver/8eb7fbb6-2f16-4bdc-b519-b18f36304e2cn%40googlegroups.com.
Message has been deleted

William Rucklidge

unread,
Apr 16, 2024, 1:46:59 PMApr 16
to ceres-...@googlegroups.com
I have a feeling that the solution will involve nested template specialisation, which is not something I'm really familiar with.

On Tue, Apr 16, 2024 at 12:47 AM 'Robin Rofallski' via Ceres Solver <ceres-...@googlegroups.com> wrote:
Thanks for the response. I think I understand the problem but cannot really think of a solution, except for "hard coding" the separate classes and removing the templates which I would rather avoid, if possible. Would there be any way to circumvent this deadlock and still keep the templating polymorphism in place?

Thanks again!

Sergiu Deitsch

unread,
Apr 19, 2024, 12:50:07 AMApr 19
to ceres-...@googlegroups.com
You want a template template parameter here:

template <template <class> class IO>
struct cRelOri {
    // ...

    template <typename T>
    bool operator()(const T* const io_array, T* residual) const {
        IO<T> io(io_array);
        // ...
        return true;
    }
};


Then instantiate the cost function as follows:

ceres::CostFunction* cost_function_ro = cRelOri<Derived>::Create(var);

--
Reply all
Reply to author
Forward
0 new messages