views:

95

answers:

6

I have two methods f(vector<int>& x, ....) and g(DBConn& x, ....) where the (....) parameters are all identical.

The code inside the two methods are completely identical except for one statement where we do different actions based on the type of x:

in f(): we do x.push_back(i)
in g(): we do x.DeleteRow(i)

What is the simplest way to extract the common code into one method and yet have the two different statements?

I am thinking of having a templated functor that overloads operator () (int a) but that seems overkill.

A: 

You could make a function that has the parameters of what you call (...), and this function can implement the logic that is the same in f() and g(). You could then change the implementation of f() and g() to call this new function instead of duplicating the logic. Be careful though if you're doing something duplicated before and after your unique lines. You may need two functions in that case. At any rate I think this would be preferable to having duplicated blocks of code.

Ben Burnett
+6  A: 
common_function(....)
{
}

f(vector<int>x,... )
{
    x.push_back(i);
    common_f(...);
}
g(DBConn& x, ....)
{
    x.DeleteRow(i);
    common_f(...);
}
Arseny
+1. This is usually the simplest way to do it.
Brian
This may work, except that I will need the following format:`common_g()x.push_back(i) common_f()`Not as simple.
Also, you've got an extra set (or two) of parameter pushing.
Skizz
+4  A: 

You could write a simple adapter with two implementations, each calling the desired method of a different class.

class MyInterface {
public:
  virtual doIt(int i) = 0;
}

class VectorImp : public MyInterface {
public:
  vector<int>& v;
  VectorImp(vector<int>& theVector) : v(theVector) {}
  doIt(int i) { x.push_back(i); }
}

class DbImp : public MyInterface {
public:
  DBConn& c;
  VectorImp(DBConn& conn) : c(conn) {}
  doIt(int i) { c.DeleteRow(i); }
}
Péter Török
I prefer this over templates for two reasons. Firstly, it will only generate compiled code for one instance of the f/g function, templates would normally generate compiled code for each template instantiation. Secondly, I see templates as 'do the same thing regardless of type', but this is 'do a different thing depending on type'.
Skizz
I am not going for it as it adds next level of indirection with virtual function call. But still it is valid solution.
Tomek
@Tomek, a real-life solution is always a tradeoff :-)
Péter Török
+1  A: 
template<class T>
struct Adapter;

template<>
struct Adapter<vector<int> >
{
  static void execute(vector<int> &x, int i)
  {
    x.push_back(i);
  }
};

template<>
struct Adapter<DBConn>
{
  static void execute(DBConn &x, int i)
  {
    v.DeleteRow(i);
  }
};

template<class T>
void f(T &t, ...)
{
  ...
  Adapter<T>::execute(t, i);
  ...
}

OR:

template<class T>
struct adapter_traits;

template<>
struct adapter_traits<vector<int> >
{
  typedef void (vector<int>::*PMF)(int);
  static const PMF pmf = &vector<int>::push_back;
}

template<>
struct adapter_traits<DBConn>
{
  typedef void (DBConn::*PMF)(int);
  static const PMF pmf = &DBConn::DeleteRow;
}

template<class T>
void f(T &t, ...)
{
  ...
  (t.*adapter_traits<T>::pmf)(i);
  ...
}

NOTE: I might have some syntax wrong but you get the idea.

Tomek
+1  A: 

Yet another idea:

template<class T>
void f(T &t, void (T::*p)(int), ...)
{
  ...
  (t.*p)(i);
}

void g()
{
  DBConn x;
  vector<int> y;
  f(x, &DBConn::DeleteRow, ...);
  f(y, &vector<int>::push_back, ...);
}
Tomek
+1  A: 

Classic case for a functor:

#include <vector>
#include <DBConn.h>

// T:    The type of the object that is to be manipulated.
// A:    The type of the object that will do the manipulating
//       This may be a functor object or a function pointer.
//
// As this is a template function the template parameters will
// be deduced by the compiler at compile time.
template<typename T,typename A>
void action(T& obj,A const& action/*,....*/)
{
    // Do Stuff
    action(obj,5);
    // Do more Stuff
}

// Functor object
struct MyVectorAction
{
    // Just defines the operator()
    // Make sure it is a const method.
    // This does the unique bit of code. The parameters should be what you pass into action
    void operator()(std::vector<int>& data,int val) const   {data.push_back(val);}
};
void f(std::vector<int>& x)
{
    action(x,MyVectorAction()/*.... Params ....*/);
}


struct MyDBConnAction
{   void operator()(DBConn& data,int val) const   {data.DeleteRow(val);} };
void g(DBConn& x)
{
    action(x, MyDBConnAction());
}

int main()
{
    std::vector<int>    x;

    f(x);
}
Martin York
It would be even better with C++0x and lambdas... You probably can avoid functors then.
Tomek