Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 7 years ago.
Improve this question
What options are available to create an object with lots of parameters in the global namespace? I'm thinking of the tradeoff between temporary object/variable creation and readability.
Edit
This is for embedded programming on the Arduino. It will be the main object of a reusable library controlling a set of nested objects.
Background: I have a test PCB which can be populated differently depending on its final use and I need to cover all of these options in one easy to use library. I am trying to avoid the user accidently missing required initialisation parameters before using the object.
Feel free to constructively criticise my code!
The two options that I can think of are:
A constructor with lots of parameters.
A constructor with a single struct parameter.
Option 1 looks messy and hard to follow with lots of parameters.
Option 2 requires a temporary struct variable for readability.
Example below (normally I would separate into headers etc.):
#include <Arduino.h>
class NestedClass {
public:
// Empty constructor for creation of unitialised object. Bad practice?
NestedClass() {
}
// Main constructor.
NestedClass(float voltageReference) :
voltageReference_(voltageReference) { // Use initialisation list.
}
float measureVoltage(uint_fast8_t channel) {
// Convert ADC value to absolute voltage.
return analogRead(channel) * (voltageReference_ / 1023);
}
private:
float voltageReference_;
};
class ComplexClass {
public:
enum class Mode
: uint_fast8_t {
MODE1,
MODE2,
MODE3
};
struct Parameters {
uint_fast8_t parameter1;
uint8_t parameter2;
float parameter3;
float parameter4;
Mode mode;
float voltageReference;
};
// Empty constructor for creation of unitialised object. Bad practice?
ComplexClass(void) {
}
// Big constructor. Messy when used.
ComplexClass(uint_fast8_t parameter1, uint8_t parameter2, float parameter3,
float parameter4, Mode mode, float voltageReference) {
// Could have used initialisation list instead.
this->parameter1_ = parameter1;
this->parameter2_ = parameter2;
this->parameter3_ = parameter3;
this->parameter4_ = parameter4;
this->mode_ = mode;
this->nestedClass_ = NestedClass(voltageReference); // Wasted temporary object with reassignment?
}
// Alternative constructor. Looks neater/more legible when used.
ComplexClass(Parameters parameters) {
this->parameter1_ = parameters.parameter1;
this->parameter2_ = parameters.parameter2;
this->parameter3_ = parameters.parameter3;
this->parameter4_ = parameters.parameter4;
this->mode_ = parameters.mode;
this->nestedClass_ = NestedClass(parameters.voltageReference); // Wasted temporary object with reassignment?
}
void megaMeasurements() {
// Do something involving nestedClass.measureVoltage().
}
private:
// Maybe put all of these in another struct for neatness?
uint_fast8_t parameter1_;
uint8_t parameter2_;
float parameter3_;
float parameter4_;
Mode mode_;
NestedClass nestedClass_;
};
//####################
// Start main code.
//####################
// Option 1:
// Not immediately obvious which value is for which parameter.
ComplexClass complexClass(1, 2, 3.30, 2.7, ComplexClass::Mode::MODE2, 5.00);
// Option 2:
// Unitialised object (sort-of).
ComplexClass complexClass2;
// Arduino standard function. Called once from main.cpp
void setup() {
// Option 2 continued:
ComplexClass::Parameters parameters;
parameters.mode = ComplexClass::Mode::MODE2;
parameters.parameter1 = 1;
parameters.parameter2 = 2;
parameters.parameter3 = 3.30;
parameters.parameter4 = 2.7;
parameters.voltageReference = 5.00;
complexClass2 = ComplexClass(parameters); // Reassignment. Wasteful?
}
// Arduino standard function. Called in a continuous loop after setup().
void loop() {
complexClass.megaMeasurements();
complexClass2.megaMeasurements();
}
My opinion (based on my practice):
constructor with many parameters look messy and should better be avoided. More, if some parameters are bad, you can't return "false" and the only way to complain is to throw an exception. If you want to go this way, it's better to define some init() function with several parameters, optionally returning false (or some error code) to complain if parameters are bad. In this case it's better to avoid literal numeric values using #define or static const declarations.
The other way is to assign values one by one, either directly (public) or with set() methods. In this case you can put literals in the code.
Related
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 1 year ago.
Improve this question
The use case is quite common: I have a few variables that should be accessible globally (by various classes) and initialized from a configuration file. I can't decide the best way to do this.
Options struct is owned by someone and passed around:
struct Opts {
int op1;
double op2;
};
int main() {
Opts o = {3, 0.5};
// Pass this around as const reference, or potentially copying
return 0;
}
Use static vars for some Options struct.
struct Opts {
inline static int op1;
inline static double op2;
};
int main() {
Opts::op1 = 3;
Opts::op2 = 0.5;
// No passing around, but use Opts::op1 instead
return 0;
}
Use static vars, but sorta differently (not really).
struct Opts {
static int op1;
static double op2;
};
int Opts::op1;
double Opts::op2;
int main() {
Opts::op1 = 3;
Opts::op2 = 0.5;
// No passing around, but use Opts::op1 instead
return 0;
}
What should I actually do here? I thought the best case would be static const vars to make sure no one changes these, but the values can only be obtained from some file (so seems like it has to be run time instead of compile time), even though they won't change at after initializing.
I could make it const refs if I don't make it static, but that just forces me to pass this around among many classes. I also wanted the class that owns these options to be different from the class that parses them. This would just be even uglier with either moving unique_ptr or copying.
Is there a way to get best of both worlds:
not having to pass things around excessively (hence the semantics of "static")
making sure that these can't be modified after initialization, at least indicating that is the case (hence the semantics of "const")?
I commonly use a singleton Class. A config object is instantiated at startup time, and it reads the content of the file. It contains "getters" which return the values to you, and it might also contain "setters" which allow those values to be changed – in which case it also rewrites the underlying file. The settings file is simply how this object "persists" itself from one run to the next.
The object is also responsible for checking the contents of the settings to ensure that they are proper. If someone edits the file incorrectly, the object is going to throw a meaningful exception. So, if it doesn't do that, you know the settings file is good. The "setters" are equally suspicious: if some other part of the program tries to store an incorrect value, the object will catch the attempt and throw an exception.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
Have homework and we just went over classes. Thinking about how I could implement classes into my homework but the instructions say use a structure. (It's reading binary files and editing information) After looking at some stuff we did in class I probably don't need classes for my situation. But now I'm curious, why would you want to use a structure inside a class? Why would you use a structure when you can just set the data members in the class instead? What does struct have to offer to warrant doing this?
The primary benefit of putting values into an inner struct as opposed to just declaring them as member variables of the class would be that you can then instantiate multiple instances of that struct very easily, and refer to the set of variables in each struct with a single pointer or reference.
As an example, here are two implementations of a toy array-of-3D-points class. The first one just uses separate member-variables, while the second one declares an inner struct to represent a Point object.
Note that in the first implementation, the RotatePoint() method takes four arguments, while in the seconds argument it takes just two. Being able to refer to a Point with a single struct Point & argument (rather than three separate float & arguments) is both more efficient at run-time and less error-prone for the programmer to call.
// implementation without an inner struct
class MyPointArray1
{
public:
[...]
void RotatePoints(float radians)
{
for (int i=0; i<POINTS_ARRAY_LENGTH; i++)
{
// Hazard here -- a tired programmer might specify arguments in the wrong order!
RotatePoint(x[i], y[i], z[i], radians);
}
}
private:
enum {POINTS_ARRAY_LENGTH = 100};
float x[POINTS_ARRAY_LENGTH];
float y[POINTS_ARRAY_LENGTH];
float z[POINTS_ARRAY_LENGTH];
void RotatePoint(float & x, float & y, float & z, float radians)
{
// [math to update the values of x, y, and z would go here]
}
};
// implementation with an inner struct
class MyPointArray2
{
public:
[...]
void RotatePoints(float radians)
{
for (int i=0; i<POINTS_ARRAY_LENGTH; i++)
{
// It's pretty much impossible to get this wrong without provoking a compile-time error :)
RotatePoint(points[i], radians);
}
}
private:
enum {POINTS_ARRAY_LENGTH = 100};
struct Point
{
float x;
float y;
float z;
};
struct Point points[POINTS_ARRAY_LENGTH];
void RotatePoint(struct Point & pt, float radians)
{
// [math to update the values in (pt) would go here]
}
};
From my understanding, you would implement a struct inside a class when you want to create objects within that class only. Outside of the class, you would not be able to create objects of that struct.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
How can I make a wrapper class for existing object, which automatically prohibits or allows modification of wrapped object's data depending if wrapped object was provided to constructor with or without const type qualifier.
So if wrapper class received (WrappedObj *ptr), then it allows set and get methods. If wrapper class received (const WrappedObj *ptr), then only get methods are allowed at compile time.
Problem example:
I have a pointer to buffer where there is Ethernet header and I want to ease access to Ethernet data for readability and reduce mistakes by endianess.
struct ethhdr {
uint8_t h_dst[6]; /* destination eth addr */
uint8_t h_src[6]; /* source ether addr */
uint16_t h_proto; /* packet type ID field */
uint8_t h_data[0];
} __attribute__((packed));
// My wrapper "view" class, which doesn't really work as expected
class EtherView {
public:
EtherView(uint8_t *ptr) : mPtr{(ethhdr*)ptr} {}
EtherView(const uint8_t *ptr) : mPtr{(ethhdr*)ptr} {}
/* SET METHODS */
void setProtocol(uint16_t proto) {
mPtr->h_proto = htons(proto);
}
/* ........ */
/* CONST GET METHODS */
uint16_t protocol() const {
return ntohs(mPtr->h_proto);
}
/* ........ */
private:
ethhdr *mPtr;
};
int main() {
uint8_t fakeBuffer[128];
EtherView ethView(fakeBuffer);
//OK - we want to modify because fakeBuffer isn't const
ethView.setProtocol(80);
const uint8_t *fakeConstBuff = fakeBuffer;
EtherView constEthView(fakeConstBuff);
// HERE I WANT COMPILE ERROR, because Wrapper was created with const param
constEthView.setProtocol(80);
/* I know its possible to define const wrapper:
const EtherView constEthView(fakeConstBuff);
, but I do not trust to "remember" to do it - it must be automatic. */
return 0;
}
As you can see this wrapper gives safe and importantly fast wrapper for buffer modification/reading. I've tested and it has the same performance as if buffer value modifications were done inline (because they are inlined by compiler).
Possible (not perfect) solutions so far:
1) Remember to add const for wrapper class, when const data source.
Don't want to "remember" (will eventually make a mistake)
2) Make base class (EthViewConst) with getters and construct only on const source. Then inherit that class, which constructs on non-const source and has also setter functions. If there won't be any other solutions, then this probably will be the "best" way to do this, but:
Doesn't really meet requirements. I would like to have one class, but if not possible, then its acceptable.
3) Have some special Factory, which creates const or non-const object depending on data source. "Pseudo code" example:
[EtherView view = EtherView::Create(buffer);], but I can't seem to find a way to do it, because:
returning const or non-const dynamically created Wrapper class pointer would work, but doesn't follow requirements: It must be fast. In this case it would be ralitively big cost.
returning const/non-const object doesn't work, because "receiver" can define non-const object and constantness will be lost by copy construction
returning reference can't be done for non-const object (becuase of temporary). Maybe there is some hacky way?
4) ..... any other solutions.....??
move construction?
perfect forwarding?
templates?
constexpr?
preprocessor - no thanks
This is probably complete abuse of the template mechanism, and your mileage will vary depending on if unused template methods are generated, and if they are, how your linker deals with them. I will say that the following is most likely not portable and probably insane. Luckily, you can hide behind some type aliases for safety if it doesn't port well.
This method complains extremely loudly if you use a const uint16_t* in the mutable version of the view, or a uint16_t* in the const version of the view.
#include <type_traits>
struct ethhdr {
uint8_t h_dst[6]; /* destination eth addr */
uint8_t h_src[6]; /* source ether addr */
uint16_t h_proto; /* packet type ID field */
uint8_t h_data[0];
} __attribute__((packed));
template<typename T, typename U>
class EtherView {
public:
EtherView(T* ptr) : mPtr{(U*)ptr} {}
/* SET METHODS */
void setProtocol(T proto) {
mPtr->h_proto = proto;
}
/* CONST GET METHODS */
T protocol() const {
return mPtr->h_proto;
}
private:
U *mPtr;
};
using MutableEtherView = EtherView<uint16_t, ethhdr>;
using ConstEtherView = EtherView<const uint16_t, const ethhdr>;
int main() {
uint8_t fakeBuffer[128];
MutableEtherView ethView(fakeBuffer);
ethView.setProtocol(80);
const uint8_t *fakeConstBuff = fakeBuffer;
ConstEtherView constEthView(fakeConstBuff);
MutableEtherView mutView(fakeConstBuff); // Generates error here
constEthView.setProtocol(80); // Generates error here
return 0;
}
I have a class, say
class AddElement{
int a,b,c;
}
With methods to set/get a,b,c... My question is definitely a logic question - say I implement AddElement as follows:
int Value=1;
Value+=AddElement.get_a()+AddElement.get_b()+AddElement.get_b();
Now imagine I want to do the above except 'a,b,c' are now arrays, and instead of 'adding' I do scalar addition. At runtime sometimes I need 'a' but not 'b' or 'c', so I could rewrite as:
Value+=AddElement.get_a();
(Of course the += is overloaded to represent a scalar addition... and Value is the same size as a) - Other times I might only need b or c to be added etc...
Is there a way to go about selecting which elements, a,b,c, I want to initialize and later use at runtime? ( i.e. I don't want to malloc a huge array if I'm not going to use it).
In the end I need a class that has a,b,c and then methods that can operate on any combination of a,b, or c - having the user define what methods they need at runtime (via some kind of flag, or config file).
Currently I'm doing the following:
Value+=AddElement.get_a()*FlagA+AddElement.get_b()*FlagB+AddElement.get_c()*FlagC;
where FlagA=1 if you want to use 'a' in the addition or 0 if you don't want it to be included (The same for FlagB and FlagC). This is costly if the array 'a' is very large.
I'm probably just not thinking hard enough, but this problem has been bothering me. If you need me to better define the issue I will try, but I believe this is enough to get my point across.
Edit 2
I also forgot to add that I can't use any conditionals during the implementation of the addition (this is going to be used in a CUDA kernel and I can't have any thread diverngance - I was hoping to avoid mentioning CUDA since this is entirely a c++ question)
Edit 3
I believe what I need to do is use virtual functions. I want to call the function in the same manner, except have it execute a case specific function.
Edit 4
I would appreciate if someone took a look at my solution - maybe its too 'exotic' and there's a simpler method to accomplish the same end. Thanks for all the suggestions!
Edit 5
Thanks to another user I looked at the Strategic Design Pattern - and this is exactly the solution I used for this problem. I had never heard of that before and ended up rethinking a problem that has already been done (took a while for someone to mention something about it). So the solution:
Determine Algorithm at Runtime = Strategic Design Pattern.
You provide your class with a method GetSumOfActiveElements that does just what the name says. You can make this class virtual and create subclasses for each scenario, or have the class manage the memory efficiently in some other way.
What about something like this?
vector<pair<int, bool>> values(3);
values[0].first = 1;
values[0].second = false;
values[1].first = 2;
values[1].second = true;
values[2].first = 3;
values[2].second = false;
int sum = values[0].first * values[0].second +
values[1].first * values[1].second +
values[2].first * values[2].second;
You could probably make this cleaner/extensible using functors and <algorithm>.
It's not clear to me why conditionals are a bad thing - multiplication will be more expensive I would think. Is this a CUDA limitation or idiosyncracy?
If you allowed conditionals you could make your vector member a class that encapsulated a value and an in-use flag, and use filtering algorithms to perform aggregation as required.
Does this rough outline of code work for you?
struct S{
int getx() {return 0;}
int gety() {return 0;}
int getz() {return 0;}
};
int main(){
int (S::*p[3])(); // allocate as per need
p[0] = &S::getx; // populate as per need at run time
p[1] = &S::gety;
p[2] = 0;
int val = 1;
S obj;
int nCount = 0;
while(p[nCount] != 0)
val += (obj.*(p[nCount++]))();
}
EDIT 2: #Steve Townsend: That's right. I missed that conditional stuff.
How about this.
struct S{
int getx() {return 0;}
int gety() {return 0;}
int getz() {return 0;}
S(){}
S(S &obj, int (S::*p)()){
val += (obj.*p)();
}
static int val;
};
int S::val = 0;
int main(){
S obj;
S buf[] = {S(obj, &S::getx), S(obj, &S::gety)}; // the magic happens here in
// the constructor
}
So I think I got it -
struct S{
int x,y;
bool needx,needy;
};
class AnyFunction {
protected:
S Vals;
int TotalValue;
public:
virtual void SetValues(void) =0;
virtual void AddValues(void) =0;
}
class ImplementationFunc1 : public AnyFunction {
public:
void SetValues(S * Vals) { S.x=Vals->xval; }
void AddValues(void){ TotalValue+=Vals->x; }
}
class ImplementationFunc2 : public AnyFunction {
public:
void SetValues(S * Vals) {S.x=Vals->xval;S.y=Vals->yval;}
void AddValues(void){ TotalValue+=(Vals->x+Vals->y); }
}
int main(){
S SVals;
AnyFunction * APointerToAnyFunction;
// read a file that says if we need either x or y
SVals.needx=true; // (i.e. read from file)
SVals.needy=false; // (read from file)
if(Svals.needx){
SVals.x=Xfromfile;
if (Svals.needy){
ImplementationFunc2 Imp1;
SVals.y=yfromfile;
APointerToAnyFunction=&Imp1;
}
else{
ImplementationFunc1 Imp2;
APointerToAnyFunction=&Imp2;
}
}
...
// blah set some values
...
// So now I can call the function the same way (i.e. the call is always the same, no matter what kind of addition it needs to do), but I have all
// the logic for the conditions done _outside_ the addition
APointerToAnyFunction->AddValues();
So that should basically do it! no I can use the call: "APointerToAnyFunction->AddValues()" To perform the addition. The implementation can be determined by flags at the beginning of the program, then I can write a different class for each condition that i need to satisfy, and then have my polymorphic class inherit the properties of the base class.
Sorry if I did not fully define my problem, or the statement was vague - I didn't really know exactly how to do what I was explaining, but knew it was possible. Is this the right way to go about this? Is there a more efficient way?
Thanks to all who responded. Of course when x and y are arrays, I dynamically allocate x and y when necessary...
How about a std::vector of elements?
Problem spec is a bit unclear, to say the least, but I think that would work for you.
Cheers & hth.,
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.