views:

610

answers:

5

Hi,

I was reading this morning the book The Pragmatic Programmer Chapter 3 on Basic Tools every programmer should have and they mentioned Code Generation Tools. They mentioned one Perl script for C++ programs which helped automate the process of implementing the get/set() member functions for private data members.

Does anyone know about such a script and where to find it? I've been unable to come up with the right google keywords to find it.

+3  A: 

As most C++ private members should not be accesible via Get/Set style functions, this seems like a bad idea.

For a simple example of why this is so, consider the C++ std::string class. Its private members probably look something like this (exact implementation not important):

private:
   int last, len;
   char * data;

Do you believe it makes any sense to provide get/set members for those?

anon
Really? This is the first time I read that get/set functions are not a good idea. Would you mind elaborating on why is it a bad idea? Thank you!
nmuntz
I don't think you understood the question. The variables are private so that the class can exercise control over how they are changed and has the opportunity to react to any changes, but the public interface to those variables are implemented by the get/set methods. In a language, like C++, where there is no way to retroactively add controls on mutating a member without changing the public interface, this is not a bad idea at all.
A. Levy
I understand what you mean. Not ALL private data members should have accessors and/or mutators. However, it is simple enough to just delete the methods for the few private data members you don't want to be accessible in any way. The point of a code generator is not to spend a lot of time writing mundane, automatable code. Ideally the language would allow you to declare access privileges for a member. For example, C# properties. C++ doesn't give you this, so code generators are used to supplement that deficiency.
A. Levy
You may like to ponder why C++ doesn't give you this.
anon
You encapsulate variables in a class because you want to preserve a consistency condition between them: len + ptr should indeed point to the first non-valid character in my string class, last should be ==len-1, etc. Setters/Getters break this simple principle; they are typically the result of trying to convert a C code to an 'object-oriented' one by replacing 'struct' by 'class'...
@ackb: You're right, but it's still the case that there's a lot of code out there dedicated to modeling business objects where the majority of "attributes" of an "entity" can be modified at will, and independently of each other. (E.g. customers' email addresses and telephone numbers.)
j_random_hacker
+2  A: 

I can't help you with the location of that particular script. However, there are quite a lot of code generation tools. You may even have what you are looking for already part of your IDE. For more debate/input on how, why, and whether to use code generation tools, you might look at this stackoverflow question. I like the accepted answer on that question.

A. Levy
+7  A: 

Although it doesn't directly answer your question, you may find that generated code is actually unnecessary for managing properties in C++. The following template code will allow you to declare and use properties conveniently:

// Declare your class containing a few properties
class my_class {
public:
    property<int> x;
    property<string> y;
    ...
};

...

my_class obj;
cout << obj.x();          // Get
obj.y("Hello, world!");   // Set

Here is the code:

// Utility template to choose the 2nd type if the 1st is void
template <typename T, typename U>
struct replace_void {
    typedef T type;
};

template <typename T>
struct replace_void<void, T> {
    typedef T type;
};

// Getter/setter template
template <typename T, typename D = void>
class property {
    typedef typename replace_void<D, property>::type derived_type;

    derived_type& derived() { return static_cast<derived_type&>(*this); }

public:
    property() {}   // May be safer to omit the default ctor
    property(T const& v) : _v(v) {}
    property(property const& p) : _v(p._v) {}
    property& operator=(property const& p) { _v = p._v; return *this; }

    T operator()() const { return _v; }                 // Getter
    void operator()(T const& v) { derived().check(v); _v = v; }   // Setter

protected:
    // Default no-op check (derive to override)
    void check(T const& v) const {}

private:
    T _v;
};

check() is a function that tests whether the value being assigned is valid. You can override it in a subclass:

class nonnegative_int : public property<int, nonnegative_int> {
public:
    // Have to redeclare all relevant ctors unfortunately :(
    nonnegative_int(int v) : property<int, nonnegative_int>(v) {}

