1
votes

In my specific case, I have a base class 'Base', with a data member 'A_var'. I'd like that any derived classes only have const access to that data member, in a syntactically equal fashion to the 'Base' class.

If it's protected or private, then derived classes have full or no access, respectively. I could make it private, and make a protected function that returns a const reference, but then the access would be syntactically different.

class Base {
protected:
    const type_t& A() const {return A_var;}
private:
    type_t A_var;
    void f();
};

class Derived : public Base{
public:
    void g();
};

//access in Base class
void Base::f() {
    type_t value = A_var;
    A_var = value;
}

//access in Derived class
void Derived::g() {
    type_t value = A();
    A() = value; //Error, const reference; good
}

Overloading 'A()', as below, also doesn't work, because the 'Derived' class calls the private non-const 'A()'.

protected:
    const type_t& A() const {return A_var;}
private:
    type_t& A() {return A_var;}

The small difference may not seem like a big deal, but in my code there are various macros that start with access to that data member. As such, I have to have different macros for the 'Base' class and derived classes, which disrupts the flow of the code, both reading and writing.

Update: To clarify, the issue is one of making the access in the derived and base classes the same, syntactically. That is, for instance, that I could call a function f(), and have it return a non-const reference when called in the base class, but a const reference when called in a derived class. The motivation is to make the forced const access in derived classes seamless. I realize there may not be a way to do this, but I asked just in case.

Update: To present a real example (there are 2-3 of such cases), this is used a lot in the code:

 test_files_var.current()->current_test()

I replaced that with a

 #define TEST() test_files_var.current()->current_test()

because the derived class would access test_files_var through a different function/member, i.e. testFiles(), I have to have a second definition of TEST(), i.e. DTEST(). The problem is given more by the number of times the 'macros' are used, than by how many of them there are.

5
"but in my code there are various macros ..." That might be the root case of a serious design flaw. Avoid macros. - πάντα ῥεῖ
Wouldn't this violate the Liskov substitution principle? - Vlad Feinstein
@πάνταῥεῖ Perhaps you are right. However, they aren't exactly long macros, and "various" may be pushing it. To present a real example (there are 2-3 of these), this is used a lot in the code 'test_files_var.current()->current_test()'. I replaced that with a #define TEST() test_files_var.current()->current_test(). Because the derived class would access test_files_var through a function, i.e. testFiles(), I have to have a second definition of TEST(), i.e. DTEST(). The problem is given more by the number of times the 'macros' are used, than by how many of them there are. - Ramon
@VladFeinstein I just quickly read a definition of Liskov substition principle, so take my answer with a grain of salt, but I don't think it does. The Base class is even abstract, and there are no interface functions in derived classes. - Ramon
@Ramon You probably should introduce this additional information in your question post (instead of comments), to make it clearer what your actual use case is. - πάντα ῥεῖ

5 Answers

1
votes

If I understand your question correctly, you want to give access to the derived classes access to a private variable of the base class but in read only.

In this case, you just have to define a protected constant reference variable and intialize it to the private variable:

class Base {
public: 
    Base() : cA(A_var) { ... }  // to be completed with rule of 3
protected:
    const type_t& cA;
private:
    type_t A_var;
    void f();
};

The access in the derived class uses then the constant reference:

//access in Derived class
void Derived::g() {
    type_t value = cA;
    //cA = value; //Error, const reference: can't assign 
}

Live demo

1
votes

Where is no simple built-in solution. But a bit of template magic can probably do a trick:

template <class NonConst>
struct Matcher {
    template <class AnyOther>
    static const AnyOther &get(AnyOther &obj) { return obj; }

    static NonConst &get(NonConst &obj) { return obj; }
};

class Base {
public:
    Base() : a_(42) { }
public:
    virtual void Fun() {
        Matcher<Base>::get(*this).A();
    }

    const int &A() const {
        std::cout << "const" << std::endl;
        return a_;
    }
    int &A() {
        std::cout << "no const" << std::endl;
        return a_;
    }
private:
    int a_;
};

class Derived : public Base {
public:
    void Fun() {
        Matcher<Base>::get(*this).A();
    }
};

int main(int argc, const char * argv[]) {
    Derived d;
    d.Fun();

    Base b;
    b.Fun();
    return 0;
}

The code above will output: const no const.

So in both Fun functions you have essentially the same access pattern which you could wrap in a macros if you need.

0
votes

You may change your Base class to

class Base {
public:
    Base() : A_cref(A_var) {}
private:
    type_t A_var;
    void f();
protected:
    const type_t& A_cref;
};

with the extra member overhead.

0
votes

If the macros are usable from the derived classes, they can only need const access - so the macros can use the protected const access function.

Macros that are going to modify the variable will have to use the private variable, and will only be usable in the base class.

0
votes

It appears that you problems will go away if you replace your TEST() macro with the public function test() in the base class:

class Base
{
public:
    void test() { test_files_var.current()->current_test() }
private:
    type_t test_files_var;
};