views:

88

answers:

1

Today, I wrote some code that needed to add elements to different container variables depending on the type of a template parameter. I solved it by writing a friend helper class specialized on its own template parameter which had a member variable of the original class. It saved me a few hundred lines of repeating myself without adding much complexity. However, it seemed kludgey. I would like to know if there is a better, more elegant way.

The code below is a greatly simplified example illustrating the problem and my solution. It compiles in g++.

#include <vector>
#include <algorithm>
#include <iostream>

namespace myNS{

  template<class Elt>
  struct Container{
    std::vector<Elt> contents;
    template<class Iter>
    void set(Iter begin, Iter end){
      contents.erase(contents.begin(), contents.end());
      std::copy(begin, end, back_inserter(contents));
    }
  };


  struct User;

  namespace WkNS{
    template<class Elt>
    struct Worker{
      User& u;

      Worker(User& u):u(u){}

      template<class Iter>
      void set(Iter begin, Iter end);
    };
  };

  struct F{ int x; explicit F(int x):x(x){} };
  struct G{ double x; explicit G(double x):x(x){} };

  struct User{
    Container<F> a;
    Container<G> b;

    template<class Elt>
    void doIt(Elt x, Elt y){
      std::vector<Elt> v; v.push_back(x); v.push_back(y);
      Worker<Elt>(*this).set(v.begin(), v.end());
    }

  };


  namespace WkNS{
    template<class Elt> template<class Iter>
    void Worker<Elt>::set(Iter begin, Iter end){
      std::cout << "Set a." << std::endl;
      u.a.set(begin, end);
    }

    template<> template<class Iter>
    void Worker<G>::set(Iter begin, Iter end){
      std::cout << "Set b." << std::endl;
      u.b.set(begin, end);
    }
  };

};

int main(){
  using myNS::F; using myNS::G;
  myNS::User u;
  u.doIt(F(1),F(2));
  u.doIt(G(3),G(4));
}

User is the class I was writing.

Worker is my helper class. I have it in its own namespace because I don't want it causing trouble outside myNS.

Container is a container class whose definition I don't want to modify, but is used by User in its instance variables.

doIt<F> should modify a. doIt<G> should modify b.

F and G are open to limited modification if that would produce a more elegant solution. (As an example of one such modification, in the real application F's constructor takes a dummy parameter to make it look like G's constructor and save me from repeating myself.)

In the real code, Worker is a friend of User and member variables are private. To make the example simpler to write, I made everything public. However, a solution that requires things to be public really doesn't answer my question.

Given all these caveats, is there a better way to write User::doIt?

+1  A: 

After reading Emile Cormier's comment, I thought of a way to both keep from repeating myself and to also eliminate the Worker class: make two trivial non-template doIt functions for F and G and have each call a third templated doIt function with the variable to change passed as a parameter. Here is the modified code.

#include <vector>
#include <algorithm>
#include <iostream>

namespace myNS{

  template<class Elt>
  struct Container{
    std::vector<Elt> contents;
    template<class Iter>
    void set(Iter begin, Iter end){
      contents.erase(contents.begin(), contents.end());
      std::copy(begin, end, back_inserter(contents));
    }
  };

  struct F{ int x; explicit F(int x):x(x){} };
  struct G{ double x; explicit G(double x):x(x){} };

  struct User{
    Container<F> a;
    Container<G> b;

    template<class Elt>
    void doIt(Elt x, Elt y, Container<Elt>& cont, const char*name){
      std::vector<Elt> v; v.push_back(x); v.push_back(y);
      cont.set(v.begin(), v.end());
      std::cout << "Set " << name << std::endl;
    }

    void doIt(F x, F y){ doIt(x,y,a,"a"); }

    void doIt(G x, G y){ doIt(x,y,b,"b"); }

  };

}

int main(){
  using myNS::F; using myNS::G;
  myNS::User u;
  u.doIt(F(1),F(2));
  u.doIt(G(3),G(4));
}
Eponymous