    void check(int const& v) const {
        if (v < 0) {
            throw "Yikes! A negative integer!";
        }
    }
};

There you have it -- all of the advantages of externally-generated getter/setter functions, with none of the mess! :)

You could choose to have check() return a bool indicating validity instead of throwing an exception. And you could in principle add a similar method, access(), for catching read references to the property.

EDIT: As Mr. Fooz notes in the comments, the class author can later change the implementation without modifying the logical structure of the class (e.g. by replacing the property<int> x member with a pair of x() methods), although binary compatibility is lost so users will need to recompile their client code whenever such a change is made. This ability to painlessly incorporate future changes is actually the main reason people use getter/setter functions instead of public members in the first place.

Performance note: Because we are using the CRTP to achieve "compile-time polymorphism", there is no virtual-call overhead for providing your own check() in a subclass, and you need not declare it virtual.

j_random_hacker
Will that work with private data members?
nmuntz
Wow! I actually thought of using templates to emulate properties and came back to edit my answer...only to find this solution already here, and it is much more complete than the simple hack I was going to post! Thanks for sharing.
A. Levy
It's probably worth noting that the class author can change the implementation without modifying the logical structure of the class (e.g. by directly making a pair of x() methods or changing the D implementation), but binary compatibility is lost so users will need to recompile their client code whenever such a change is made.
Mr Fooz
@Mr Fooz: Excellent point, that's actually the reason people use getters/setters in the first place! I've updated the article. (Minor nit: I don't think D can be changed as the static_cast<> in derived() will fail at compile time unless D is actually a subclass of *this.)
j_random_hacker
@nmuntz: I'm suggesting that you could use a public property<foo> member **instead** of the usual combination of a private foo member plus public getter and setter methods. The *reason* people use getters/setters at all (rather than just a plain public member foo) is so that (a) newly assigned values can be checked for validity, (b) internal relationships can be maintained (e.g. assigning to square.width should update square.area) and (c) future implementations can alter how values are stored (e.g. in a DB) without requiring client code to be rewritten...
j_random_hacker
... Getters/setters deal with (b) and (c) by letting you manually write your own getter/setter functions (possibly dropping the private member if you are now getting the data from the DB) instead of using autogenerated versions. You can do exactly the same with the property<> template, which *also* allows for (a) with minimal effort (just subclass and provide your own check()). As Mr. Fooz says, you can switch back to the "traditional" getter/setter style without breaking client code.
j_random_hacker
+2  A: 

I know of one programmer who uses Perl as a replacement for the C preprocessor when it comes to macros. The basic idea is you would decide on some convention to tell your Perl script when to generate a getter or setter:

struct My_struct {
    //set
    //get
    int x;

    int y;

    //get
    int z;
};

Given code like this, I could write a script to look for comment lines consisting of the comment "get" or "set" on the line(s) before a member variable declaration, and then replace them with simple setters/getters. In the above example I would generate void set_x(int i), int get_x(), and int get_z() after the associated member variable definitions.

A word of warning: do not do this in-place with s///. Instead, scan each line individually, and if you find an appropriate comment line push something that says "I need a getter/setter" onto a stack, then when you see the associated member variable, pop things off of the stack and generate the code.

There are a few devils in the details, but overall that is the idea.

Max Lybbert
+1. Because parsing fully general C++ declarations is actually quite difficult, I'm betting your Perl "preprocessor" will recognise only a subset of relatively simple declarations, which is perfectly fine -- but I would recommend that your script aggressively complain about any declaration it can't decode so you nip any problems in the bud. It's always better to fail fast.
j_random_hacker
@j_random_hacker: Good point.
Max Lybbert
+2  A: 

You want a script to generate get/set functions for all of your private members indiscriminately? That wouldn't be a very useful script; you probably no going to find it in Google. If you want to be able to somehow tag your member variable and have getter and/or setter skeletons automatically generated for you, than a IDE macro seems more appropriate. Try Google for that.

Shing Yip