#13312 - sajiimori - Thu Dec 11, 2003 1:05 am
I'm trying to make a class that will allow me to use simple assignment syntax for data members, while retaining the encapsulation benefits of accessor/mutator methods (similar to Properties in C#). This is what I have so far:
Code: |
template<typename T>
class Property
{
public:
Property<T>() {}
Property<T>(const T& t) : val(t) {}
virtual Property<T>& operator=(const T& newval)
{
val = newval;
return *this;
}
virtual operator T() const { return val; }
protected:
T val;
private:
Property<T>& operator=(const Property<T>&);
};
|
And you would use it like so:
Code: |
class PropertyTest
{
public:
Property<int> RegularInteger;
class SpecialInteger : public Property<int>
{
public:
SpecialInteger& operator=(int newval)
{
cout << "Changing to " << newval << endl;
val = newval;
return *this;
}
operator int() const
{
cout << "Accessing value" << endl;
return val;
}
} SpecialInteger;
};
void test()
{
PropertyTest t;
t.RegularInteger = 5;
cout << "RegularInteger is " << t.RegularInteger << endl;
t.SpecialInteger = 20;
cout << "SpecialInteger is " << t.SpecialInteger << endl;
}
|
The output is:
Code: |
RegularInteger is 5
Changing to 20
Accessing value
SpecialInteger is 20
|
It *almost* gives me a transparent syntax...except for this:
Code: |
struct Blah
{
int i;
};
void test()
{
Blah b;
Property<Blah*> p = &b;
// p->i = 5; <-- does not compile, says p is not a pointer.
(*p).i = 5; // ok
}
|
I'd like to be able to overload operator->() in Property, but I don't know how to declare the method. I tried this:
Code: |
typedef typeof(*val) DT;
DT operator->() const { return *val; }
|
...but the compiler says "invalid use of member `Property<Blah*>::val'. Even if that did work, I would expect the typedef to have problems when T is not a pointer.
Any ideas?
#13315 - dagamer34 - Thu Dec 11, 2003 1:43 am
Not sure what you are trying to do but i see the word "virtual" in your code and virtual calls are a BIG NO NO on the GBA.
I don't think many of us use both virtual methods and templates. (except those hardcore C++ fans).
Please look over at the Beginner FAQ for why i am saying this.
_________________
Little kids and Playstation 2's don't mix. :(
#13316 - sajiimori - Thu Dec 11, 2003 2:09 am
Thanks for sharing, but virtuals have not been a problem on GBA for a while now, and the template I posted will generate code that is identical to direct struct access.
BTW, it's funny that you would direct me to the beginner section, considering the questions I've seen you ask around here.
#13317 - dagamer34 - Thu Dec 11, 2003 3:10 am
I retract my previous post. (HA HA, very funny, sajiimori, poking fun at me...., hey, I tried to help and that is good, right????)
Just to let you know, i don't even know what a virutal function is... (my C++ is a little fuzzy at the moment).
Anyway, i tried and thats good enough for me.
_________________
Little kids and Playstation 2's don't mix. :(
#13326 - col - Thu Dec 11, 2003 5:19 am
I think partial template specialization might help - its worth checking out, although, i'm not sure if devkit g++ supports it yet.
after your standard tamplate, you do somthing like this:
Code: |
template<typename T>
class Property<T*>
{
//special version of your template goes here
//that is just for pointers
};
|
check out a good book for the details
cheers
Col
#13327 - sajiimori - Thu Dec 11, 2003 6:19 am
Way to go, col, you nailed it. :-)
Thanks!
Edit: I guess I also need a specialization for void*, because operator* and operator-> cause problems there (which is odd, since I didn't think those methods would be compiled unless I tried to use them).
Anyway, this is great...now I don't have to write all those getXYZ()s and setXYZ()s to get good encapsulation.
#13342 - col - Thu Dec 11, 2003 1:24 pm
sajiimori wrote: |
Way to go, col, you nailed it. :-)
Thanks!
Edit: I guess I also need a specialization for void*, because operator* and operator-> cause problems there (which is odd, since I didn't think those methods would be compiled unless I tried to use them).
Anyway, this is great...now I don't have to write all those getXYZ()s and setXYZ()s to get good encapsulation. |
In The C++ Programming Language, Stroustrup uses as an example the Vector class.
What he does is declare full specialization for void*, then he creates a partial specialization for T* in terms of a void pointer.
"The Vector<T*> class is simply an interface to Vector<void*> implemented exclusively through derivation and inline expansion."
so no code bloat :)
What I would really like to know is why you want to do this in the first place? It seems to me that in trying to get rid of accessor methods, you are losing the best part of the encapsulation they provide! and ending up with a bunch of complicated template code to debug?
If you are using basic accessor methods, each one can have a totally different underlying implementation.
Lets say in a class in your project, you have three simple integer Properties - then three months into development, you find that two of them need to be given unique underlying implementations - if you were using get/methods, its a no-brainer, you just go in and add some code to each get method.
If you use Properties, you will end up with a whole stack of work:
Either you will have new derivations of class Property to write, maintain and shoehorn into your system... Or you will have to switch to get/set methods, then go and update all the code that uses the class...
Another reason is that you are adding a custom interface to your code - any competent programmer reading the code who sees:
Code: |
int myLocal = objectPtr->getVar();
|
knows exactly what is going on...
if you start using a custom system that tries to hide its existance, you could get into trouble?
best to keep things simple right?
cheers
Col
#13343 - MumblyJoe - Thu Dec 11, 2003 1:59 pm
I have a slight solution for what col just suggested. To make the code a little more future proof and allow yourself to modify the way the variables inside of the property are handled (say you want to change a straight return to a calculation later) how about putting in a function pointer that is only called if it isn't pointing to 0?
An example (note I haven't tested this, feel free to pick on it if I missed something vital):
Code: |
template<class T>
class Property
{
public:
typedef T& (*typeintypeoutpointer)();
Property<T>() : functionforlater(0) {}
Property<T>(const T& t) : val(t), functionforlater(0) {}
void SetFunctionForLater(typeintypeoutpointer fptr)
{
functionforlater = fptr;
}
virtual Property<T>& operator=(const T& newval)
{
if(functionforlater)
newval = functionforlater(newval)
val = newval;
return *this;
}
virtual operator T() const { return val; }
protected:
T val;
private:
Property<T>& operator=(const Property<T>&);
typeintypeoutpointer functionforlater;
}
|
Then you could write a static function in the class you are writing to use the Property (has to be static to avoid the this pointer, so it will only be able to mess with static members of the class but whatever). Pass the address of said static function to SetFunctionForLater().
Of course I know this adds a little overhead (well really alot if you consider every Property now has a 32-bit function pointer and somewhere in the code an extra few bytes of code exist (a one off cost of course).
Anyway, just something I thought of while reading it, there may be something wrong with it I havent considered.
_________________
www.hungrydeveloper.com
Version 2.0 now up - guaranteed at least 100% more pleasing!
#13358 - sajiimori - Thu Dec 11, 2003 8:31 pm
Quote: |
It seems to me that in trying to get rid of accessor methods, you are losing the best part of the encapsulation they provide!
|
I'm not getting rid of accessor methods -- they're still present in operator T() and operator=(). What I am getting rid of is the need to write 2 extra methods for every read/write attribute in every class, every one of which has a different name even though they do the same thing 99% of the time.
Quote: |
and ending up with a bunch of complicated template code to debug? |
This is a matter of philosophy. I personally find it worthwhile to develop tools and utilities that will save me work later. Anyway, a couple of templates with a few inline methods each hardly qualifies as "a bunch of complicated template code".
Quote: |
If you are using basic accessor methods, each one can have a totally different underlying implementation.
|
You can do that here, too -- just overload operator T() and/or operator=(). All the benefits of seperate methods are here, and then some.
For instance, logging access is one common operation to perform within accessor methods. Using seperate methods, you would have to write new code every time you want to add logging to a particular attribute. By abstracting the process of accessing an attribute, I can simply derive a new class LoggedProperty<T> and reuse the logging functions.
Quote: |
if you were using get/methods, its a no-brainer, you just go in and add some code to each get method.
If you use Properties, you will end up with a whole stack of work
|
First, let's look at what we save in the most common case, where accessors just assign or return a value:
Code: |
class StandardAccessors
{
public:
int getI() const { return i; }
void setI(int val) { i = val; }
private:
int i;
};
class AbstractedAccessors
{
public:
Property<int> I;
};
|
Now let's look at what we have to add in the exceptional cases, when we are doing more than simply assigning or returning:
Code: |
class SpecialAccessors
{
public:
struct I : public Property<int>
{
operator int() const { do_something_weird(); }
I& operator=(int) { do_something_crazy(); }
} I;
};
|
In the general case, each Property saves a few lines of repetitive code. In the worst case, the Property code is only slightly longer than in StandardAccessors.
But terse declarations are not the primary benefit of Properties. Here's an example of the difference in usage:
Code: |
void test()
{
StandardAccessors s;
s.setI(5);
s.setI(s.getI() + 2);
AbstractedAccessors a;
a.I = 5;
a.I = a.I + 2;
}
|
Quote: |
if you start using a custom system that tries to hide its existance, you could get into trouble?
|
Another philisophical difference. I think it's better to accept a slight learning curve if it will result in significant benefits (e.g. clarity, conciseness, and consistency) than to restrict yourself to an inferior system just because it's more familiar.
Anyway, it doesn't try to hide anything. A quick glance at the class definition in the header will reveal that the attributes are not plain data members (as if anybody ever exposes data members these days).
Edit: I meant to say it doesn't try to hide its existence. It does try to hide the implementation details of accessors.
#13364 - torne - Thu Dec 11, 2003 11:24 pm
You're not Doing The Simplest Thing That Could Possibly Work. =)
Why not just make the members whose accessors are just plain assignments public? The only advantage of not doing so is the ability to change the accessor later to one that does something more clever; but in the light of DTSTTCPW I'd suggest that you worry about that later if you discover that you do want to do something more clever (and get an editor that supports refactoring, so that it's no effort to replace field access with method calls).
#13370 - sajiimori - Fri Dec 12, 2003 12:06 am
Edit: On second thought, I'm convinced that a public data member is acceptable now that I have the means to replace them with "something clever" without affecting client code (by using the template). All it would require is a recompile.
Quote: |
You're not Doing The Simplest Thing That Could Possibly Work. =)
|
The simplest thing that could possibly work is not a good ultimate goal -- it's just a good way to get started. In other words, don't add complexity too early.
Quote: |
Why not just make the members whose accessors are just plain assignments public?
|
Lack of access control, for one.
Quote: |
worry about that later
|
I think I'll save myself the trouble. ;-) If I make this utility once, I can reuse it as much as I want.
Quote: |
get an editor that supports refactoring, so that it's no effort to replace field access with method calls
|
This is the first I've heard of such a feature. Is there really an editor that can automatically globally replace all variants of this:
Code: |
void test()
{
int i;
i = 5;
SomeClass c;
c.i = c.i + 5;
}
|
...with this?:
Code: |
void test()
{
int i;
i = 5;
SomeClass c;
c.SetI(c.GetI() + 5);
}
|
#13373 - col - Fri Dec 12, 2003 12:48 am
Quote: |
...What I am getting rid of is the need to write 2 extra methods for every read/write attribute in every class, every one of which has a different name even though they do the same thing 99% of the time...
|
The only reason to make any read/write attribute anything other than public(or protected if the accessors were to be protected) is if you feel that you might have to change the underlying implementation.
Quote: |
Quote: |
and ending up with a bunch of complicated template code to debug?
|
This is a matter of philosophy. I personally find it worthwhile to develop tools and utilities that will save me work later. |
Quite the opposite - its a matter of practicallity. Compared to the time taken designing, implementing, debugging and maintaining a oop system, the time it takes to type out a get/set pair for each attribute that needs it is negligible. get/set is so simple it's fool proof - no hidden bugs, no possible portability issues, no digging around in the darkest recesses of the language specification - no worries if you find yourself working on a team with other less able/knowledgeable programmers !
Quote: |
Anyway, a couple of templates with a few inline methods each hardly qualifies as "a bunch of complicated template code" |
Any code - however small - that needs discussion on a programming forum before you can get it working is non-trivial IMO - i never saw anyone asking for help with their get/set methods :)
Quote: |
...(eg. clarity, conciseness, and consistency)
|
your earlier code snipet
Quote: |
Code: |
void test()
{
StandardAccessors s;
s.setI(5);
s.setI(s.getI() + 2);
AbstractedAccessors a;
a.I = 5;
a.I = a.I + 2;
}
|
|
demonstrates nicely how your approach rather than having clarity and consistency, displays the opposite traits:
A programmer seeing the AbstractedAccessors code will assume that a.I is plain old data - that is the conclusion they will come to - they will be in no doubt, but they will be wrong, and that is a bad thing.
The same programmer seeing the StandardAccessors code will be in no doubt either, but in this case their assumptions will be correct, and that is a good thing.
Quote: |
than to restrict yourself to an inferior system just because it's more familiar
|
IMO, the simpler and more transparent a system is, the better - you are confusing cleverness with superiority :)
Having said all this, I do like your code - it is elegant, and it certainly goes some way towards achieving your goal :)
If (and its a big if) you can make your Property template system flexible enough that they can be fully interchangable with POD, then it would be a nice alternative to current techniques. Otherwise, the fact that it looks like POD, but isn't makes it too dangerous for serious use.
hey, don't forget
"Syntactic sugar causes cancer of the semicolon."
cheers
Col
#13375 - col - Fri Dec 12, 2003 1:02 am
sajiimori wrote: |
Edit: On second thought, I'm convinced that a public data member is acceptable now that I have the means to replace them with "something clever" without affecting client code (by using the template). All it would require is a recompile.
|
I was thinking about this, but it could get you into trouble.
if you are using a public data member, and at some point, you (or somone else) decides it would be good to take its address for a pointer (in the spirit of DTSTTCPW) - then a future conversion to a Property with a fancy underlying implementation may be somewhat less straighforward :) - could leave you with lots of extra work and a big headache.
I'm thinking references should work - right?
can't think of any other impedement for now :)
cheers
Col
#13376 - sajiimori - Fri Dec 12, 2003 1:07 am
Quote: |
The only reason to make any read/write attribute anything other than public(or protected if the accessors were to be protected) is if you feel that you might have to change the underlying implementation.
|
Which is to say, "always". That is, unless you have a way to transparently replace a public data object with a class (like I'm doing).
Quote: |
Compared to the time taken designing, implementing, debugging and maintaining a oop system, the time it takes to type out a get/set pair for each attribute that needs it is negligible.
|
I dunno...there are a hell of a lot of get/set methods out there. Honestly, I've spent an order of magnitude more time typing in this thread than working on the code (which is about a page in length).
Quote: |
Any code - however small - that needs discussion on a programming forum before you can get it working is non-trivial IMO
|
I didn't say it's trivial, just that it is neither large nor complicated. ;-) I mean, how much work did it take to come up with E=mc^2?
Quote: |
A programmer seeing the AbstractedAccessors code will assume that a.I is plain old data - that is the conclusion they will come to - they will be in no doubt, but they will be wrong, and that is a bad thing.
|
Yeah, it would be bad because such a programmer must be too lazy to even glance at the class definition, and nobody would want such lazy people on their team.
I'm of the unusual mindset that code shouldn't pander to the dumbest person who might try to read it. ;-)
Quote: |
Otherwise, the fact that it looks like POD, but isn't makes it too dangerous for serious use.
|
Hey, tell it to the C# design team. It was their idea, and a lot of people like it.
Edit: Good point about taking pointers of public data...I hadn't thought of that.
#13377 - MumblyJoe - Fri Dec 12, 2003 1:18 am
I guarantee if that Property method was part of the std library you would all love it :P
_________________
www.hungrydeveloper.com
Version 2.0 now up - guaranteed at least 100% more pleasing!
#13379 - sajiimori - Fri Dec 12, 2003 1:48 am
No way! I *hate* the standard library. lol
#13381 - torne - Fri Dec 12, 2003 2:40 am
sajiimori wrote: |
The simplest thing that could possibly work is not a good ultimate goal -- it's just a good way to get started. In other words, don't add complexity too early. |
Not really. I always use the simplest thing at every stage. As requirements change, the simplest thing that will fit the requirements might get more complicated, but it's still the simplest thing.
Quote: |
Quote: |
Why not just make the members whose accessors are just plain assignments public?
|
Lack of access control, for one. |
Surely making the member public/protected/friended..etc gives you exactly the same access control as doing so to the methods?
Quote: |
Quote: |
get an editor that supports refactoring, so that it's no effort to replace field access with method calls
|
This is the first I've heard of such a feature. Is there really an editor that can automatically globally replace all variants of this:
Code: |
void test()
{
int i;
i = 5;
SomeClass c;
c.i = c.i + 5;
}
|
...with this?:
Code: |
void test()
{
int i;
i = 5;
SomeClass c;
c.SetI(c.GetI() + 5);
}
|
|
Not exactly. Refactoring support for C is difficult, because the C language (and all its derivatives) have poorly defined implementation semantics, and are obfuscated by the use of a textual (rather than semantic) preprocessing stage. The Eclipse CDT guys are working on it; not sure how far they've got. However, use of cscope-like tools (or Visual Studio) can often find all but the most obfuscated of references for you; it's then pretty trivial to automate their replacement (with human supervision to make sure that all located references are indeed things you want to replace). I've used Vim and cscope to make substitutions like the one you give there; it's not automatic, but it's much faster than doing it with a textual find.
If you'd like a fully automatic C refactoring tool comparable to Eclipse's JDT, or the Smalltalk refactoring browser, go help the CDT developers. I'll eventually be working on an asm refactoring editor, and it would be pretty funny if that was finished first. =)
#13382 - torne - Fri Dec 12, 2003 2:42 am
Having to make these kinds of design decisions is exactly why I rarely program in C; the language is too viscous, so you have to decide on many aspects of design 'up front', as it's too hard to change them later. I prefer the evolutionary approach that can be easily used in languages like Smalltalk, Java and Forth; design should be fluid and easy to change, to allow for changing requirements.
#13383 - col - Fri Dec 12, 2003 4:07 am
sajiimori wrote: |
...
I'm of the unusual mindset that code shouldn't pander to the dumbest person who might try to read it. ;-)
|
I am of the unusual mindset that if you use language conventions, the development process will be less error prone and generally smoother than if you use non-standard 'improved' mechanisms ;-)
Quote: |
Quote: |
Otherwise, the fact that it looks like POD, but isn't makes it too dangerous for serious use.
|
Hey, tell it to the C# design team. It was their idea, and a lot of people like it.
|
Its less likely to be an issue in C# because its part of the language - so folks know about it !
whatever,
I think a good thing would be to go and float this idea at comp.lang.c++.moderated - some of the best brains in c++ are active there, so you should get some good crit.
There are some pretty big threads there about the evils of the get/set idiom, so I guess you might find out if others have tried a similar approach to yours as well.
maybe there is already a fully featured version available, or maybe there are some nasty gotchas that havn't bitten you yet,
or possibly, people have been waiting for exacly this idea...
cheers
col
#13405 - sajiimori - Fri Dec 12, 2003 7:25 pm
Quote: |
I always use the simplest thing at every stage. As requirements change, the simplest thing that will fit the requirements might get more complicated, but it's still the simplest thing.
...
design should be fluid and easy to change, to allow for changing requirements.
|
What you say makes sense because one of your requirements is that the code be adaptable to new requirements. The reason I disagreed initially is because I thought 'requirements' only meant the things that meet your immediate needs, in which case the simplest thing that meets your immediate needs might be harder to adapt later.
Quote: |
Surely making the member public/protected/friended..etc gives you exactly the same access control as doing so to the methods?
|
No, because none of public/protected/friended allows read access for the outside world and read/write access for the class.
Quote: |
I am of the unusual mindset that if you use language conventions, the development process will be less error prone and generally smoother than if you use non-standard 'improved' mechanisms ;-)
|
I'd have to say that depends on how hard it is to learn the new utility, and how significant the benefits are of using it. Look at the Boost library -- it uses lots of weird tricks, but people adopted it anyway because its usefulness outweighs the learning curve.
Ok, I'm done with this thread. :-)
It will please many of you to know that I've abandoned the idea. I couldn't find a way to make it truly transparent -- C++ is too rigid.
My new project is to develop a language that is somewhat like Lisp in its consistency and flexibility, but uses explicit memory management, static typing, and has the low-level features of C. Unless somebody knows of a language like that...?
(When I refer to Lisp's "consistency", I'm referring to the fact that there is no fundemental distinction between declarations, statements, expressions, and data, and by "flexibility" I essentially refer to its macros, which are programs that write programs.)
#13406 - torne - Fri Dec 12, 2003 8:14 pm
sajiimori wrote: |
What you say makes sense because one of your requirements is that the code be adaptable to new requirements. The reason I disagreed initially is because I thought 'requirements' only meant the things that meet your immediate needs, in which case the simplest thing that meets your immediate needs might be harder to adapt later. |
No, that's exactly the philosophy that DTSTTCPW is against. 'Easy to adapt' is not a requirement. I ignore it, until there is a specific way in which it needs to be adapted, then make the smallest possible change to implement the new requirement. I have never found any piece of code, even horrible, hideous undocumented code written by someone else, to be hard to adapt if sensible refactoring steps are applied; designing for possible change seems broken to me.
#13407 - sajiimori - Fri Dec 12, 2003 8:25 pm
Quote: |
design should be fluid and easy to change
|
Quote: |
designing for possible change seems broken to me.
|
I guess I am not understanding something.
#13411 - torne - Fri Dec 12, 2003 10:50 pm
sajiimori wrote: |
Quote: |
design should be fluid and easy to change
|
Quote: |
designing for possible change seems broken to me.
|
I guess I am not understanding something. |
That was a bit, uhm, unclear, yes. What I mean is that you don't design something to make it easy to change; you design it to be the simplest thing that works. When it comes time to change it, then you can use refactoring knowledge (and tools if you have them) to do so easily. It is not neccecary to anticipate the change when you wrote it originally.
For example, I'm writing an assembler; I have a class for an instruction, and many subclasses of it representing specific kinds of instruction (move, compare, load..etc). It was all ARM-specific; the Instruction class assumed that instructions were all 32 bits wide, for example. No planning for change there. =)
I then decided later that I wanted it to be architecture independent; thus it was neccecary to introduce a class between Instruction and the specific kinds of ARM instruction. Doing so using refactoring (and supported by unit tests; automated tests that verify hundreds of aspects of your code in typically under a second) was trivial:
1) Apply Rename Class to turn Instruction into ARMInstruction
2) Create a new empty class called Instruction
3) Change the superclass of ARMInstruction to be Instruction instead of Object
4) Apply Extract Method to split two methods that contained both platform independant and ARM-specific code
5) Apply Pull Up to move all arch-independant methods and fields into the Instruction class
6) Apply Extract Abstract Method a few times to create abstract methods in the supertype that match ARM-specific methods in the subtype that nonetheless should be present for all instructions (e.g. the method that returns this instruction in assembled form as a byte array)
95% of this was automatic and verified as safe by the refactoring tools in Eclipse; the remainder was manual but trivial (like steps 2 and 3).
This took two minutes to make a large change to the structure of my class hierarchy, and the assumptions that were underlying the design, despite not having planned for change. That's how come design can be fluid and easy to change, and yet you can ignore future changes when designing the code you're writing now. =)
#13451 - sajiimori - Sat Dec 13, 2003 6:56 pm
So, you organized your program into hierarchy of classes initially. Is that really the simplest way? Couldn't you have just laid out your program as a flat set of functions? Or simpler yet, all as one procedure? After all, converting assembly mnemonics to binary opcodes is just one big procedure in the end.
I think you take it for granted that the traditional OO style is the "simplest" one, when in fact it is merely the one you are most comfortable with. You are presumably comfortable with it because it is so common. It is common because it has been successful. But the reason it has been so successful is because it tends to lead to adaptable programs!
Edit: I forgot to make my point. My point is that you *are* writing programs for adaptability, even though you aren't thinking about it. By trying to write a good OO program, you are implicitly trying to make your programs adaptable.
#13453 - torne - Sat Dec 13, 2003 7:35 pm
sajiimori wrote: |
So, you organized your program into hierarchy of classes initially. Is that really the simplest way? Couldn't you have just laid out your program as a flat set of functions? Or simpler yet, all as one procedure? After all, converting assembly mnemonics to binary opcodes is just one big procedure in the end. |
Yes, I did exactly that. My code was all in main() to start with; parts got factored out into new functions, then groups of functions factored out into classes, then classes turned into hierarchies, as the requirements became more complex. This is the approach I'm recommending.
Quote: |
Edit: I forgot to make my point. My point is that you *are* writing programs for adaptability, even though you aren't thinking about it. By trying to write a good OO program, you are implicitly trying to make your programs adaptable. |
I disagree. I have never found the OO style to be either an aid or a hinderance to future change; I merely find it quicker to refactor in an OO form. I do think about adaptability; and every time I do, I remind myself to do the simplest thing that can possibly work instead.
#13455 - sajiimori - Sat Dec 13, 2003 7:39 pm
Quote: |
I have never found the OO style to be either an aid or a hinderance to future change; I merely find it quicker to refactor in an OO form.
|
Those statements seem contradictory. OO is not an aid to change, but it's quicker to change OO programs?
#13472 - Andy Friesen - Sun Dec 14, 2003 8:52 am
Nice!
Too bad you lose 'this', though, since you create a new scope. (since all properties have to be subclasses)
Also, if virtual methods really scare you that much, you can use more template-fu to use yet another helper struct instead of polymorphism. I wound up coding such a class for kicks: http://ikagames.com/andy/property.h
The main advantage of this approach is that the get and set methods can be inlined, which could be a big win if you're doing something like y-sorting sprites that are frequently moving.
_________________
"Tell a man there are 300 billion stars in the universe and he'll believe you. Tell him a bench has wet paint on it and he'll have to touch it to be sure."
#13475 - col - Sun Dec 14, 2003 2:13 pm
nice code.
big question is:
Did you use this code extensively in any projects, and if so, do you feel that there were any significant productivity gains as a result?
cheers
Col
#13477 - Andy Friesen - Sun Dec 14, 2003 8:28 pm
Not in C++. I coded it in about an hour last night. (in response to this very post, in fact)
In other languages, I use properties all the time. The C# winforms API is marvelously straightforward thanks to them. Reading winforms code is infinitely easier than SWING. (I suppose that's subjective, though)
I use them extensively in Python too, where the dynamic nature makes me deplorably lazy when it comes to encapsulation. I get away with it because I can create a property that behaves in every way like the public variable it used to be.
Significant productivity gains? I think that it would be an immense help if a project were being coded by more than one person. sprite.y++; looks much more obvious to me than sprite.setY(sprite.getY() + 1);. It's less of a hassle to type too. :)
You can also play games with 'simple' structures like rectangles, which could be expressed either as two corner points, or a corner and width/height. Properties allow you to use a rect as whichever at any given moment, depending on what's more convenient at the time.
_________________
"Tell a man there are 300 billion stars in the universe and he'll believe you. Tell him a bench has wet paint on it and he'll have to touch it to be sure."