Собственный с настраиваемыми скалярными типами: умножение матриц с настраиваемым типом завершается ошибкой с использованием `перегруженного оператора '*' неоднозначно` - PullRequest
1 голос
/ 27 мая 2020

Я пытаюсь интегрировать boost.units в Eigen, следуя официальной документации . Я также рассмотрел два проекта, которые сделали то же самое, и я также протестировал работу с их кодом, хотя получаю ту же ошибку ( Project 1 Project 2 * 1008 •). с clang 10.0.0.3 и Apple clang 11.0.3.

Код, вызывающий ошибку (use of overloaded operator '*' is ambiguous), следующий:

    const Eigen::Matrix<boost::units::quantity<boost::units::si::length, double>, 2, 3> x;
    const Eigen::Matrix<boost::units::quantity<boost::units::si::dimensionless, double>, 3, 1> y;
    const Eigen::Matrix<boost::units::quantity<boost::units::si::length, double>, 2, 1> result = x * y;

Для интеграции I создал файлы, содержащие NumTraits для boost::units::quantity<T> следующим образом:

namespace Eigen {

template<class T> struct NumTraits<boost::units::quantity<T>> : NumTraits<double> {
    typedef boost::units::quantity<T> Real;
    typedef boost::units::quantity<T> NonInteger;
    typedef boost::units::quantity<T> Nested;

    enum {
        IsComplex             = 0,
        IsInteger             = 0,
        IsSigned              = 1,
        RequireInitialization = 1,
        ReadCost              = 1,
        AddCost               = 3,
        MulCost               = 3
    };
};
} // namespace Eigen

А для поддержки умножения у меня есть следующие struct определения:

namespace Eigen {

namespace units = boost::units;

template<typename scalarA, typename scalarB>
struct ScalarBinaryOpTraits<units::quantity<scalarA>, scalarB,
    internal::scalar_product_op<units::quantity<scalarA>, scalarB>> {
    typedef units::quantity<typename units::multiply_typeof_helper<scalarA, scalarB>::type> ReturnType;
};

template<typename scalarA, typename scalarB>
struct ScalarBinaryOpTraits<scalarA, units::quantity<scalarB>,
    internal::scalar_product_op<scalarA, units::quantity<scalarB>>> {
    typedef units::quantity<typename units::multiply_typeof_helper<scalarA, scalarB>::type> ReturnType;
};

template<typename scalarA, typename scalarB>
struct ScalarBinaryOpTraits<scalarA, units::quantity<scalarB>,
    internal::scalar_product_op<units::quantity<scalarA>, units::quantity<scalarB>>> {
    typedef units::quantity<typename units::multiply_typeof_helper<scalarA, scalarB>::type> ReturnType;
};

template<typename scalarA, typename scalarB>
struct ScalarBinaryOpTraits<units::quantity<scalarA>, scalarB,
    internal::scalar_conj_product_op<units::quantity<scalarA>, scalarB>> {
    typedef typename units::multiply_typeof_helper<scalarA, scalarB>::type ReturnType;
};

template<typename scalarA, typename scalarB>
struct ScalarBinaryOpTraits<scalarA, units::quantity<scalarB>,
    internal::scalar_conj_product_op<scalarA, units::quantity<scalarB>>> {
    typedef typename units::multiply_typeof_helper<scalarA, scalarB>::type ReturnType;
};

template<typename scalarA, typename scalarB>
struct ScalarBinaryOpTraits<units::quantity<scalarA>, units::quantity<scalarB>,
    internal::scalar_conj_product_op<units::quantity<scalarA>, units::quantity<scalarB>>> {
    typedef units::quantity<typename units::multiply_typeof_helper<scalarA, scalarB>::type> ReturnType;
};

} // namespace Eigen

Полный вывод ошибок:

Scanning dependencies of target LaserCalibration
[  8%] Building CXX object CMakeFiles/LaserCalibration.dir/maths/Maths.cpp.o
/Users/dwright/projects/dv-laser-calibration/maths/Maths.cpp:9:97: error: use of overloaded operator '*' is ambiguous (with operand types 'const Eigen::Matrix<boost::units::quantity<boost::units::si::length, double>, 2, 3>' (aka 'const Matrix<quantity<unit<list<dim<boost::units::length_base_dimension, static_rational<1> >, boost::units::dimensionless_type>, homogeneous_system<list<boost::units::si::meter_base_unit, boost::units::list<boost::units::scaled_base_unit<boost::units::cgs::gram_base_unit, boost::units::scale<10, static_rational<3> > >, boost::units::list<boost::units::si::second_base_unit, boost::units::list<boost::units::si::ampere_base_unit, boost::units::list<boost::units::si::kelvin_base_unit, boost::units::list<boost::units::si::mole_base_unit, boost::units::list<boost::units::si::candela_base_unit, boost::units::list<boost::units::angle::radian_base_unit, boost::units::list<boost::units::angle::steradian_base_unit, boost::units::dimensionless_type> > > > > > > > > > >, double>, 2, 3>') and 'const Eigen::Matrix<boost::units::quantity<boost::units::si::dimensionless, double>, 3, 1>' (aka 'const Matrix<quantity<unit<boost::units::dimensionless_type, homogeneous_system<list<boost::units::si::meter_base_unit, boost::units::list<boost::units::scaled_base_unit<boost::units::cgs::gram_base_unit, boost::units::scale<10, static_rational<3> > >, boost::units::list<boost::units::si::second_base_unit, boost::units::list<boost::units::si::ampere_base_unit, boost::units::list<boost::units::si::kelvin_base_unit, boost::units::list<boost::units::si::mole_base_unit, boost::units::list<boost::units::si::candela_base_unit, boost::units::list<boost::units::angle::radian_base_unit, boost::units::list<boost::units::angle::steradian_base_unit, boost::units::dimensionless_type> > > > > > > > > > >, double>, 3, 1>'))
        const Eigen::Matrix<boost::units::quantity<boost::units::si::length, double>, 2, 1> result = x * y;
                                                                                                     ~ ^ ~
