#31 example where sqrt(pow(a, 2)) works while abs(a) fails to c

closed-fixed
nobody
arrays (15)
5
2011-12-06
2011-10-21
Sylwester Arabas
No

In the following code:

#include <blitz/array.h>
#include <boost/units/systems/si.hpp>
using namespace blitz;
using namespace boost::units;
using namespace std;
typedef quantity<si::dimensionless, double> dimles;
int main() {
Array<double,1> a1(3), b1(abs(a1)); // OK
Array<dimles,1> a2(3), b2(sqrt(pow(a2,2))); // OK
Array<dimles,1> a3(3), b3(abs(a3)); // KO :(
}

The compilation fails on the line marked with "KO :(":
All other lines compile OK, and work OK after compilation.
abs(dimles) alone work OK, and the blitz-boost::units combination
seem to work flawlessly in numerous other contexts as well.

The compiler messages from clang++ 2.8 are listed below
(recent Blitz version from the hg repo and Boost v1.47)

HTH,
Sylwester

P.S. Patrik mentioned on the mailing list that:
"I vaguely remember that abs is a special case in the ET
code, so that might be the reason it doesn't work."

eyrie:Temp slayoo$ clang++ -I /opt/local/include/ a.cpp
In file included from a.cpp:1:
In file included from /opt/local/include/blitz/array.h:37:
In file included from /opt/local/include/blitz/array-impl.h:2505:
In file included from /opt/local/include/blitz/array.cc:9:
In file included from /opt/local/include/blitz/array/asexpr.cc:38:
/opt/local/include/blitz/array/expr.h:505:26: error: implicit instantiation of undefined template
'blitz::Fn_abs<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> >'
typedef _bz_typename T_op::T_numtype T_numtype;
^
/opt/local/include/blitz/array/expr.h:111:26: note: in instantiation of template class
'blitz::_bz_ArrayExprUnaryOp<blitz::_bz_ArrayExpr<blitz::FastArrayIterator<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>, 1>
, blitz::Fn_abs<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> > >'
requested here
typedef _bz_typename T_expr::T_numtype T_numtype;
^
a.cpp:10:29: note: in instantiation of template class
'blitz::_bz_ArrayExpr<blitz::_bz_ArrayExprUnaryOp<blitz::_bz_ArrayExpr<blitz::FastArrayIterator<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>, 1>
, blitz::Fn_abs<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> > >
' requested here
Array<dimles,1> a3(3), b3(abs(a3)); // KO :(
^
In file included from a.cpp:1:
In file included from /opt/local/include/blitz/array.h:37:
In file included from /opt/local/include/blitz/array-impl.h:2505:
In file included from /opt/local/include/blitz/array.cc:11:
In file included from /opt/local/include/blitz/range.cc:6:
In file included from /opt/local/include/blitz/array/ops.h:35:
/opt/local/include/blitz/funcs.h:553:8: note: template is declared here
struct Fn_abs;
^
1 error generated.

Discussion

  • Patrik Jonsson
    Patrik Jonsson
    2011-12-06

    Finally got a moment to look into this. The primary problem was that there was indeed no generic template implemented for Fn_abs, only specializations for the POD numeric types. I added a generic implementation, which relies on an overload for "abs()" existing for the type.

    But there's a further problem: abs(dimles) does not exist, it relies on an implicit conversion to double. When the array applic tries to do abs(a), it finds the *array abs*, which doesn't match. It then stops looking and says:

    blitz_hg/blitz/funcs.h(558): error: no instance of function template "blitz::abs" matches the argum
    ent list
    argument types are: (const dimles)
    apply(const T_numtype1 a) { return abs(a); }

    Note that it says "no instance of FUNCTION TEMPLATE BLITZ::ABS". This is a result of the C++ name lookup. If we redefine the array abs function to be called "myabs", there is no clash and myabs(a3) then works fine.

    This accidental matching between the blitz array function templates defined in array/funcs.h and the underlying numeric function is the cause of quite a few headaches, in my experience. I believe this can be solved by using SFINAE to prevent the array function template from being considered, so I'll try to play with this a bit, but that will touch the core function resolution so would need some consideration.

     
  • Patrik Jonsson
    Patrik Jonsson
    2011-12-06

    • status: open --> open-accepted
     
  • Patrik Jonsson
    Patrik Jonsson
    2011-12-06

    This required a severe refresh of C++ lookup rules. Currently, the functors explicitly qualify the math function called in apply() by doing e.g.

    BZ_MATHFN_SCOPE(sin(a))

    When I do the same with the generic abs function, your example compiles.

    However, the problem with this approach is that it will only work for cases where the operation on the contained type can be done by forcing a call to the builtin math function. So, if I try to make an Array<myType, 1> for which I've defined sin(mytype) this fails to compile because we are hardwiring the application of the math operator to use std::sin(mytype).

    In this situation it would be better to use the UNQUALIFIED name, because then argument-dependent lookup would look for a function sin(mytype) in the namespace where mytype is defined, regardless of what we have in the blitz namespace. For example, if I define dimles as "struct dimles {};" and then define a "dimles sin(dimles) {};", this will work with the unqualified name in the apply() method but not with BZ_MATHFN_SCOPE(sin(a)).

    I believe the solution is to define the functors for fundamental types and class types separately.

    I think the immediate solution here is to do away with the separate definitions of abs(). According to standard C++, there are overloaded abs() methods for all floating and integer types, so the explicit definitions seem like a C holdover that we can throw out now.

     
  • Patrik Jonsson
    Patrik Jonsson
    2011-12-06

    • status: open-accepted --> closed-fixed
     
  • Patrik Jonsson
    Patrik Jonsson
    2011-12-06

    Fixed in r1893, abs is now treated like all other unary functions.