-1
#include <cstdio>

struct Settings
{
  int i1, i2;
  Settings(int i1, int i2) : i1(i1), i2(i2) {}

  struct GeneralSettings
  {
    int gi1, gi2;
  } static gs;

  void do_something() const
  {
    printf("%d %d %d %d\n", i1, i2, gs.gi1, gs.gi2);
  }
};

Settings::GeneralSettings Settings::gs;

int main()
{
  Settings s1(0,1);
  Settings s2(1,0);

  s1.gs.gi1 = 1;         // I would like to access GeneralSettings like this only!
  Settings::gs.gi2 = 1;  // Can i prevent global access like this?

  s2.do_something();

  return 0;
}

Please see code above and comments. Besides making Settings::gs private with accessors/mutators, are there other ways to restrict access to Settings::gs so that it can be accessed through Settings objects only? The way it is, any function can access Settings::gs whether it has access to a Settings object or not. Settings::gs is essentially a global object.

Community
  • 1
  • 1
u17
  • 2,776
  • 4
  • 31
  • 43

3 Answers3

3

I still don't get the point of doing this but you could use a public reference to a private static member (I would not advise doing that by the way):

struct A {
private:
    struct B { int x, y; } static _b;

public:
    // c++11 initialization, prior you need to initialize b in the 
    // constructor of A
    B &b{_b};
};

A::B A::_b{0, 0};

Then:

int main() {
    A a1, a2;

    std::cout << a2.b.x << " ";
    a1.b.x = 4;
    std::cout << a2.b.x << std::endl;
}

Output:

0 4

As pointed out by @Niall in the comment, having a reference as an attribute will delete the default assignment operators:

A a1, a2;
a2 = a1; // copy assignment is implicitly deleted

But if you need it, you could always create your own since you do not need to update b:

struct A {
private:
    struct B { int x, y; } static _b;
public:
    B &b{_b};
    A& operator= (A const&) {
        // ok, no need to update this->b!
        return *this;
    }
};

A a1, a2;
a1 = a2; // ok
Holt
  • 36,600
  • 7
  • 92
  • 139
  • Interesting alternative. The `B& b{_b};` effectively makes the `A` un-assignable (the assignment operator is implicitly deleted). You could make it a pointer `B* b{&_b};` but that in turn suffers the drawback that it can be set to `nullptr` (but at least limited to the current object instance). Wrappers can be written/used to limit or permitted the required semantics of the public member (`b`) (e.g. `std::reference_wrapper`, but ultimately control is still exercised though a function of some sort (e.g. `reference_wrapper::get()`). – Niall Apr 26 '16 at 11:17
  • @Niall The `B& b{_b}` deletes the default assignment operator but it does not stop you from creating your own if you need it (and since you don't need to assign to `b` in this case it should be ok). – Holt Apr 26 '16 at 11:46
  • That is true. Maybe add that to the answer for clarity. – Niall Apr 26 '16 at 11:52
1

Besides making Settings::gs private with accessors/mutators, are there other ways to restrict access to Settings::gs so that it can be accessed through Settings objects only?

Not really (assuming you want the control when "accessed through"), that is pretty much the point of the private and what accessors are meant for; that being to provide access to the internals (possibly checked) for both instance and private static data.

You can vary the syntax to get the desired semantics (pointers, references, const and non-const), but ultimately once a member variable is public (or accessed via a public member, e.g. a pointer or reference), you surrender a certain amount of control. Control here is achieved in the function, be that a member function or an external utility/helper function.

I'm not sure of the intent here. It depends on what you mean by "accessed through".

If it the just the syntax you are after, this answer (for the current question) provides a solution.

If it is more control, you can explore the feasibility of creating a utility class that provides a custom operator*() and operator->(), thus you could "mimic" a smart pointer (in the absence of a "smart reference"). I'm not sure this will provide the syntax you are after.

You could also explore the pimpl pattern/idiom (an opaque pointer), it doesn't answer the question, but presents an alternative design that could be better at solving the problem you have.

Community
  • 1
  • 1
Niall
  • 30,036
  • 10
  • 99
  • 142
-1

I can think of a couple of options, though, like all singletons these would not be thread safe:

  1. You could just use a public reference to avoid the accessors/mutators:
struct Settings {
private:
    struct GeneralSettings {
        int gi1, gi2;
    } static _gs;
public:
    int i1, i2;
    GeneralSettings& gs;
    Settings(int i1, int i2) : i1(i1), i2(i2), gs(_gs) {}

    void do_something() const {
        printf("%d %d %d %d\n", i1, i2, gs.gi1, gs.gi2);
    }
};
  1. You could actually make it a full on singleton, though this would require an accessor, though not a mutator, and it would cost you your const:
struct Settings {
    struct GeneralSettings {
        int gi1, gi2;
    };

    GeneralSettings& gs() {
        static GeneralSettings gs;

        return gs;
    }

    int i1, i2;
    Settings(int i1, int i2) : i1(i1), i2(i2) {}

    void do_something() {
        printf("%d %d %d %d\n", i1, i2, gs().gi1, gs().gi2);
    }
};
Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288