Duplicates in QSet - c++

For a class X and a QSet< X* >, how is it possible to make sure that the QSet doesn't contain duplicate elements?
The unique property in each object of type X is a QString that can be fetched using getName().
I've implemented the qHash(X*) function, the operator==(), operator<() and operator>(), but the QSet still accepts duplicate elements, i.e., those with the same Name.
Could someone help me out in making this work?
Ok. Here's what I'm trying to do.
I have a class Y and a class X, both of which inherit QDialog. A function in class Y ( a slot), is responsible for spawning objects of class X. The dialog for Y is to be made responsible for the X objects spawned. This is why I created a QSet< X* > member in Y.

The problem is that you cannot overload operator== like this:
bool operator==(X*, X*);
This is because at least one of the argument must be of class type.
Since you say you implemented operator==, I suppose you did something like this:
struct X
{
bool operator==(X*) const;
};
This operator will never be called when QSet tries to fiend duplicates because it needs a left argument of type X and a right of type X*
I can see two possible solutions to this problem:
Do not store your items as pointers (ie using QSet<X>). This will allow you to overload the correct operators. This solution, however, is not always feasible.
If you could enforce somehow that there is only one object with a given id, you could just store pointers in you QSet without needing to overload any operators nor the qHash function.
Edit: If your design allows to create multiple X-objects with the same id but you only want one such object to exist at any time, maybe it's best to use a QMap which maps from id to X*. When you create a new object, do something like this:
QString newId = ...;
delete objectsMap[newId];
objectsMap[newId] = new X(newId);

Depending on your exact requirements, you could use a sorted vector together with std::unique (which accepts a custom binary predicate for comparison).

Could you use QMap instead? Your dialog would have member variable QMap<QString, X*> items. Then the checking and creating new X's would be like:
QString name = "foo";
if (!items.contains(name))
{
items[name] = new X(name);
}
else
{
// "foo" already exists
}
Maybe this is not as elegant solution as using QSet might be, but I think this is shorter and easier to understand.

