Situation
I want to implement the Composite pattern:
class Animal
{
public:
virtual void Run() = 0;
virtual void Eat(const std::string & food) = 0;
virtual ~Animal(){}
};
class Human : public Animal
{
public:
void Run(){ std::cout << "Hey Guys I'm Running!" << std::endl; }
void Eat(const std::string & food)
{
std::cout << "I am eating " << food << "; Yummy!" << std::endl;
}
};
class Horse : public Animal
{
public:
void Run(){ std::cout << "I am running real fast!" << std::endl; }
void Eat(const std::string & food)
{
std::cout << "Meah!! " << food << ", Meah!!" << std::endl;
}
};
class CompositeAnimal : public Animal
{
public:
void Run()
{
for(std::vector<Animal *>::iterator i = animals.begin();
i != animals.end(); ++i)
{
(*i)->Run();
}
}
// It's not DRY. yuck!
void Eat(const std::string & food)
{
for(std::vector<Animal *>::iterator i = animals.begin();
i != animals.end(); ++i)
{
(*i)->Eat(food);
}
}
void Add(Animal * animal)
{
animals.push_back(animal);
}
private:
std::vector<Animal *> animals;
};
The Problem
You see, for my simple requirement of the composite pattern, I end up writing a lot of the same repeating code iterating over the same array.
Possible solution with macros
#define COMPOSITE_ANIMAL_DELEGATE(_methodName, _paramArgs, _callArgs)\
void _methodName _paramArgs \
{ \
for(std::vector<Animal *>::iterator i = animals.begin(); \
i != animals.end(); ++i) \
{ \
(*i)->_methodName _callArgs; \
} \
}
Now I can use it like this:
class CompositeAnimal : public Animal
{
public:
// It "seems" DRY. Cool
COMPOSITE_ANIMAL_DELEGATE(Run, (), ())
COMPOSITE_ANIMAL_DELEGATE(Eat, (const std::string & food), (food))
void Add(Animal * animal)
{
animals.push_back(animal);
}
private:
std::vector<Animal *> animals
};
The question
Is there a way to do it "cleaner" with C++ meta-programming?
The harder question
std::for_each
has been suggested as a solution. I think our problem here is a specific case of the more general question, let's consider our new macro:
#define LOGGED_COMPOSITE_ANIMAL_DELEGATE(_methodName, _paramArgs, _callArgs)\
void _methodName _paramArgs \
{ \
log << "Iterating over " << animals.size() << " animals"; \
for(std::vector<Animal *>::iterator i = animals.begin(); \
i != animals.end(); ++i) \
{ \
(*i)->_methodName _callArgs; \
} \
log << "Done" \
}
Looks like this can't be replaced by for_each
Aftermath
Looking at GMan's excellent answer, this part of C++ is definitely non-trivial. Personally, if we just want to reduce the amount of boilerplate code, I think macros probably is the right tool for the job for this particular situation.
GMan suggested std::mem_fun
and std::bind2nd
to return functors. Unfortunately, this API doesn't support 3 parameters (I can't believe something like this got released into the STL).
For illustrative purpose, here're the delegate functions using boost::bind
instead:
void Run()
{
for_each(boost::bind(&Animal::Run, _1));
}
void Eat(const std::string & food)
{
for_each(boost::bind(&Animal::Eat, _1, food));
}