[C++] A quick replacement for get()/set() functions?

Started by
19 comments, last by Poita 16 years, 3 months ago
So I was bored and I thought about this little utility class. It's usefulness is very limited but maybe someone will still be able to benefit from it or extend it. This class is designed to automatically generate get()/set() like methods for a member variable. Here's the code:

template <class T, class R = T>
class Prop {
public:
	explicit Prop(const T &v = T()):val(v) {}

	R operator()() { return val; }
	void operator()(const T &v) { val = v; }

private:
	T val;
};

The second template parameter allows you change the return type to a const/non-const reference (see the name property in the following example). You can use it like this:

class Player {
public:
	Prop<std::string, const std::string &> name;
	Prop<int> gold;
};

void func() {
    Player player;
    player.name("John");
    player.gold(25);
    int gold = player.gold();
}

Obviously the usefulness of this class is extremely limited, because it's not customizable. For example, in the setter, you may want to check that the parameter is in a specific range. Right now I'm not sure if there's an elegant way to extend it to handle such cases (maybe with policy classes or something similar). I was hoping that maybe someone here would have an idea. Even as it is, it provides at least one benefit to simply making the members public - if you do decide you need to perform a check in the getter, you can replace this class with a custom class like in the example shown here (the URL thing isn't working for some reason): www.kirit.com/C++%20killed%20the%20get%20&%20set%20accessors Any thoughts are appreciated.
Advertisement
An overabundance of getters and setters in C++ is often a sign of bad design. Try to think in terms of state and behaviour instead. Taking your example a players name should probably be constant state, so a setter function is likely not appropriate. Equally, rather than getting and setting the gold value it is probably more appropriate to initialise the gold in the constructor and then spend and acquire, although a setter function may be appropriate as well. Doing so makes code more readable and can reduce duplication.

Some classes really are just dumb containers for data. Sometimes a POD type is appropriate in these instances, sometimes a slightly more intelligent class with getters and setters is required. Just don't fall into the trap of making everything a dumb container by default. Encapsulate behaviour as well as state.

Σnigma
Quote:Original post by Enigma
An overabundance of getters and setters in C++ is often a sign of bad design.
That's just baloney. There's firstly nothing that makes getters/setters in C++ different to Java/C#, except that people are not so good at using them. Getters and setters are often the ONLY methods on many bean-type classes, like DTO objects for instance.

Personally I use this for my get/set methods, it is more readable I think

struct Foo{  int m_X;  Foo(int a) : m_X(a) {}    inline int x()   {    return m_X;  }    inline const int& x() const   {    return m_X;  }}; int main(){  Foo bar(2);  bar.x() = 12;  std::cout << bar.x() << std::endl;}


my 2 little cents
Quote:Original post by d000hg
Quote:Original post by Enigma
An overabundance of getters and setters in C++ is often a sign of bad design.
That's just baloney. There's firstly nothing that makes getters/setters in C++ different to Java/C#,


Indeed, an overabundance of getters and setters in Java and C# is often a sign of bad design, too. Although there is something that makes them different in C#: it has language-level support for actual properties.

Quote:except that people are not so good at using them.


And they aren't that good at using them in any language (I've seen some horrible abuse in C# for example), which contributes to the design issues.

Quote:Getters and setters are often the ONLY methods on many bean-type classes, like DTO objects for instance.


I think many people would hold up "bean-type classes" as a shining example of this bad design. :/ Seriously, anything that leads to code monkeys being paid to clone-and-modify get/set pairs, and eventually to the development of code generation tools to replace them, can't really be a good thing. The emperor has no clothes here, folks. This is not real OO.

Free hint about C# property usage: just use a public data member first. Later, you can replace it with a property, without changing the calling code (the *interface* is the same). If the thought of having a public data member bothers you, the get/set pair ought to as well. If you just start out writing a property for every member (the kind of abuse I allude to above), then you (a) lie to yourself about encapsulation, if you aren't making the decision with full consciousness; (b) fall afoul of YAGNI; and (c) start out with publishing an interface that may be sub-optimal, simply because it expects the calling code to think in terms of the *state* of another object.

"DURRRR, they tolded me in teh OOP101 that data is supposed to be private, but I don't get how you're supposed to manipulate an object if you don't have access to its internals... I guess this is what they want you to do. ZOMG this is so much better than procedural programming! I get paid by the line!!!11"

</rant>
Gage64:
There are a few cases where get/set is useful, but unfortunately your utility class doesn't cover them:
- You can't have different access specifiers for the get/set (e.g. public get, protected set)
- You can't validate the input to the set operation
- Although you can change the getter to const& via the second template argument, that still doesn't prevent use of the setter to change the value.

d000hg:
It's perfectly good advice.

If you're building a class you should be thinking in terms of state/behavior - if you want a collection of data (like a c-style struct) use a class/struct with all public members.



To easily and automatically create get and set functions for your c++ instance variables go to:

http://www.houen.net/codegenerator/index.php?language=2

This site will respond to instance variables with corresponding functions. E.g.

private:
String _email;
int _num_users;

will return

//-------------QUERIES-------------
public: String get_email() {
return _email;
}
public: int get_num_users() {
return _num_users;
}
//-------------COMMANDS------------
public: void set_email(String email) {
_email = email;
}
public: void set_num_users(int num_users) {
_num_users = num_users;
}


Hope you find this useful.
PS: It also works for C#, PHP, Java and Flash actionscript
I find the idea of making some variables private, then making public functions that modify those variables directly rather awkward. The words "get" and "set" sound so generic too. Does it not make you raise an eyebrow, however much you try to justify it?
Set methods are wonderful if you use them to do validation of the assigned variable, something that isn't really supported by hacked together properly template class thingies. I often use private Set methods for variables that need validation so I don't need to repeat the same code throughout the class. Basic assignments don't get a Set method.

As for Get methods for complex objects, I generally don't like providing anyone with access to modify a class's internals, so const ref Get methods allow me to implement the super-secret idiom of "you can look but you can't touch" ;).

On the whole, I will never provide a get/set pair until I know damned well they are going to be used and there isn't a better solution instead.
I think if you need a get AND set, you need neither. If a variable needs validation, then why not create a member function make_variable( input )? Or, only send validated info to the class. If I'm not mistaken, you can CREATE a compiler warning telling the user they've bypassed your function! But, it seems to me, they probably have a reason.

Personally, I wish in C++, you could do something like this:
class a{    int x;        void a.x = ( type right ) {};}


...but now that I think of it, if I wanted to do this (really, really badly), I might try a macro.

This topic is closed to new replies.

Advertisement