简体   繁体   中英

Is it possible to chose at runtime function that can access private members of a class?

The topic might be a it confusing but I believe the concept I am after is pretty easy to grasp.

I have a class, that has some private data. I would like to provide a way to specify a function from the outside of a class that can operate on this data, during the run time. However, I would not like to expose the data to the public.

So far I had something like this:

#include <iostream>
using namespace std;

class Base{
    double priv = 0;
public:
    Base(double initial) : priv(initial) {};
    double get_private(){ return priv; };

    static void mix_privates(Base& b1, Base& b2);
};

void Base::mix_privates(Base& b1, Base& b2){
  b1.priv = b2.priv = (b1.priv+b2.priv)/2;
}

//void Base::mix_privates(Base& b1, Base& b2){
//  b1.priv = b2.priv = max(b1.priv, b2.priv);
//}

int main() {
    Base test1(5), test2(10);
    Base::mix_privates( test1, test2 );

    cout << test1.get_private() << '\n';
    cout << test2.get_private() << '\n';
    return 0;
}

Up to this point I did not need to change or choose the implementation so I declared the function as a static member, but now I like in the example above I can provide several different implementations of mix_privates . Moreover I have many objects similar to Base and can easily generalize mix_privates for them, for example via template.

Ideally I would like to be able to do is to define few mix_privates implementations. For example like this:

template<typename BaseLike>
void mix_privates_max(BaseLike& b1, BaseLike& b2){
  b1.priv = b2.priv = max(b1.priv, b2.priv);
}
template<typename BaseLike>
void mix_privates_avg(BaseLike& b1, BaseLike& b2){
  b1.priv = b2.priv = (b1.priv + b2.priv)/2;
}

And then in main assign a concrete implementation to some general name, eg:

std::function<void(Base&, Base&)> mix_privates = mix_privates_avg<Base>

and then use mixer through general name mix_privates(test1, test2) through out rest of the program.

There is no problem if mix_privates uses only public members and attributes, and I got it done. But I would like to be able mess with private data. There is also no point in exposing it to public except for this functionality, so I would like to avoid it.

I was thinking about using friendship, but it is not inheritable, which is a drawback, and not transferable, so I think I cannot specify a friend function that would act like a proxy.

Is there a way to implement this or something similar in c++11?

What about providing a friend class that defines protected getter/setter methods? That way only derived classes of the friend can access the internals. Eg:

class Base {
   friend class Access;
   ...
   private: double priv;
};

class Access {
protected:
   double get_private(Base &b) { return b.priv; }
   void set_private(Base &b, double priv) { b.priv = priv; }

public:
   virtual void mix_privates(Base &b1, Base &b2) = 0;
};

class AccessMax : public Access {
    void mix_privates(Base &b1, Base &b2) { set_private(b1, max(get_private(b1), get_private(b2)); }
};

Contrary to the other suggestions, I would consider making mix_privates a template that takes a function that will do the actual mix:

template <typename Functor>
void Base::mix_privates(Base& b1, Base& b2, Functor f) {
   b1.priv = b2.priv = f(b1,b2);
}

Then you can use it:

Base b1(..), b2(..);
Base::mix_privates(b1,b2,
                   [](double x, double y) { return (x+y)/2; });  // average
Base::mix_privates(b1,b2,
                   &std::max<double>);                           // max

You could create a Mixer class which implements a function mix(Base& b1, Base& b2) , and declare an instance of it in Base . Then subclass Mixer to implement the various ways to mix. Assign an instance of your desired Mixer subclass to the instance declared in Base , and then implement mix_privates to call mix of the instance.

For example:

class Mixer;

class Base{
  double priv = 0;
public:
  ...
  static Mixer* mixer;
  static void mix_privates(Base& b1, Base& b2) {
    mixer->mix(b1, b2);
  };
};

class Mixer {
public:
  void mix(Base& b1, Base&b2) = 0;
};

class MixAverage  : public Mixer {
public:
  void mix(Base& b1, Base&b2) {
    b1.priv = b2.priv = (b1.priv+b2.priv)/2;
  };
}

main {
  ...
  Base b1, b2;
  Base::mixer = new MixAverage();
  ...
  Base::mix_privates(b1, b2);
  ...
}

The problems with this is (as pointed out), the Mixer and its subclasses cannot access the private members of Base . So, you could make Mixer a friend, and implement methods that can then be used by the subclass to get access to the private member of base. For example:

class Base{
  double priv = 0;
public:
  ...
  static Mixer* mixer;
  static void mix_privates(Base& b1, Base& b2) {
    mixer->mix(b1, b2);
  };
  friend class Mixer;
};

class Mixer {
public:
  void mix(Base& b1, Base&b2) = 0;
  int& priv(Base& b) {
    return b.priv;
  };
};

class MixAverage  : public Mixer {
public:
  void mix(Base& b1, Base&b2) {
    priv(b1) = priv(b2) = (priv(b1)+priv(b2))/2;
  };
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM