Search code examples
c++boost-ublas

Encapsulating ublas and overloading the const reference to the operator()


Considering the following toy example, where I declare a class which encapsulates ublas from boost libraries:

#include <boost/numeric/ublas/matrix_sparse.hpp>
#include <iostream>

namespace ublas = boost::numeric::ublas;

class UblasEncapsulated {
public:
    ublas::compressed_matrix<float>::reference operator()(int i, int j){
        std::cout << "Non const reference" << std::endl;
        MtrUpdated_ = true;
        return mtr_(i, j);
    }

    ublas::compressed_matrix<float>::const_reference operator()(
        int i, int j) const {
        std::cout << "Const reference" << std::endl;
        return mtr_(i, j);
    }

    UblasEncapsulated() { MtrUpdated = false; }

private:
    ublas::compressed_matrix<float> mtr_(3, 3);
    bool MtrUpdated_;
};

int main() {
    UblasEncapsulated foo;

    foo(2, 0) =  1.0f;
    float const foo_float = foo(2, 0);

    return 0;
}

I was expecting the output

Non constant reference
Constant reference

But I got

Non constant reference
Non constant reference

What am I doing wrong? How can I properly track when mtr_ could have its values changed?


Solution

  • foo is non-const, so the non-const version of foo.operator() will be called. It doesn't matter how the value it returns is used.

    If you really want to know that MtrUpdated_ is only set true if an element is actually assigned to, you will need to use a proxy class:

    class UblasEncapsulated {
    public:
        class proxy {
        public:
            proxy(UblasEncapsulated* ptr, int i, int j)
                : ptr_(ptr), i_(i), j_(j)
            {}
    
            proxy& operator=(float f) {
                ptr_->MtrUpdated_ = true;
                ptr_->mtr_(i_, j_) = f;
                return *this;
            }
    
            operator float() {
                return ptr_->mtr_(i_, j_);
            }
    
        private:
            UblasEncapsulated* ptr_;
            int i_;
            int j_;
        };      
    
        proxy operator()(int i, int j) {
            return proxy(this, i, j);
        }
    
        ublas::compressed_matrix<float>::const_reference operator() (int i, int j) const {
            return mtr_(i, j);
        }
    
        UblasEncapsulated()
            : mtr_(3, 3),
              MtrUpdated_(false)
        {}
    
    private:
        ublas::compressed_matrix<float> mtr_;
        bool MtrUpdated_;
    };
    

    Live Demo

    Note that you should avoid using a proxy class if you can get away with it since it doesn't play nicely with things like auto or template argument deduction.