More elegant way to make a C++ member function change different member variables based on template p
- by Eric Moyer
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?