I get exactly the same problem. In the end I get here. My solution is very simple.
If class QSet can't do what I want, why don't use it object in my class with added code to every function I need. Here is my solution:
Declaration of Set class:
#pragma once
#include<Plant.h>
#include<qset.h>
class Set
{
public:
Set(void);
~Set(void);
bool contains(Plant *plant);
QSet<Plant*>::iterator insert(Plant *plant);
QSet<Plant*>::iterator erase(Plant *plant);
private:
QSet<Plant*> plants;
};
Definition of Set class
#include "Set.h"
Set::Set(void){
plants = QSet<Plant*>();
}
Set::~Set(void){
}
bool Set::contains(Plant *plant){
for(int i=0;i<plants.size();++i){
if(plants.values().at(i)->compare(plant))
return true;
}
return false;
}
QSet<Plant*>::iterator Set::insert(Plant *plant){
if(!contains(plant))
return plants.insert(plant);
}
QSet<Plant*>::iterator Set::erase(Plant *plant){
QSet<Plant*>::iterator it;
for(it = plants.begin();it!=plants.end();++it){
if((*it)->compare(plant)){
return plants.erase(it);
}
}
return it;
It worked for me very well.

Related

What is a nice way to cycle through an enum?

Background
For a UI in an embedded project, I'm looking for a nice generic way to store a "state" and cycle through it with a button press, e.g. a list of menu items.
Normally, I like to use enums for this purpose, for example:
enum class MenuItem {
main,
config,
foo,
bar,
};
Then, in my UI code, I can store a currentMenuItem state like
MenuItem currentMenuItem = MenuItem::MAIN;
and do things depending on the current state by comparing currentMenuItem to any of its possible values, as declared in the enum.
Problem
The thing is, I would now like to advance to the next menu item. For that, I can quite trivially write a function that does that by casting to int, incrementing by one, and casting it back to the enum. I have multiple distinct enums, so I can even use this templated function that does this for arbitrary enums:
template <typename T>
void advanceEnum(T &e) {
e = static_cast<T>(static_cast<int>(e) + 1);
}
The problem with this is that it doesn't wrap around: it will happily keep increasing the underlying integer beyond the number of elements in the actual enum. I could quite easily solve this (by taking the modulo with the number of elements in the above function), if only there was an clean way to get the element count of an enum. Which, as far as I could find, there isn't really.
Custom enum?
I was thinking of writing a custom 'CyclicEnum' class that implements this behaviour, that I can subsequently derive from. That way, I could also write this as an overloaded operator++.
However, I still haven't devised how to can get an enum-like thing without actually using an enum. For example, I got to something like this:
class CyclicEnum {
public:
uint8_t v;
CyclicEnum& operator++() {
v = (v+1) % count;
return *this;
}
CyclicEnum operator++(int) {
CyclicEnum old = *this;
operator++();
return old;
}
private:
uint8_t count;
protected:
CyclicEnum(uint8_t v, uint8_t count) : v(v), count(count) {}
};
struct Tab : public CyclicEnum {
enum Value {
main,
config,
foo,
bar,
};
Tab(Value v) : CyclicEnum(v, 4) {}
};
However, as you can see, this still uses an enum inside the custom CyclicEnum class, and I'm back to the same issue: I can't count the number of Enum elements, so I have to specify that manually (which I think is not nice because it's redundant). Secondly, this way I also have to override the constructor in the derived class which I would like to avoid to keep it as clean as possible.
Upon searching this issue, many people apparently recommend to add a "dummy" value at the end of the enum as a trick to get the size:
enum Value {
main,
config,
foo,
bar,
_count,
};
but frankly, I find that just ugly, since _count is now actually a valid option.
Is there any way around this? Am I abusing enums? Looking at the fact that enums are apparently (by design) so hard to count, probably. But what would be a nice way to have a structure like this with named values like an enum provides?
EDIT:
OK, I'm convinced that using a _count element at the end isn't so bad of an idea. Still, I'd like to encapsulate this in some kind of structure, like a class.
I also thought of instead of using inheritance, using a class template to accomplish this, something like this:
(caution, this doesn't compile):
template<typename T>
struct CyclicEnum {
T v;
enum Values = T;
CyclicEnum& operator++() {
v = (v+1) % T::_count;
return *this;
}
CyclicEnum operator++(int) {
CyclicEnum old = *this;
operator++();
return old;
}
CyclicEnum(T v) : v(v) {}
};
struct MenuItem : public CyclicEnum<enum class {
main,
config,
foo,
bar,
_count,
}> {};
However, this does not work because "ISO C++ forbids forward references to 'enum' types" and "anonymous enum cannot be defined in a type specifier"...
Is there another way to make this idea work (template a class with an enum), or is this not going to work?
The thing is, I would now like to advance to the next menu item.
++ comes to mind. Keep it simple.
That way, I could also write this as an overloaded operator++
Yeah... or again, keep it simple, you could just drop the whole class. There's no need to write an abstraction layer around a simple integer. Really.
Upon searching this issue, many people apparently recommend to add a "dummy" value at the end of the enum as a trick to get the size
Sure why not. This is incredibly common practice.
but frankly, I find that just ugly, since LAST is now actually a valid option.
It's canonical code, it isn't ugly. Just give it a sensible name, maybe something like MENU_ITEMS_N to suggest that this a counter variable then use it as such. for(int i=0; i<MENU_ITEMS_N; i++) ...
Am I abusing enums?
Enums are just named integer values. Don't over-engineer your code. It's bad for performance, it's bad for maintenance, it adds needless complexity.
You could use the magic_enum library to reflect on enums.
Example which gets the names of all enum elements as std::array < std::string_view > and prints them.
#include <algorithm>
#include <iostream>
#include <magic_enum.hpp>
enum struct Apple
{
Fuji = 2,
Honeycrisp = -3,
Envy = 4
};
int
main ()
{
constexpr auto &appleNames = magic_enum::enum_names<Apple> (); // get an std::array<std::string_view> with the names for the enum sorted by value
std::copy (appleNames.begin (), appleNames.end (), std::ostream_iterator<std::string_view> (std::cout, "\n")); // print all the names
}
Prints:
Honeycrisp
Fuji
Envy
There are some limitations please read magic enum limitations
Rather then using _count, set the last "sentinel" value to the last actual value.
enum Value {
main,
config,
foo,
bar,
last = bar
};
Then you avoid the problem of having an enum value that is not a valid menu option. in your increment for example instead of :
v = static_cast<Value>( (static_cast<int>(v) + 1) %
static_cast<int>(Value::_count) );
you'd have:
v = static_cast<Value>( (static_cast<int>(v) + 1) %
(static_cast<int>(Value::last) + 1) ) ;
If in fact these enums simply cause different menu item handler functions to be called, then you could instead used an array of pointer-to-functions rather then an enum/switch or whatever.

auto type deduction coercion for templated class?

I have 2 issues in a template class I'm building. I've included example code below. First question is whether I can coerce the auto type deducted for a templated class. i.e.:
auto p = myvar;
where myvar is T<...>, could I force auto to detect Q<...>? This is simplified. Read on for a more clear explanation.
Edited for clarity: Let me explain what I'm doing. And I'd also like to indicate that this style code is working on a large-scale project perfectly well. I am trying to add some features and functions and in addition to smooth out some of the more awkward behaviors.
The code uses templates to perform work on n-dimensional arrays. The template has a top-level class, and a storage class underneath. Passing the storage class into the top level class allows for a top level class which inherits the storage class. So I start with NDimVar, and I have NDimStor. I end up with
NDimVar<NDimStor>
The class contains NO DATA except for the buffer of data:
class NDimStor<size_t... dimensions> {
int buffer[Size<dimensions...>()]
}
This makes the address of the class == the address of the buffer. This is key to the whole implementation. Is this an incorrect assumption? (I can see this works on my system without any issues, but perhaps this isn't always the case.)
When I create NDimVar<NDimStor<10,10>> I end up with a 10x10 array.
I have functions for getting pieces of the array, for example:
NDimVar<NDimStor<dimensions...>>::RemoveDim & get(int index);
This creates a new 1d array of 10 elements out of the 2d 10x10 array:
NDimVar<NdimStor<10>>
In order to return this as a reference, I use a reinterpret_cast at the location of the data I want. So in this example, get(3) would perform:
return reinterpret_cast<NDimVar≤NDimStor<dimensions...>>::RemoveDim&>(buffer[index * DimensionSumBelow<0>()]);
DimensionSumBelow<0> returns the sum of elements at dimensions 1+, i.e. 10. So &buffer[30] is the address of the referenced 1d NDimVar.
All of this works very well.
The only issue I have is that I would like to add on overlays. For example, be able to return a reference to a new class:
NDimVar<NDimPermute<NDimStor<10,10>,1,0>>
that points to the same original location along with a permutation behavior (swapping dimensions). This also works well. But I would like for:
auto p = myvar.Permute<1,0>()
to create a new copy of myvar with permuted data. This would work if I said:
NDimVar<NDimStor<10,10>> p = myvar.Permute<1,0>().
I feel that there is some auto type deduction stuff I could do in order to coerce the auto type returned, but I'm not sure. I haven't been able to figure it out.
Thanks again,
Nachum
What I want is:
1. Create temporary overlay classes on my storage, e.g. A_top<A_storage> can return a type called A_top<A_overlay<A_storage>> without creating a new object, it just returns a reference to this type. This changes the way the storage is accessed. The problem is upon a call to auto. I don't want this type to be instantiated directly. Can I modify the return to auto to be an original A_top?
#include <iostream>
using namespace std;
class A_storage {
public:
float arr[10];
A_storage () {
}
float & el (int index) {
return arr[index];
}
};
template <typename T> class A_overlay : T {
private:
A_overlay () {
cout << "A_overlay ()" << endl;
}
A_overlay (const A_overlay &) {
cout << "A_overlay (&)" << endl;
}
public:
using T::arr;
float & el (int index) {
return arr[10 - index];
}
};
template <typename T> class A_top;
template <typename T> class A_top : public T {
public:
A_top () {
}
A_top<A_overlay<A_storage>> & get () {
return reinterpret_cast<A_top<A_overlay<A_storage>>&>(*this);
}
};
using A = A_top<A_storage>;
int main (void) {
A a;
auto c = a.get(); // illegal - can i auto type deduce to A_top<A_storage>?
return 0;
}
If a function accepts (A_top<A_storage> &) as a parameter, how can I create a conversion function that can cast A_top<A_overlay<A_storage>>& to A_top<A_storage>& ?
Thanks,
Nachum
First, your design doesn't look right to me, and I'm not sure if the behaviour is actually well-defined or not. (Probably not.)
In any case, the problem is not with auto. The error is caused by the fact that the copy constructor of A_overlay is private, while you need it to copy A_top<A_overlay<A_storage>> returned by a.get() to auto c.
(Note that the auto in this case obviously gets deduced to A_top<A_overlay<A_storage>>, I assume you made a typo when said that it's A_top<A_storage>.)
Also note that A_storage in A_top::get() should be replaced with T, even if it doesn't change anything in your snippet because you only have T == A_storage.
If a function accepts (A_top &) as a parameter, how can I create a conversion function that can cast A_top> to A_top& ?
Ehm, isn't it just this:
return reinterpret_cast<A_top<A_storage>&>(obj);
reinterpret_cast should almost never be used. It essentially remove any compiler validation that the types are related. And doing unrelated cast is essentially undefined behavior as it essentially assume that derived classes are always at offset 0...
It does not make any sense to write such code. It is not maintainable and hard to understand what you are trying to achieve. It look like you want to pretend that your A_top<A_storage> object is a A_top<A_overlay<A_storage>> object instead. If this is what you want to do, then declare A alias as that type.
In your code, it look like you want to invert the indexing so that item at position 10 is returned when you ask item at position 0 and vice versa. Do you really think, that it is obvious from your obfuscated code? Never write such bad code.
Something like
class A_overlay {
public:
float & el (int index) { return arr[10 - index]; }
private:
A_storage arr;
};
would make much more sense than your current code.
No cast needed.
Easy to understand.
Well defined behavior.
You might keep your job.
And obviously, you would update the following line as appropriate:
using A = A_top<A_storage>;
Also, if A_top has no useful purpose, then why not using A_overlay directly? And why are you using template if A_storage is not a template? Do you really want to reuse such mess elsewhere in your code base.
Obviously, your code inheritance does not respect IS-A relationship if your write such code. So it is clearly a bad design!

Data Structure that stores object with names

I want to store object that are given a certain name.
I wanted to use struct and then store them in a vector, but it was suggested to me that I should rather use a different data structure, a little more simple, but I cant seem to find one.
My current ("complex") solution:
//in header file
struct objStorage{
Classname obj;
string name;
};
vector<objStorage> vec;
//in constructor
objStorage firstObj;
firstObj.obj = new Classname();
firstObj.name = "foo";
vec.push_back(firstObj);
Is there a more simple solution (Data structure)?
I should add that I don't need the structure once I stored (copied?) it in the vector, because this is all happening in another class (constructor) so I don't want any problems when calling the constructor multiple times.
If you want to lookup items by some key, for example a string, the classic thing to use is a map:
std::map<std::string, Classname> items;
std::pair<std::map<std::string, Classname>::iterator, bool> inserted =
items.insert(std::make_pair(std::string("foo"), Classname()));
items["bar"] = Classname();
In this set up, if you really think you want to use pointers, you should consider some form of smart pointer.
There are other options, for example, C++11 introduces other lookup structures - e.g. unordered maps.

Using References in methods in C++ and Returning Values?

Imagine you are implementing a List Data Type in C++. This List of yours will keep items in a simple array and will hold instances of type: "Animal".
So for example, addItem method will be something like this:
void MyAnimalList::insertAnimal(Animal a){
animalArray[currentPosition] = a;
currentPosition++;
}
So we simply have a currentPosition in the our List implementation and when we add a new Animal to it, Animal a is now referenced in the animalArray in "currentPosition" and currentPosition is then increased.
My question will be about retrieveItem. Is there any difference between these 2:
void MyAnimalList::getTheLastAddedAnimal(Animal &a){
a = animalArray[currentPosition-1];
}
Animal MyAnimalList::getTheLastAddedAnimal(){
return animalArray[currentPosition-1];
}
Obviously, the second method will be called like:
Animal lastAddedAnimal = myAnimalList.getTheLastAddedAnimal();
and the first one should be called like:
Animal someAnimal;
myAnimalList.getTheLastAddedAnimal(someAnimal);
The difference is semantic. If a method is called getXXXXX, you expect it to return that thing.
Most decent compilers implement NRVO, so efficiency isn't really an issue here. I'd go with the second version.
I would however change the definition of insertAnimal(Animal a) to insertAnimal(const Animal& a).

Accomplish this task in C++; Migration from AS3.0

I've got way too much information to work with, so for now I'll consider this question answered until I can sort it all out and decide on the final implementation! Thanks a ton gf and Simon Buchan. I wish I could accept both of your answers, since they're both definite possibilities!
Additional / Revised Conceptual Information as suggested:
What I am aiming to do;
I am making a game. In this game every object used is an instance of the DOBJ class. The TUR class extends the DOBJ class. The SHO class extends the TUR class.
Each TUR class has an array of SHO's stored in it's SHOARR array. Each SHO instance needs to be given a set of instructions.
I know for a fact I could make 1000's of different SHO classes that have their instructions set during construction.
However, considering I will have so many different acting SHO instances, I was interested in another way to pass a set of instructions. Through the contruction of the SHO would be the ideal.
The instructions I am attempting to pass to each SHO are simple if statements;
if(frame > 64) { rotation += 4; };
if(state == 0 && frame < 32) { xs = 12; ys = 12; state = 1; };
Original question
Migration from ActionScript3.0 to C++ is proving to be a trial indeed. Thanks to those who have answered my questions thus far and also to those who opened stackoverflow in the first place. Onto the question... (TL;DR near the bottom to get straight to the question)
I'm attempting to apply the same logic that I could apply in AS3.0 to my project in C++ and it's just not going very well.
In AS3.0 I was used to slapping any and every datatype into an Array. It made things pretty simple. Now that I've run into C++ dev, I realized that I can't exactly do that anymore.
So now I'm stuck with this problem of rewriting a little AI system in a new language, where the driving point of the system isn't even compatible!
Here's an example of a piece of the code I was writing in AS3.0;
AI[NUM][1]( OBJ, AI[NUM][2], AI[NUM][3] );
AI being an array, NUM being an integer, OBJ being an instance of a class.
This line obviously called the function in the second element of the first array in the main array with the arguments being a class in which to perform the function on, whatever was in the third element of the first array of the main array, and likewise the fourth element.
In this case;
AI[NUM][1] would be a function
AI[NUM][2] would be a variable
AI[NUM][3] would be a number
Generally, my AI was run on calling a function to change or compare the variable with a number.
An example would be;
CompareST( someObject, "x", 500 );
and return true if someObject's x variable was smaller than (ST) 500.
The AI array itself was just filled with arrays of calls similar to this.
Quite new to C++ I had no idea how to go about this, so I did a bit of searching and reading of many different websites and came to the conclusion that I should look into function pointers.
However, after reading a bit into them, I've come to the conclusion that it won't help me realize my goal. While it did help me call functions like I wanted to call them, it doesn't help me stack different datatypes into one large array of arrays.
TL;DR
EDIT++:
What I need for each object is a set of instructions to be checked every frame. However, for each instance of the class, the instructions have to be different.
I plan on having a LOT of different instances, so making a class for each one is unreasonable.
Thus, I needed a way to pass a set of instructions to each one through it's constructor and read + execute them at any time their think() function is called.
My ultimate goal (aside from finding out about a better way to go about this) would be to be able to have an array of function calls, like;
A[n][0]( O, A[n][1], A[n][2] );
Where;
O is the instance the function is altering
A[n][0] is a function (Equality or Comparison)
A[n][1] is the variable, eg; "x", O["x"] (or a pointer to that variable in the case of C++)
A[n][2] is the value to alter the variable by, or compare it to.
And I'm not sure how I would rewrite this into C++, or alter it to work in another way.
Aftermath / Additional Information
What I'm actually aiming to do is be able to give an object a set of instructions at the time of it's creation, through the constructor. For example upon creation give an object instructions to wait 64 frames, and then rotate in the opposite direction, would have been something like this;
t.AI = [ [ 1, AIF.CompareET, "STATE", 0, AIF.CompareGT, "FRAME", 64, 0, AIF.EqualityAT, "baseRotation", 180, AIF.EqualityET, "STATE", 1 ] ];
In pseudocode;
(The 1 in the array denotes how to read the rest of the array, in this case everything before the odd 0 [ The one that comes after 64 ] is a comparison. If any of those fail, anything after the 0 will not be looked at )
Compare STATE is equal to (ET) 0, if true
Compare FRAME is greather than (GT) 64, if true
Add 180 to (AT) baseRotation, Set STATE equal to 1
Sorry that this turned out really long. I hope it's understandable, and I'm not asking something stupidly difficult to explain.
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
}
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;
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.