Readonly class variable (tensor) c++

Hi!

Unfortunately c++ doesn't have a dedicated command to define a readonly variable. Usually we solve the problem like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class A
{
    double _x; // Private variable, it can modify in this class directly    

public:
    A() : x(_x) // Bind reference variable x to _x
    {
    }

    void setX(double x)
    {
        this->_x = x;
    }

    const double &x; // Constant variable
};

// Usage:

int main()
{
    A a;

    a.setX(50);

    cout << a.x << endl; // Correct

    a.x = 12; // Error !

    return 0;
}

What I need is do the same, but _x should be a tensor (in my specific case a 3x3 matrix). _x[3][3].
How can I do it?

Thanks
Last edited on
Can't you just use a getter function?
You could make a 3x3 matrix class (since that is what you want) and then use that in your code above in place of double.
1
2
3
4
5
6
class MyClass
{
    int my_private_var;
public:
    int const &publicVar = my_private_var; //read-only variable
};
Unfortunately c++ doesn't have a dedicated command to define a readonly variable.

const management is quite a big deal in C++. In fact, all the const stuff in C was retrofitted from C++.

You really shouldn't use public data in a class. Data shouldn't be part of the interface definition of an object, interface, abstract data type, or any other kind information hiding thing.

To complicate things, you've defined a reference. They can't be re-bound.

Perhaps if you described what effect you want to achieve, we can be more helpful.
Many thanks for the answers!
I can't use a getter function because that would imply the modification of the entire code.


In fact, what I what is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>
#include <complex>
#include "particle.h"

using namespace std;

typedef complex<double> dcmplx;


class A
{
private:
    double x;
    dxmplx _var[3][3];
    
public:
    A() : var(_var),x // Bind reference variable x to _x
    {
        x = 2;
        var[0][0]=1;
        var[1][1]=1;
        var[2][2]=1;
    }
    
    void setX(dcmplx var)
    {
        this->_var = var;
    }
    
    const dcmplx &var; // Constant variable
    const double &x;
};

int main(int argc, const char * argv[])
{
    Particle part;
    
    part.set_positions(3,5.5,1);
    cout << "part "<< part.y << endl;
    return 0;
}


In fact I could use private variables, but for call a variable (if defined as readonly), I can do it by write the A.x and not A.x().
In fact I could use private variables, but for call a variable (if defined as readonly), I can do it by write the A.x and not A.x().

What does that buy you other than headaches with maintenance?
;)

The code is a very old code with many parts (some of them in fortran 77) and have 340 000 lines!
I believe that more than 30 persons already touch the code!
That's the problem.
> What I need is do the same, but _x should be a tensor (in my specific case a 3x3 matrix). _x[3][3].
> How can I do it?

Wrap the tensor in a struct?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>
#include <complex>

struct tensor
{
    using array_t = std::complex<double>[3][3] ;
    array_t t {} ;

    operator array_t& () { return t ; }
    operator const array_t& () const { return t ; }
};

class A
{
    private:
        double x_impl = 2 ;
        tensor var_impl ;

    public:
        A()
        {
            x_impl = 2;
            var_impl[0][0]=1;
            var_impl[1][1]=1;
            var_impl[2][2]=1;
        }

        void set_var( const tensor& t ) { var_impl = t ; }

        const tensor& var = var_impl ; // in-class member init
        const double& x = x_impl ;
};

int main()
{
    A a ;

    tensor t {} ;
    t[0][0] = { 1, 1 } ;
    t[2][2] = { 0.4, 1.5 } ;

    a.set_var(t) ;
}
Perfect!

Many thanks. Works perfectly!
This compiles. Not sure if it works or if it's what you're looking for.
1
2
3
4
5
6
7
typedef std::complex<double> tensor[3][3];

int main()
{
    tensor x;
    const tensor& rx(x);
}
I think there's a bug in your tests:

1
2
3
4
5
6
7
8
9
10
struct A{
    int x;
    const int& cx = x;
};

A first;
first.x = 0;
A second(first);
second.x = 1;
// second.cx should be still assigned to first.x, and thus it should be 0. 
Hi!

Here is my implemented solution. I'm using armadillo to make the complex operations.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

#ifndef eris_particle_h
#define eris_particle_h

typedef std::complex<double> dcmplx;
using namespace arma;


class Particle{

private:
    double _x, _y, _z; // Note: different name than public, read-only interface
    
    cx_mat _dielectric_tensor;
    
    cx_mat _polarizability_tensor;
    
public:
    
    Particle():x(_x), y(_y), z(_z), dielectric_tensor(_dielectric_tensor), polarizability_tensor(_polarizability_tensor)
    {
        _x = 0;
        _y = 0;
        _z = 0;
        
        _dielectric_tensor.zeros(3,3);
    
        _polarizability_tensor.zeros(3,3);
    }
    
