+1  A: 

Wow.

Reading through that slowly suggests what you're trying to end up with is an array of function calls and you can choose a different function with the same parameters (but different implementation) for different actions and choose the correct one for the correct case.

If that is the case, you're looking for function pointers. Try this tutorial.

You should be able to use a function pointer with an argument set and point it to the correct function based on your needs. You won't need an array of function pointers for this either - any function that matches the definition should do. From the tutorial, declare a function pointer like this:

int (TMyClass::*functptr)(classname, int, int) = NULL;                // C++

Then assign it later:

this.functptr = &TMyClass::doitthisway; 
Ninefingers
I have tried to use function pointers, but I found that the problem comes in whenever I have to call the function to actually go and call those functions.Initially, I had every object call it's think function and in that function it read all of the function calls in it's AI array and did things accordingly. Otherwise, I couldn't imagine a way to set it up so that I can set it up like an extended if statement...if (this() function call returns true and this() one as well){ do this; this; and this; }I need multiple of those to be checked and set in the construction.
+1  A: 

You can store functions using function pointers or functors. Variant types though are not natively supported by C++, you have to use custom solutions there.

One possibility would be to use Boost.Any (or better, Boost.Variant if you only use a fixed set of types):

typedef void (*Function)(Object*, const std::string&, boost::any&);
std::vector<Function> functions;

Given some function:

void f(Object* obj, const std::string& name, boost::any& value) {
    // ...
}

you could store and call it similar to your example:

functions.push_back(&f);
functions[0](obj, "x", boost::any(500));

To utilize a declarative syntax, there are three options that come to my mind:

  • you use a similar approach and have central "interpreter" function, e.g. based on a switch (don't forget to switch to integers or pointers-to-members instead of strings if you need performance)
  • you invent your own language and generate C++ code from description files
  • you compose function objects in a declarative way

To do composition, you could use Boost.Bind or something like custom objects that represent operations:

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; }
};

Now we can build operation lists:

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));

We can use helper functions to avoid having to specify the template types:

template<class T>
Operation* opGreaterThen(T Object::*mem, const T& val) {
    return new GreaterThen<T>(mem, val);
}

Assuming a similar helper for SetTo and using Boost.Assign the above becomes:

OpList operations = boost::assign::list_of
    (opGreaterThen(&Object::Frame, 64)) 
    (opSetTo      (&Object::State,  1));

Executing the operations becomes the following then:

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        
}
Georg Fritzsche
I've added additional information to the beginning of my post describing exactly the context and what I'm aiming to achieve. Please let me know if you feel I still need to add more information. For function objects, would it allow me to easily set up many different types of SHO instances? In the end, if I can't avoid it, I will end up having a very large function with sets of instructions depending on an integer input and just set each SHO instance to run based on those pre-written set of instructions. I'll read into Boost and function objects.
+1  A: 

While it is possible (although a pain) to have an array of arbitrary types, you pretty much never need it, since you have to know something about what is where to do anything interesting with it: for example, your 'TL;DR' example seems to look something like:

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;
};

So now you can do something like:

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)) {
        ...
    }

This just moves the any type problem from the rules array to member though. C++ needs to know at least how many bytes a member is, and a string (for example) can be much bigger than an int. You can get around this by using pointers: which essentially is C++'s version of any, but you then need to delete it yourself (or you will leak memory!), at which point the interface method below becomes simpler.

If I was doing what you seem to want, I would use inheritance:

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;
        }
    }
};

Or a function pointer:

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;

If you really need to do it dynamically I would recommend using the ++ part of C++. For the predicates (a predicate is just something that returns a boolean, like isLowerCase()) create an AIPredicate interface, and the actions an AIAction interface:

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;
};

Now you can make types like:

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();

If you want to have a very general predicate type, you can use the very powerful (and complicated) templates feature:

// 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;
};

Unfortunately, creating templates looks a bit crazy:

ai[n].predicate = new AIMemberEquals<int, &AIObject::some_member>(100);

Read it as "create a new instance of (the type that AIMemberEquals applied to int and (the some_member member of AIObject) creates), with the argument 100".

When you have multiple predicates memory management becomes a bit more difficult without C++0x's unique_ptr or shared_ptr, types that will delete the object for you, since std::auto_ptr doesn't work in containers:

#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);
    }

In C++0x:

struct AIData {
    // unique_ptr will delete it for you, so no ~AIData() needed.
    std::vector<unique_ptr<AIPredicate>> predicates;
};

Your final example could in C++ look something like:

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

Certainly not as pretty, but it works and is fairly flexible.

Simon Buchan
This, on the surface, looked incredibly intimidating. However, reading through it a lot of things seemed to make sense, even though I've never used almost any of it. After all, I've only been working in this language for about 2 weeks. It's mostly new ground. Huge thanks for taking such extensive efforts.
Well this is a big chunk of the worlds most complex language, so you probably *should* be intimidated :). The thing about C++ though is there are at least 7 different ways to do everything, with different tradeoffs, so just deal with what makes sense to you, and be afraid of `new` :).
Simon Buchan