@Bob_Carpenter and I were trying to iron out issues with std::pow that popped up with the introduction of the stan versions of the standard library complex functions into stan::math.
We have a solution we’d like other people to take a look at (it works – but we want to know if it is good C++ or whatever).
@syclik, @ChrisChiasson, @stevebronder, @rok_cesnovar, @mcol, @bgoodri or anyone else please have a look.
We were having problems resolving ambiguities between the stan implementations of pow, the standard C++ definitions of pow, and the C definitions of pow.
The solution we have now uses a using declaration statement to bring the std::pow implementations into the stan::math namespace. stan/math/prim/fun/pow.hpp
now contains:
namespace stan {
namespace math {
using std::pow;
}
}
This solved the problems we were having defining custom specializations of pow in stan::math and trying to be careful about what std stuff we were using to avoid ambiguities.
Basically our question is, is what we’re doing here okay? Is there some obvious reason we wouldn’t do this? It’s standard to not do using namespace
things (using-directives) in namespaces, but what about using declarations? (the differences are described here in 5 and 6 here: https://en.cppreference.com/w/cpp/language/namespace)
We’d like to resolve this in a few days, otherwise we need to revert the complex-funs pull request so we aren’t clogging up the development pipeline with our failing tests. Ideally we could have a video call tomorrow, Thursday, or Friday but comments here are good too.
Here’s a little more information about what needs to work.
There are eight use cases, and all these calls should work for a and b each being any of double
, int
, var
, fvar<double>
, fvar<var>
, fvar<fvar<double>>
, fvar<fvar<var>>
.
Use case A:
using namespace stan::math;
using namespace std;
pow(a, b)
Use case B:
using namespace stan::math;
using std::pow;
pow(a, b);
Use case C:
using stan::math::pow;
using namespace std;
pow(a, b);
Use case D:
using stan::math::pow;
using std::pow:
pow(a, b);
Use case E:
using namespace stan::math;
pow(a, b);
Use case F:
using namespace std;
pow(a, b);
Use case G:
using stan::math::pow;
pow(a, b);
Use case H:
using std::pow;
pow(a, b);
Our four implementation options are:
-
No arithmetic pow implementation in stan::math
-
Arithmetic pow implementation in stan::math. No templating, only overloads.
-
Templated arithmetic pow implementation in stan::math
-
using std::pow in stan::math
The first implementation fails for case E and G (no arithmetic implementations in stan::math)
The second implementation fails for at least A and E (ambiguities between pow(double, double)
implementations in C++, the C libraries, and stan::math).
The third implementation fails for at least A as well (ambiguities between stan::math and std::pow for arithmetic types).
The fourth implementation works everywhere (as far as we know).