0

I'm working with some code where I have the following setup.

struct data
{
      void change_safe_member(){}
      void read_data(){}
      void change_unsafe_member(){}
};

struct data_processor
{
     std::shared_ptr<data> get_data(){}
     void return_data(std::shared_ptr<data> my_data)
     {
           my_data->change_unsafe_member(); // ONLY data_processor should call this function.
     }
};

struct client
{
     void foo(std::shared_ptr<data_processor>& my_processor)
     {
           auto my_data = my_processor->get_data();
           my_data->change_safe_member();
           //my_data->change_unsafe_member(); SHOULD NOT BE POSSIBLE TO CALL
           my_processor->return_data(my_data);
     }
};

The change_unsafe_member should only be used internally by the processor so I would like to hide it or disable it for the client. But I don't know of any nice ways of doing this without resorting to ugly casts...

struct internal_data
{
      void change_unsafe_member(){}
};

struct data : public internal_data
{
      void change_safe_member(){}
      void read_data(){}
};

struct data_processor
{
     std::shared_ptr<data> get_data(){}
     void return_data(std::shared_ptr<data> my_data)
     {
           auto internal_data = std::static_pointer_cast<internal_data>(my_data);
           internal_data->change_unsafe_member(); 
     }
};

Anyone know of a good pattern to use in situations like this? Maybe visitor pattern or something similar?

EDIT:

As pointed out in the comments one could declare friend classes, there is however one problem... the following will not work.

struct data
{
      void change_safe_member(){}
      void read_data(){}
private:
      friend class data_processor;
      virtual void change_unsafe_member(){}
};

struct data_decorator : public data
{
      data_decorator(const std::shared_ptr<data>& decoratee) : decoratee_(decoratee){}
      void change_safe_member(){decoratee_->change_safe_member();}
      void read_data(){decoratee_->read_data();}
private:
      virtual void change_unsafe_member()
      {
             std::cout << "Hello!"; // Add functionality
             decoratee_->change_unsafe_member(); // Won't work... compiler error
      }
      std::shared_ptr<data> decoratee_;
};

// Another example
struct data_group_decorator : public data
{
      data_group_decorator (const std::vector<std::shared_ptr<data>>& decoratees) : decoratees_(decoratees){}
      void change_safe_member(){decoratee_->change_safe_member();}
      void read_data(){decoratee_->read_data();}
private:
      virtual void change_unsafe_member()
      {
             for(size_t n = 0; n < decoratees_.size(); ++n)
                   decoratees_[n]->change_unsafe_member(); // Won't work... compiler error
      }
      std::vector<std::shared_ptr<data>> decoratees_;;
};
ronag
  • 49,529
  • 25
  • 126
  • 221
  • 1
    make it private and declare `data_processor` a friend class ? – FrankH. Dec 14 '10 at 11:13
  • That would indeed work in this simplified case, I'll update my question with a more complicated example in a moment. – ronag Dec 14 '10 at 11:14
  • 1
    You can also specify `friend void data_processor::return_data(.....)` to restrict "friendlyness" to a particular member of `data_processor` only if all encompassing love is too much. – FrankH. Dec 14 '10 at 11:17
  • Your example is confusing - why are you both using embedding (via inheritance) and pimpl (by having the explicit `decoratee_` member) ? – FrankH. Dec 14 '10 at 11:29
  • Tried to clarify example. Also please see, http://en.wikipedia.org/wiki/Decorator_pattern. – ronag Dec 14 '10 at 11:31
  • The example compiles for me if I use `friend data_decorator` instead of `friend data_processor` - you don't _have_ the latter anywhere in your code, the member functions calling the private innards are in `data_decorator`. – FrankH. Dec 14 '10 at 11:43
  • Yes, but using a friend declaration for every such class is not good design, a lot of coupling and dependencies... in my application it would give around ~8 friend declarations... – ronag Dec 14 '10 at 11:50

2 Answers2

1

Your last example looks like what you're really asking for is inherited friendship; i.e. you want to have a hierarchy of decorator - derived classes which are all allowed to call the private member function in data. That's answered (with "generally no") elsewhere:

Why does C++ not allow inherited friendship?

Polymorphism might provide some relief in your specific scenario, make class data_decorator an "almost pure" virtual base class, with the only nonvirtual member being a protected change_unsafe_member(), and make that in turn a friend of class data. All decorators would inherit from data_decorator, and call its protected nonvirtual member.

Community
  • 1
  • 1
FrankH.
  • 17,675
  • 3
  • 44
  • 63
1

You can make this happen with inheritance.

struct Y;
struct X {
    friend struct Y;
private:
    change_unsafe_member() {}
};
struct Y {
protected:
    change_unsafe_member(X& x) { x.change_unsafe_member(); }
};
struct some_other : Y {
    X x;
    change_safe_member() { change_unsafe_member(x); }
};

Any class that inherits from Y can gain X's friendship for any functions that Y defines as effectively forwards from X.

Puppy
  • 144,682
  • 38
  • 256
  • 465