/usr/local/include/eigen3/Eigen/src/Core/../plugins/CommonCwiseBinaryOps.h:50:29: note: candidate function [with T = Eigen::Matrix<boost::units::quantity<boost::units::unit<boost::units::list<boost::units::dim<boost::units::length_base_dimension, boost::units::static_rational<1, 1> >, boost::units::dimensionless_type>, boost::units::homogeneous_system<boost::units::list<boost::units::si::meter_base_unit, boost::units::list<boost::units::scaled_base_unit<boost::units::cgs::gram_base_unit, boost::units::scale<10, static_rational<3> > >, boost::units::list<boost::units::si::second_base_unit, boost::units::list<boost::units::si::ampere_base_unit, boost::units::list<boost::units::si::kelvin_base_unit, boost::units::list<boost::units::si::mole_base_unit, boost::units::list<boost::units::si::candela_base_unit, boost::units::list<boost::units::angle::radian_base_unit, boost::units::list<boost::units::angle::steradian_base_unit, boost::units::dimensionless_type> > > > > > > > > >, void>, double>, 2, 3, 0, 2, 3>]
EIGEN_MAKE_SCALAR_BINARY_OP(operator*,product)
                            ^
/usr/local/include/eigen3/Eigen/src/Core/../plugins/CommonCwiseBinaryOps.h:50:29: note: candidate function [with T = Eigen::Matrix<boost::units::quantity<boost::units::unit<boost::units::dimensionless_type, boost::units::homogeneous_system<boost::units::list<boost::units::si::meter_base_unit, boost::units::list<boost::units::scaled_base_unit<boost::units::cgs::gram_base_unit, boost::units::scale<10, static_rational<3> > >, boost::units::list<boost::units::si::second_base_unit, boost::units::list<boost::units::si::ampere_base_unit, boost::units::list<boost::units::si::kelvin_base_unit, boost::units::list<boost::units::si::mole_base_unit, boost::units::list<boost::units::si::candela_base_unit, boost::units::list<boost::units::angle::radian_base_unit, boost::units::list<boost::units::angle::steradian_base_unit, boost::units::dimensionless_type> > > > > > > > > >, void>, double>, 3, 1, 0, 3, 1>]
/usr/local/include/eigen3/Eigen/src/Core/MatrixBase.h:166:5: note: candidate function [with OtherDerived = Eigen::Matrix<boost::units::quantity<boost::units::unit<boost::units::dimensionless_type, boost::units::homogeneous_system<boost::units::list<boost::units::si::meter_base_unit, boost::units::list<boost::units::scaled_base_unit<boost::units::cgs::gram_base_unit, boost::units::scale<10, static_rational<3> > >, boost::units::list<boost::units::si::second_base_unit, boost::units::list<boost::units::si::ampere_base_unit, boost::units::list<boost::units::si::kelvin_base_unit, boost::units::list<boost::units::si::mole_base_unit, boost::units::list<boost::units::si::candela_base_unit, boost::units::list<boost::units::angle::radian_base_unit, boost::units::list<boost::units::angle::steradian_base_unit, boost::units::dimensionless_type> > > > > > > > > >, void>, double>, 3, 1, 0, 3, 1>]
    operator*(const MatrixBase<OtherDerived> &other) const;
    ^
