【发布时间】:2010-07-22 05:52:39
【问题描述】:
情况
我要实现复合模式:
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;
};
问题
你看,为了我对复合模式的简单要求,我最终编写了很多相同的重复代码来迭代同一个数组。
使用宏的可能解决方案
#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; \
} \
}
现在我可以这样使用它了:
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
};
问题
有没有办法让 C++ 元编程“更干净”?
更难的问题
std::for_each 已被建议作为解决方案。我认为我们这里的问题是更一般问题的一个具体案例,让我们考虑一下我们的新宏:
#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" \
}
这个好像不能用for_each代替
后果
看看 GMan 的出色回答,C++ 的这一部分绝对是不平凡的。就个人而言,如果我们只是想减少样板代码的数量,我认为宏可能是适合这种特殊情况的工作的正确工具。
GMan 建议 std::mem_fun 和 std::bind2nd 返回函子。不幸的是,这个 API 不支持 3 个参数(我不敢相信这样的东西会被发布到 STL 中)。
为了便于说明,下面是使用 boost::bind 的委托函数:
void Run()
{
for_each(boost::bind(&Animal::Run, _1));
}
void Eat(const std::string & food)
{
for_each(boost::bind(&Animal::Eat, _1, food));
}
【问题讨论】:
-
下面的函子解决了更难的问题,函子存储参数,并用容器中的每个对象调用它,然后它可以用任何参数调用每个对象的任何方法。您可以根据需要构建任意数量的函子,它们很简单。
标签: c++ templates metaprogramming