Re: [eigen] proposal for "clean" output arguments

[ Thread Index | Date Index | More lists.tuxfamily.org/eigen Archives ]


On Tue, Aug 18, 2009 at 6:57 PM, Robert
Bocquier<robert.bocquier@xxxxxxxxxxx> wrote:
> Hi,
> What about overloading an operator instead of adding the .output() method ?
> I am referring either to say the ~ operator, or perhaps better a type
> conversion operator.
> So instead of :
>
>        mat.lu().solve(b, xs.col(2).output());
>
> one will write :
>
>        mat.lu().solve(b, (Output<T2> ) xs.col(2));

unfortunately this solution requires to explicitly write the type of
the object which can be quite complicated in the case of a complex
expression...

> or :
>
>        mat.lu().solve(b, ~ xs.col(2));

It is usually not recommended to abuse of operator overloading when
its meaning is not obvious. Here one could expect that xs is a matrix
of bool and ~ xs.col(2) is the coeff-wise bitwise one's complement, so
that sounds very confusing. And there is no way to change the behavior
of operator~ for scalar types!

gael

> That should work for scalar as well.
>
> Robert
>
> Gael Guennebaud a écrit :
>> Hi all,
>>
>> here is a proposal to deal with functions having output (or inout)
>> arguments. Currently the situation is quite a mess:
>>
>> 1 - some take non const references like
>> TriangularView::solveInPlace(MatrixBase<T>&)
>> 2 - some take pointers like LU::solve(const MatrixBase<T1>& B,
>> MatrixBase<T2>* X)
>> 3 - some take const references like MatrixBase<T1>::swap(const MatrixBase<T2>&)
>>
>> The main advantage of the solution 3 is that allows to use temporary
>> proxies, e.g.:
>>
>> m.col(i).swap(m.col(j));
>>
>> The main advantage of solution 2 is that it makes it clear what is an output:
>>
>> m.lu().solve(b, &x);
>>
>> The respective drawbacks of each method are pretty obvious I won't
>> enumerate them one more time...
>>
>> So what I propose is to add a trivial Output<T> class mimicking a
>> reference T& that we'll be passed by value. Ok to make thing crystal
>> clear here is such a Output class:
>>
>> template<typename T> class Output
>> {
>>   public:
>>     Output(T& obj) : m_object(obj) { }
>>     operator T&() { return m_object; }
>>   protected:
>>     T& m_object;
>> };
>>
>> then we add a output() member function to AnyMatrixBase<> :
>>
>> Output<Derived> output() { return derived(); }
>>
>> then the function LU::solve(const MatrixBase<T1>& B, MatrixBase<T2>*
>> X) can be rewritten:
>>
>> LU::solve(const MatrixBase<T1>& B, Output<T2> _x) {
>>  T2& x(_x);
>>   // use x
>> }
>>
>> and the user sill call it like this:
>>
>> mat.lu().solve(b, xs.col(2).output());
>>
>> For in-out argument we can do the same with a InOut<T> class, and a
>> AnyMatrixBase::inout() function.
>>
>> Unless I missed something, I think this solution has all the
>> advantages that someone can expect:
>> - it is more C++ than pointers,
>> - it respects constness (unlike const references)
>> - it allows to use temporary proxies returned by a function (unlike
>> references and pointers)
>> - it make it crystal clear what is an output and an in-out arguments
>> when someone read a piece of code (unlike all other solutions)
>>
>> The only limitation I can see is how to extend this concept to scalar
>> type arguments because we cannot have:
>>
>> double x;
>> x.output()
>>
>> Note that if we don't make the ctor of Output explicit, then the
>> following we'll work:
>>
>> void foo(Output<float> _x);
>>
>> float x;
>> foo(x);
>>
>> If we want to enforce to have "output" next to x, one possibility is
>> to add a global function output(T&) and make the ctor of Output
>> explicit:
>>
>> float x;
>> foo(output(x));
>>
>> Note that such a global function will only work on declared objects,
>> and not on temporary proxies, that is why we really have to also have
>> the output() function as a member of AnyMatrixBase...
>>
>> Of course, another drawback is more API changes...
>>
>> What do you think ?
>>
>> gael.
>>
>>
>>
>>
>
>
>
>



Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/