/Users/dwright/projects/dv-laser-calibration/maths/Maths.cpp:6:106: warning: unused parameter 'event' [-Wunused-parameter]
Eigen::Matrix<boost::units::quantity<double>, 2, 1> Maths::convertToPhysicalCoordinates(const dv::Event &event) const {
                                                                                                         ^
In file included from /Users/dwright/projects/dv-laser-calibration/maths/Maths.cpp:1:
/Users/dwright/projects/dv-laser-calibration/maths/Maths.hpp:16:25: warning: private field 'parameters' is not used [-Wunused-private-field]
        CalibrationParameters &parameters;
                               ^
2 warnings and 1 error generated.
make[2]: *** [CMakeFiles/LaserCalibration.dir/maths/Maths.cpp.o] Error 1
make[1]: *** [CMakeFiles/LaserCalibration.dir/all] Error 2
make: *** [all] Error 2
11:04:41: The process "/usr/local/Cellar/cmake/3.17.2/bin/cmake" exited with code 2.
Error while building/deploying project LaserCalibration (kit: Imported Kit)
When executing step "CMake Build"
11:04:41: Elapsed time: 00:02.

1 Ответ

1 голос
/ 27 мая 2020

Игнорируя сопряженные черты продукта, я вижу, что третья специализация кажется отключенной:

template <typename scalarA, typename scalarB>
    struct ScalarBinaryOpTraits<
    scalarA, units::quantity<scalarB>,
    internal::scalar_product_op<units::quantity<scalarA>,
    units::quantity<scalarB>>> {
        typedef units::quantity<
            typename units::multiply_typeof_helper<scalarA, scalarB>::type>
            ReturnType;
    };

Строка:

    scalarA, units::quantity<scalarB>,

Надо читать:

    units::quantity<scalarA>, units::quantity<scalarB>,

Устранение причины

Эта третья специализация, которую мы только что исправили, очевидно, должна соответствовать нашему случаю. Однако другие специализации не исключают случая, когда оба скаляра являются количеством.

Комментирование нерелевантных двух действительно помогает:

Live On Compiler Explorer

#include <boost/units/unit.hpp>
#include <boost/units/io.hpp>
#include <boost/units/limits.hpp>
#include <boost/units/quantity.hpp>
#include <boost/units/systems/si.hpp>
#include <boost/units/cmath.hpp>
#include <Eigen/Core>

namespace Eigen {
    namespace units = boost::units;

#if 0
    template <typename scalarA, typename scalarB>
        struct ScalarBinaryOpTraits<
        units::quantity<scalarA>, scalarB,
        internal::scalar_product_op<units::quantity<scalarA>, scalarB>> {
            typedef units::quantity<
                typename units::multiply_typeof_helper<scalarA, scalarB>::type>
                ReturnType;
        };
#endif

#if 0
    template <typename scalarA, typename scalarB>
        struct ScalarBinaryOpTraits<
        scalarA, units::quantity<scalarB>,
        internal::scalar_product_op<scalarA, units::quantity<scalarB>>> {
            typedef units::quantity<
                typename units::multiply_typeof_helper<scalarA, scalarB>::type>
                ReturnType;
        };
#endif

#if 1
    template <typename scalarA, typename scalarB>
        struct ScalarBinaryOpTraits<
        units::quantity<scalarA>, units::quantity<scalarB>,
        internal::scalar_product_op<units::quantity<scalarA>,
        units::quantity<scalarB>>> {
            typedef units::quantity<
                typename units::multiply_typeof_helper<scalarA, scalarB>::type>
                ReturnType;
        };
#endif
} // namespace Eigen

#include <iostream>
int main() {
    using V = boost::units::quantity<boost::units::si::length, double>;
    using U = boost::units::quantity<boost::units::si::dimensionless, double>;
    constexpr auto Vu = boost::units::si::meter;
    constexpr auto Uu = 1.0;

    Eigen::Matrix<V, 2, 3> x;
    Eigen::Matrix<U, 3, 1> y;

    x << 1*Vu, 2*Vu, 3*Vu, 4*Vu, 5*Vu, 6*Vu;
    y << 1*Uu, 2*Uu, 3*Uu;

    std::cout << "x:\n" << x << "\n";
    std::cout << "y:\n" << y << "\n";
    auto result = (x * y).eval();

    std::cout << "result:\n" << result << "\n";
}

Печать

x:
  1 m   2 m   3 m
  4 m   5 m   6 m
y:
              1 dimensionless
              2 dimensionless
              3 dimensionless
result:
  14 m
  32 m

FIX?

Я бы попытался объединить все три в одной специализации. Однако ScalarBinaryOpTraits не подходит для SFINAE. Тем не менее я пытался что-то сделать:

namespace Eigen {
    namespace units = boost::units;

    namespace /*file-static*/ {
        template <typename A, typename B, typename R = units::multiply_typeof_helper<A, B> >
        struct QuantityProductImpl {
            using ReturnType = typename R::type;
        };
    }

    template <typename A, typename B>
        struct ScalarBinaryOpTraits<A, B,
            std::enable_if_t< units::is_quantity<A>::value && units::is_quantity<B>::value,
                internal::scalar_product_op<A, B>
            >
        > : QuantityProductImpl<A, B> { };
} // namespace Eigen

Что работает, как и раньше: Live On Compiler Explorer

Однако, если мы изменим && в || (или != для XOR logi c) мы снова получаем двусмысленность. Не знаете, что делать дальше, но, может быть, это даст вам идеи?

...