views:

318

answers:

3

Hi,

suppose you have two (or more) classes with private member vectors:

class A {  
private:  
 std::vector<X> priv_vec;  
public:  
 //more stuff  
}  

class B {  
private:  
 std::vector<Y> priv_vec;  
public:  
 //more stuff  
}

and you have a functor-class which has a state and works on a generic vector (does sorting or counts elements or something like that). The state of the functor is initialized by the first vector the functor is working on. If the functor is applied to another vector later, it will change its behavior depending on the state (sorts in the same way or trims the second vector after as many elements as the first one, etc)

What is the best way to implement such a functor (desgin-pattern or functional interface?) without exposing the private vectors to the other classes or the user of the classes?

for example: The user would like to initialize this functor with an object of class A and then use this initialized functor for one or more objects of class B. The user isn't able (and shouldn't be) to use the private vectors directly as function-arguments for the functor.

+1  A: 

Hum, first, beware on states in functors.

Most STL implementation of the algorithms may copy your functors around, therefore you generally have to extract the state in an outer structure.

Now, for the application of functors, well it is simple: have your classes declare a template member function!

class A
{
public:
  template <class Functor>
  Functor Apply(Functor f);

private:
};

class B
{
public:
  template <class Functor>
  Functor Apply(Functor f);
};

// Usage
MyFunctor f;
A a;
B b;
b.Apply(a.Apply(f));

As for the functor, if you need state:

// Alternative 1
class FunctorState {};

class Functor
{
public:
  Functor(FunctorState& state): m_state(state) {}

  // some operator()(...)

private:
  FunctorState& m_state;
};

// Alternative 2
class Functor
{
  struct FunctorState {};
public:
  Functor(): m_state(new FunctorState) {}

  // some operator()(...)

private:
  boost::shared_ptr<FunctorState> m_state;
};

This way, copies of the Functor all points to the same FunctorState instance. Just choose depending if you wish to actually access the state from outside the class or not.

Matthieu M.
thank you for your answer(s) - made my day a lot easier!
Dane
A: 

Looks like a problem of importing policies from an object of class A and applying them to objects of class B, the only difference being, all of this is done at runtime (as opposed to typical policy-based design). This begs the question, are these policies proprietary to class A or can we isolate them and pass them around as required? That ought to simplify the deisgn.

dirkgently
A: 

A solution based on templates.

#include <iostream>
#include <string>
#include <vector>

// Wraps up a vector of type T and exposes a
// simple interface to access it.
template <class T>
class A
{  
public:  
  A(const std::vector<T>& v) : priv_vec(v) { }
  virtual size_t count() const { return priv_vec.size(); }
  virtual T operator[](size_t index) const { return priv_vec[index]; }
private:  
  std::vector<T> priv_vec;  
};  

// A sample functor with state.
// The state is the size and current index of the vector object.
class Functor
{
public:
  Functor() : _size(0), _index(0) { }

  // Prints the element at the current index.
  // If the index exceeds size, it is reset to 0.
  template <class T>
  void operator()(const A<T>& b)
  {
    if (_size == 0) _size = b.count();
    if (_index >= _size) _index = 0;
    std::cout << b[_index++] << '\n';
  }

private:
  size_t _size;
  size_t _index;
};

int
main()
{
  // Some tests.

  std::vector<int> int_vec;
  int_vec.push_back(1);
  int_vec.push_back(2);
  int_vec.push_back(3);
  A<int> a(int_vec);

  std::vector<std::string> str_vec;
  str_vec.push_back("aaaa");
  str_vec.push_back("bbbb");
  str_vec.push_back("cccc");
  A<std::string> b(str_vec);

  Functor f;
  f(a); // 1
  f(b); // bbbb
  f(a); // 3
  f(a); // 1
  f(a); // 2
  f(b); // cccc

  return 0;
}
Vijay Mathew
my classes A and B aren't similar enough to make a generic class out of them - but that could be circumvented - the problem here is: if I want my functor to be able to change the given vectors (for example: sort them), then such an interface would expose the private data (or am I missing something here?)
Dane
@Dane Please keep in mind that my code just demonstrates an idea. If you let your functor sort the vector in-place, then that is really bad design. Probably you might still want to do it for the sake of performance but ideally the functor should retrieve the elements from the vector (using operator[] in class A) and construct a new, sorted vector. (kinda functional programming!)
Vijay Mathew