答案 0 :(得分:1)
哇。
通过阅读慢慢地建议您最终想要的是一系列函数调用,您可以为不同的操作选择具有相同参数(但实现不同)的不同函数,并为正确选择正确的函数情况下。
如果是这种情况,那么你正在寻找函数指针。试试this tutorial。
您应该能够使用带有参数集的函数指针,并根据您的需要将其指向正确的函数。你也不需要一个函数指针数组 - 任何与定义匹配的函数都应该这样做。在本教程中,声明一个像这样的函数指针:
int (TMyClass::*functptr)(classname, int, int) = NULL; // C++
然后再分配:
this.functptr = &TMyClass::doitthisway;
答案 1 :(得分:1)
您可以使用函数指针或仿函数存储函数。虽然C ++本身不支持变体类型,但您必须在那里使用自定义解决方案。
一种可能性是使用Boost.Any(或更好,Boost.Variant如果您只使用一组固定的类型):
typedef void (*Function)(Object*, const std::string&, boost::any&);
std::vector<Function> functions;
给定一些功能:
void f(Object* obj, const std::string& name, boost::any& value) {
// ...
}
你可以存储和调用它类似于你的例子:
functions.push_back(&f);
functions[0](obj, "x", boost::any(500));
要使用声明性语法,我会想到三个选项:
要进行合成,您可以使用Boost.Bind或类似操作的自定义对象:
struct Operation {
virtual ~Operation() {}
virtual bool operator()(Object&) = 0;
};
template<class T>
struct GreaterThen : Operation {
typedef T Object::*Member;
Member member;
const T value;
CompareGT(Member member, const T& value) : member(member), value(value) {}
bool operator()(Object& obj) { return (obj.*member > value); }
};
template<class T>
struct SetTo : Operation {
typedef T Object::*member;
Member member;
const T value;
SetTo(Member member, const T& value) : member(member), value(value) {}
bool operator()(Object& obj) { obj.*member = value; return true; }
};
现在我们可以构建操作列表:
typedef std::vector<Operation*> OpList;
OpList operation;
operations.push_back(new GreaterThen<int>(&Object::Frame, 64));
operations.push_back(new SetTo<int>(&Object::State, 1));
我们可以使用辅助函数来避免必须指定模板类型:
template<class T>
Operation* opGreaterThen(T Object::*mem, const T& val) {
return new GreaterThen<T>(mem, val);
}
假设SetTo
使用Boost.Assign并使用{{3}}的类似帮助,则上述内容变为:
OpList operations = boost::assign::list_of
(opGreaterThen(&Object::Frame, 64))
(opSetTo (&Object::State, 1));
执行操作如下:
OpList::iterator it = operation.begin();
for( ; it != operations.end(); ++it) {
Operation& op = *it; // just for readability
if(!op(someObject)) break; // stop if operation returns false
}
答案 2 :(得分:1)
虽然有可能(虽然很痛苦)拥有一个任意类型的数组,但你几乎从不需要它,因为你必须知道某些东西关于它在哪里做任何有趣的事情:例如,您的'TL; DR'示例看起来像:
struct AIRule {
// Can only handle comparing ints, see later for more general solution.
typedef bool compare_type(AIObject*, AIObject::*int, int);
compare_type* compare;
AIObject* object;
AIObject::int* member;
int comparand;
};
所以现在你可以这样做:
bool ai_equal(AIObject* object, AIObject::int* member, int comparand) {
return object->*member == comparand;
}
...
ai[n].compare = &ai_equal;
ai[n].object = some_object;
ai[n].member = &AIObject::some_member;
ai[n].comparand = 50;
...
if (ai[n].compare(ai[n].object, ai[n].member, ai[n].comparand)) {
...
}
这只是将任何类型的问题从rules数组移到member
。 C ++需要知道一个成员至少有多少字节,一个字符串(例如)可以比一个大得多。你可以通过使用指针解决这个问题:这本质上是C ++的任何版本,但你需要自己删除它(或者你会泄漏内存!),此时下面的接口方法变得更简单。
如果我正在做你想要的事情,我会使用继承:
struct Sprite {
int frame;
double rotation;
Sprite() {
frame = 0;
rotation = 0.0;
}
virtual ~Sprite() {}
virtual void think() {
++frame;
}
virtual void draw() {
...
}
};
struct RotatingSprite : public Sprite {
int state;
MyShape() {
state = 0;
}
void think() {
Sprite::think();
if (state == 0 && frame > 64) {
state = 1;
rotation += 180.0;
}
}
};
或函数指针:
struct Sprite {
int frame;
double rotation;
void (*think)(Sprite*);
Sprite() {
frame = 0;
rotation = 0.0;
}
};
void rotate_think(Sprite* sprite) {
if (sprite->state == 0 && sprite->frame > 64) {
sprite->state = 1;
sprite->rotation += 180.0;
}
}
...
sprite->think = &rotate_think;
如果你真的需要动态地做,我建议使用C ++的++部分。对于谓词(谓词只是返回布尔值的东西,如isLowerCase())创建AIPredicate接口,以及AIAction接口的操作:
struct AIPredicate {
// "When you delete an AIPredicate, delete the full type, not just this interface."
virtual ~AIPredicate() {}
// "You can treat this as a function (operator()) but I'm not providing an implementation here ( = 0)"
virtual bool operator()(AIObject* object) = 0;
};
struct AIAction {
virtual ~AIAction() {}
virtual void operator()(AIObject* object) = 0;
};
struct AIRule {
// std::auto_ptr (or std::unique_ptr if you can use C++0x) will delete predicate for you.
// Add "#include <memory>" to your includes if it complains (most std headers will include it already)
std::auto_ptr<AIPredicate> predicate;
std::auto_ptr<AIAction> action;
};
现在你可以制作如下类型:
struct AIFrame : public AIPredicate {
// Implement the operator() member AICondition promises.
bool operator()(AIObject* object) {
return object->foo < 100;
}
};
...
// Use .reset() instead of = if you use std::unique_ptr.
ai[n].predicate = new AIFooIsLow();
如果你想拥有一个非常通用的谓词类型,你可以使用非常强大(和复杂)的模板功能:
// The naming convention I'm using here is 'T'TitleCase for template parameters, TitleCase for types,
// lower_case for arguments and variables and '_'lower_case for members.
template<typename TMemberType, AIObject::TMemberType* TMember>
struct AIMemberEquals : public AIPredicate {
// Constructor: Initializes a new instance after it is created.
AIMemberEquals(TMemberType comparand) {
// Save comparand argument so we can use it in operator().
_comparand = comparand;
}
bool operator()(AIObject* object) {
return object->*TMember == comparand;
}
// Stores the value to compare.
TMemberType _comparand;
};
不幸的是,创建模板看起来有点疯狂:
ai[n].predicate = new AIMemberEquals<int, &AIObject::some_member>(100);
将其读作“创建一个新实例(AIMemberEquals应用于int的类型和(AIObject的some_member成员)创建的类型),参数为100”。
当你有多个谓词时,如果没有C ++ 0x的unique_ptr或shared_ptr,内存管理会变得有点困难,因为std :: auto_ptr在容器中不起作用,因此会删除对象的类型:
#include <vector>
struct AIData {
// vector is fairly close to AS3's Array type, it is a good default for
// arrays of changing or unknown size.
std::vector<AIPredicate*> predicates;
// Destructor: will be run before the memory for this object is freed.
~AIData() {
for (int i = 0; i != predicates.size(); ++i) {
delete predicates[i];
}
}
};
...
ai[n].predicates.push_back(new AIFooIsLow());
...
for (int i = 0; i != ai[n].predicates.size(); ++i) {
(*ai[n].predicates[i])(ai[n].object);
}
在C ++ 0x中:
struct AIData {
// unique_ptr will delete it for you, so no ~AIData() needed.
std::vector<unique_ptr<AIPredicate>> predicates;
};
你最后的例子可能在C ++中看起来像:
std::auto_ptr<Shape> shape(new Shape());
...
std::auto_ptr<AIRule> rule(new AIRule());
rule->predicates.push(new AIMemberEquals<int, &Shape::state>(0));
rule->predicates.push(new AIMemberGreater<int, &Shape::frame>(64));
rule->actions.push(new AIAddMember<double, &Shape::rotation>(180.0));
rule->actions.push(new AISetMember<int, &Shape::state>(1));
shape->ai.push(rule); // .push(std::move(rule)); if you are using unique_ptr
当然不是那么漂亮,但它有效且相当灵活。