template <typename Derived>
std::optional<Eigen::Array<typename Derived::Scalar, Eigen::Dynamic, Eigen::Dynamic>>
function1(const std::optional<Eigen::ArrayBase<Derived>>& opt1,
const std::optional<Eigen::ArrayBase<Derived>>& opt2)
{
}
how to write this kind of template with Eigen::ArrayBase so that it can accept two Array or Matrix or nullopt
like:
ArrayXXd a(4, 3);
ArrayXXd b(4, 3);
auto q = function1(a,b);
I know I can do this:
function2(const Eigen::ArrayBase<Derived1>& opt1,
const Eigen::ArrayBase<Derived2>& opt2)
{
}
I would advise against it but with some helper functions it can be made to work.
#include <optional>
#include <functional>
// using std::cref, std::reference_wrapper
template<class Derived>
using optional_const_array_ref =
std::optional<std::reference_wrapper<const Eigen::ArrayBase<Derived>>>;
template<class Derived>
optional_const_array_ref<Derived>
make_optional_const_array(const Eigen::ArrayBase<Derived>& arr) noexcept
{ return std::cref(arr); }
template <typename Derived>
void function1(optional_const_array_ref<Derived> opt1)
{
if(opt1)
std::cout << opt1->get().transpose() << '\n';
}
int main()
{
Eigen::Array3d b(1., 2., 3.);
function1(make_optional_const_array(b + 1.));
}
The reasons why this is hard:
optional
contains its content by-value. You cannot use it with an abstract classSeveral caveats:
option->
to access the array inside. You have to go the extra step of using reference_wrapper::get()
optional
or its content. This all seems like a recipe for creating dangling pointersoptional
have? You have to define some type but you can't necessarily use every type in Eigen. For one, you create an extra template instantiation, which will just waste a lot of compiled code size. Also, you might trigger static assertions and other compile time issues, for example if the scalar type is different from the ones you expect. You probably have to define the type explicitly per call site. E.g. function1(optional_const_array<Eigen::Array3d>{})
Did I already mention that I don't think this is a good idea? Because I don't think this is a good idea.
There is very little advantage in an optional reference wrapper compared to a plain pointer. We might as well do this:
template<class Derived>
const Eigen::ArrayBase<Derived>*
make_ptr(const Eigen::ArrayBase<Derived>& arr) noexcept
{ return &arr; }
template <typename Derived>
void function2(const Eigen::ArrayBase<Derived>* opt1)
{
if(opt1)
std::cout << opt1->transpose() << '\n';
}
int main()
{
Eigen::Array3d b(1., 2., 3.);
function2(make_ptr(b + 1));
function2<Eigen::Array3d>(nullptr);
}
It's just as unsafe as the reference wrapper but it's cheaper and shorter code. The moment you extend the lifetime of that pointer beyond the expression in which make_ptr
is called, you have a dangling pointer.