【问题标题】:Ceres-Solver cost function inheritance error: Templates may not be virtualCeres-Solver 成本函数继承错误:模板可能不是虚拟的
【发布时间】:2018-06-18 11:01:43
【问题描述】:

我已经使用ceres-solver 很长时间了,它是一个了不起的工具。到目前为止,我的使用不是基于可重用代码,我正在努力改进这一点。 Ceres 使用具有特定模板化方法的特定结构作为其automatic differentiation 的接口。在我试图解决的问题中,继承是有意义的,因为我需要拥有的不同成本函数彼此非常相似。我创建了一个类似的示例(但没有意义,抱歉)。想象一下,我们希望能够找到具有给定面积的多边形。在我的示例中,多边形可以是三角形或矩形。考虑到这一点,有一个实现所有内容的基类和实现每个特定多边形的面积计算的特定类是有意义的:

ShapeCostFunction

class shapeAreaCostFunction
{
public:
    shapeAreaCostFunction(double desired_area): desired_area_(desired_area){}

    template<typename T>
    bool operator()(const T* shape, T* residual) const{
        residual[0] = T(desired_area_) - area(shape);
        return true;
    }

    template<typename T>
    virtual T area(const T* shape) const = 0;

protected:
    double desired_area_;
};

矩形成本函数

#include "shapeAreaCostFunction.h"
#include "areaLibrary.h"

class rectangleAreaCostFunction : public shapeAreaCostFunction
{
public:
    rectangleAreaCostFunction(double desired_area): shapeAreaCostFunction(desired_area){}

    template<typename T>
    T area(const T* triangle) const
    {
        return rectangleArea(triangle);
    }
};

TriangleCostFunction

#include "shapeAreaCostFunction.h"
#include "areaLibrary.h"

class triangleAreaCostFunction : public shapeAreaCostFunction
{
public:
    triangleAreaCostFunction(double desired_area): shapeAreaCostFunction(desired_area){}

    template<typename T>
    T area(const T* triangle) const
    {
        return triangleArea(triangle);
    }
};

区域库

template<typename T>
T rectangleArea(const T* rectangle)
{
    return rectangle[0]*rectangle[1];
}

template<typename T>
T triangleArea(const T* triangle)
{
    return rectangleArea(triangle)/T(2);
}

主要

#include <ceres/ceres.h>
#include <iostream>

#include "rectangleAreaCostFunction.h"
#include "triangleAreaCostFunction.h"
#include "areaLibrary.h"

int main(int argc, char** argv){

    // Initialize glogging
    //google::InitGoogleLogging(argv[0]);

    // Get values
    /// Get total area
    double total_area;
    std::cout<<"Enter the desired area: ";
    std::cin>>total_area;
    /// Get initial rectangle
    double rect[2];
    std::cout<<"Enter initial rectangle base: ";
    std::cin>>rect[0];
    std::cout<<"Enter initial rectangle height: ";
    std::cin>>rect[1];
    /// Get initial triagnle
    double tri[2];
    std::cout<<"Enter initial triangle base: ";
    std::cin>>tri[0];
    std::cout<<"Enter initial triangle height: ";
    std::cin>>tri[1];

    // Copy initial values
    double rect_ini[] = {rect[0],rect[1]};
    double tri_ini[] = {tri[0],tri[1]};

    // Create problem
    ceres::Problem problem;
    ceres::CostFunction* cost_function_rectangle = new ceres::AutoDiffCostFunction<rectangleAreaCostFunction, 1, 2>(
            new rectangleAreaCostFunction(total_area));
    ceres::CostFunction* cost_function_triangle = new ceres::AutoDiffCostFunction<triangleAreaCostFunction, 1, 2>(
            new triangleAreaCostFunction(total_area));
    problem.AddResidualBlock(cost_function_rectangle, NULL, rect);
    problem.AddResidualBlock(cost_function_triangle, NULL, tri);

    // Solve
    ceres::Solver::Options options;
    options.linear_solver_type = ceres::DENSE_QR;
    options.minimizer_progress_to_stdout = true;
    options.max_num_iterations = 10;
    ceres::Solver::Summary summary;
    ceres::Solve(options, &problem, &summary);

    // Compute final areas
    double rect_area = rectangleArea(rect);
    double tri_area = triangleArea(tri);

    // Display results
    std::cout << summary.FullReport() << std::endl;
    std::cout<<"Rectangle: ("<<rect_ini[0]<<","<<rect_ini[1]<<") -> ("<<rect[0]<<","<<rect[1]<<") total area: "<<rect_area<<"("<< rect_area - total_area<<")"<<std::endl;
    std::cout<<"Triangle: ("<<tri_ini[0]<<","<<tri_ini[1]<<") -> ("<<tri[0]<<","<<tri[1]<<") total area: "<<tri_area<<"("<< tri_area - total_area<<")"<<std::endl;

    // Exit
    return 0;
}

