-1

Let's say I have a class MainApp using methods of a dynamic library via an interface FrontEnd

FrontEnd uses internally instances of a class Data (contained in a class BackEnd)

This class Data only contains a member and its accessor, no public method to manipulate the member

Ok now MainApp has no knowledge of Data and can't access it but it needs indirectly to manipulate it

My goal is to create some kind of a reference keeping a pointer to Data with 2 behaviours

  1. forbidding access to Data when in MainApp
  2. Allowing access to Data when in FrontEnd

I listed some solutions like PIMPL idiom, Bridge or Adaptor pattern but the problem is I don't want to use the Reference like an interface but just like an holder

How can I handle it?

Here some code to illustrate:

Data.hpp

struct Data {
    Data(int i):member(i){}
    int getMember();
private :
    int member;
};

BackEnd.hpp

#include "Data.hpp"

struct BackEnd {
    BackEnd(){}
    Data* createData(int i) {
      Data* d = new Data(i);
      mDatas.push_back(d);
      return d;
    }

    void doSomething(Data* d, int param) {
      int r = param+d->getMember();
      /*do other things with d*/
    }
private:
    vector<Data*> mDatas;
};

Reference.hpp

#include //???

struct Reference {
    Reference(Data* d):mData(d){}
private:
    //no access to data->member
    Data* mData;
};

FrontEnd.hpp

#include "Data.hpp"
#include "Reference.hpp"
#include "BackEnd.hpp"

struct FrontEnd {
    Reference* createData(int i) {
      Data* d = mBackEnd->createData(i);
      //conversion Data to Reference
      Reference ref = new Reference(d);
      return ref;
    }

    void doSomething(Reference* ref) {
      //In the Front-End, I want an access to Ref->mData
      Data* d = ref->getData();//Allows access to Ref->mData
      int result = mBackEnd->doSomething(d);
    }
private:
    BackEnd* mBackEnd;
};

MainApp.hpp

//I don't want to reference Data.hpp
#include "Reference.hpp"
#include "FrontEnd.hpp"

struct MainApp {
    Reference* createRef(){ mRef = mFrontEnd->createData(8);}
    void doSomething(){ mFrontEnd->doSomething(mRef); }
private:
    FrontEnd* mFrontEnd;
    Reference* mRef;
    //I want to keep a reference to Data without using Data directly
    //Forbids access to mRef->mData
};
Deduplicator
  • 44,692
  • 7
  • 66
  • 118
codablank
  • 181
  • 2
  • 9

2 Answers2

2

If you want to allow external classes access to internals of another you can set it as a friend.

struct Foo;

struct Bar {
private:
  friend struct Foo;
  int count;
};

struct Foo {
  void doBarStuff(Bar &bar) {
    bar.count = 2;
  }
};

This is a trade off obviously. It is alright to think about the two structs differently, but if they access each others internals they don't have much separation of concern. Use sparingly.

When you do this it is good practice to access the internals via method calls as opposed to manipulating them directly. It may seem like you are adding complexity and you are to some extent. It makes your access intentions clear, though.

struct Foo;

struct Bar {
private:
  friend struct Foo;
  void setCount(int c) {
    if (count < 0)
      throw std::logic_error("out of range");
    count = c;
  }
  int count;
};

struct Foo {
  void doBarStuff(Bar &bar) {
    bar.setCount(2);
  }
};

You wouldn't want that count check littered throughout Foo, for example.

Tom Kerr
  • 10,444
  • 2
  • 30
  • 46
2

Consider using friendship - i.e. make FrontEnd a friend of Reference, this way you can have a protected member in Reference which will allow you to get the Data*. In Reference.h, include FrontEnd.h (to allow friendship), in FrontEnd.h, use Reference as a forward declaration (i.e. don't include the header). Only include the header in the implementation file for FrontEnd. You'll have to move all the definitions (in FrontEnd.h to the implementation file as well.

Now MyApp can happily hold a Reference but Data* can only be got at by FrontEnd.

Nim
  • 33,299
  • 2
  • 62
  • 101
  • Ok it works; what if I want Data to be templated? And Is there any design pattern to do this or am I stuck with the keyword "friend" – codablank Oct 04 '11 at 15:20
  • You can use inheritance in this case - So make `Reference` a base class, and you can have some derived instances which can hold a templated `Data` instance. In `MyApp`, hold a smart pointer to `Reference`, and then use a visitor pattern to do stuff via the `Reference` - is that as clear as mud? :) – Nim Oct 04 '11 at 15:41
  • @Nim: Why not simply make the Reference class also templated? – Mooing Duck Oct 04 '11 at 16:31
  • @Mooing, then you introduce a coupling, you'd have to somehow introduce that type to `MyApp`. – Nim Oct 04 '11 at 16:34