    void set_positions(double, double, double)
    {
        this->_x = x;
        this->_y = y;
        this->_z = z;
    }
    
    void set_dielectric_const(dcmplx eps){
        
        cx_mat temporary;
        temporary.zeros(3,3);
        temporary = eps*temporary.eye();
        
        _dielectric_tensor=temporary;
        
        this->_dielectric_tensor = dielectric_tensor;
    }
    
    void set_dielectric_tensor(cx_mat dielectric_tensor){
        
        this->_dielectric_tensor=dielectric_tensor;
        
    }
    
    void set_polarizability_tensor(cx_mat polarizability_tensor){
        
        this->_polarizability_tensor=polarizability_tensor;
        
    }

    
    const double &x;
    const double &y;
    const double &z;
    const cx_mat &dielectric_tensor;
    const cx_mat &polarizability_tensor;

};


#endif


and the main

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>
#include <complex>
#include <armadillo>
#include "particle.h"

using namespace std;
using namespace arma;

typedef complex<double> dcmplx;

int main(int argc, const char * argv[])
{
    cout << "Armadillo version: " << arma_version::as_string() << endl;
    
    dcmplx eps(2,3);
    
    cx_mat beta = randu<cx_mat> (3,3);
    
    Particle part;
    part.set_dielectric_tensor(beta);
    part.set_positions(3,5.5,1);
    part.set_dielectric_const(eps);

    
    cout << "tensor = " << part.dielectric_tensor << endl;
    
    return 0;
}


Many thanks to all of you for the help.
Are you sure this is correct?
1
2
3
4
5
6
7
8
9
10
void set_positions(double, double, double)
    {
        this->_x = x;
        this->_y = y;
        this->_z = z;
    }
...
const double &x;
    const double &y;
    const double &z;
> Are you sure this is correct?

What is wrong with it?

double _x ; is a modifiable lvalue.
Aliasing the object with const double &x = _x ; does not change the type of the object.


> Unfortunately c++ doesn't have a dedicated command to define a readonly variable.

Didn't want to derail the discussion on the original question earlier.

Not worthwhile for just one or two variables; but if there are many of them in many different classes, we can provide a generic wrapper that controls access.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <iostream>

namespace detail_
{
    template < typename T > struct restricted_
    {
        operator const T& () const { return value ; } // readonly

        protected:
            T value ;

            T& operator()() { return value ; } // writeable by friends

            explicit restricted_( const T& v = T() ) : value(v) {}
            restricted_( const restricted_<T>& ) = default ;
            restricted_( restricted_<T>&& ) = default ;
    };

    template < typename T, typename... F > class allow_ ;

    template < typename T, typename FRIEND >
    struct allow_<T,FRIEND> : virtual restricted_<T> { friend FRIEND ; };

    template < typename T, typename FRIEND, typename... MORE_FRIENDS >
    struct allow_<T,FRIEND,MORE_FRIENDS...> : allow_<T,FRIEND>, allow_<T,MORE_FRIENDS...> {} ;
}

template < typename T, typename... FRIENDS > struct readonly : detail_::allow_<T,FRIENDS...>
{ readonly( const T& v = T() ) : detail_::restricted_<T>(v) {} } ;

struct B ;

struct A
{
    // readable by everyone
    // writeable only by members and friends odf classes A and B
    readonly< int, A, B > x = 23 ;

    void foo() { x() = x + 5 ; }
};

struct B
{
    void foo( A& a ) { { a.x() = a.x * a.x ; } } // fine, B is a friend of a.x
};

struct X
{
    int foo( const A& a ) { return a.x ; } // fine; readable

    void bar( A& a, int v ) { a.x = v ; } // *** error: use of deleted function

    void baz( A& a, int v ) { a.x() = v ; } // *** error: a.x() is inaccesssible

};

http://coliru.stacked-crooked.com/a/6d9d0d943e39e0f0
Can somebody confirm this solution has the bug I stated above?
Doesn't copying the class re-bind the reference to the ORIGINAL class?
JLBorges wrote:
What is wrong with it?

The set_positions function takes un-named parameters, so unless I'm missing something the function does nothing.
1
2
3
4
5
6
void set_positions(double, double, double)
    {
        this->_x = x;
        this->_y = y;
        this->_z = z;
    }
You're right. It should be:

1
2
3
4
5
6
void set_positions(double x, double y, double z)
    {
        this->_x = x;
        this->_y = y;
        this->_z = z;
    }

> The set_positions function takes un-named parameters,
> so unless I'm missing something the function does nothing.

Yes. Thanks.
Topic archived. No new replies allowed.