这样做的问题是模板函数不能是虚拟的,正如在 stackoverflow 中多次解释的那样(herehere)。但是,似乎有一些workarounds 使用boost::any。我试图在我的示例中使用它但没有成功。我也尝试过将模板从类方法移到类中,类似于here,但是ceres不接受它作为代价函数。

我的问题是(请记住,我只能使用 template&lt;typename T&gt; bool operator()(...)const 方法,否则我无法与 ceres 交互):

  1. 拥有这样的继承系统是否有意义(想象一下,这是一个比示例复杂得多的问题)?
  2. 有没有办法保持这个继承系统并使代码正常工作,或者我应该将所有内容移动到单独的函数中并从每个template&lt;typename T&gt; bool operator()(...)const 类方法中调用正确的函数?

提前谢谢你。

【问题讨论】:

    标签: c++ templates inheritance ceres-solver


    【解决方案1】:

    我可以想到两种方法。

    首先,编写 lambda。其次,使用 CRTP。

    编写 Lambdas

    最好使用

    template<class Area>
    auto cost_function(Area area, double desired){
      return [=](auto const* shape, auto* residual){
        using T=std::decay_t<decltype(*shape)>;
        residual[0] = T(desired_area_) - area(shape);
        return true;
      };
    }
    auto triangle = [](auto* shape){return triangleArea(shape);};
    

    创建三角形面积成本函数:

    auto tri_cost = cost_function(triangle, 3.14159);
    

    要获取类型,decltype(tri_cost)

    所以:

    auto tri_cost = cost_function(triangle, 3.14159);
    ceres::CostFunction* cost_function_triangle = new ceres::AutoDiffCostFunction<decltype(tri_cost), 1, 2>(
            new decltype(tri_cost)(tri_cost));
    

    您可以在不使用 lambda 的情况下执行类似的组合技术,但它更乏味。您还可以将其中一些裸露的 new 包装在辅助函数中。

    CRTP

    template<class D>
    class shapeAreaCostFunction {
    public:
      shapeAreaCostFunction(double desired_area): desired_area_(desired_area){}
    
      template<typename T>
      bool operator()(const T* shape, T* residual) const{
        residual[0] = T(desired_area_) - static_cast<D const*>(this)->area(shape);
        return true;
      }
    protected:
      double desired_area_;
    };
    

    像这样修改派生类型:

    class triangleAreaCostFunction :
      public shapeAreaCostFunction<triangleAreaCostFunction>
    {
      using base=shapeAreaCostFunction<triangleAreaCostFunction>;
    public:
      triangleAreaCostFunction(double desired_area): base(desired_area){}
    
      template<typename T>
      T area(const T* triangle) const
      {
        return triangleArea(triangle);
      }
    };
    

    这称为使用 CRTP 实现静态多态。

    【讨论】:

    • 我喜欢这种 CRTP 方法,因为它消除了虚拟呼叫。
    • 谢谢@Yakk,CRTP 解决方案只需复制粘贴即可完美运行。 lamnda 解决方案有点超出我理解复杂 c++ 代码的能力。
    猜你喜欢
    • 1970-01-01
    • 2020-03-10
    • 2011-06-25
    • 2012-09-27
    • 2019-10-20
    • 1970-01-01
    • 2015-06-21
    • 2023-04-10
    • 1970-01-01
    相关资源
    最近更新 更多