Previous section   Next section

Imperfect C++ Practical Solutions for Real-Life Programming
By Matthew Wilson
Table of Contents
Part Six.  Extending C++


Chapter 35. Properties

Member variables (also known as fields) are data that are part of an object or, in the case of static member variables, belong to a class. Methods are functions that belong to object instances or, in the case of static methods, to a class. Several modern languages (C#, D, Delphi) support the concept of a property. Though using a property is syntactically the same as accessing member variables, properties are different to C++ member variables in that using them may actually involve a function call. For example, property definitions might look like[1] the Date class shown in Listing 35.1.

[1] Note that the keyword property and the get_/set_ prefixes are just inventions of mine to illustrate what might be, they do not represent any particular compiler extensions, or proposals for language enhancements.

Listing 35.1.


class Date


{


public:


  enum WeekDay { Sunday, Monday, ... };


public:


  property WeekDay      get_DayOfWeek() const;


  property int          get_DayOfMonth() const;


  property void         set_DayOfMonth(int day);


  property int          get_Year() const;


  static property Date  get_Now();


private:


  time_t  m_time;


};



There are several nice attributes that properties have over member variables. First, they can be read-only, write-only, or read-write. This provides finer grained control than is possible in C++, where a public member variable can be changed by any client code, unless it is made const:



Date          date    = Date::Now;      // calls get_Now();


Date::WeekDay weekday = date.DayOfWeek; // calls get_DayOfWeek();





date.DayOfMonth = 31; // calls set_DayOfMonth();



Second, they can be used as an overt, and readily comprehensible, conversion between internal and external types. Our Date class uses a field of type time_t, but its public interface is expressed in straightforward and usable concepts.

Third, properties can provide validation. You might define a Date type, which contains a read-write Day property. It is easy to implement the underlying property set_DayOfMonth(int day) method so as to provide range validation, for example, restricting the value to be between one and the maximum number of days for the given month and throwing an exception otherwise.

Listing 35.2.


void Date::set_DayOfMonth(int day)


{


  struct tm   tm = *localtime(&m_time);


  if(!ValidateDay(tm, day))


  {


    throw std::out_of_range("The given day is not valid for the current month");


  }


  tm.tm_mday = day;


  time_t  t = mktime(&tm);


  if(static_cast<time_t>(-1) == t)


  {


    throw std::out_of_range("The given day is not valid for the current month");


  }


  m_time = t;


}



Fourth, they separate interface from implementation. It is possible that one class may have a property that is simply a view to a member variable. Another class could provide a property that actually made an out-of-instance call to some shared system state. The caller neither knows nor cares about these details, only that the public interfaces of the types it is using are meaningful. The static property Now yields a Date instance representing the current time. The implementation of get_Now() could, if you wished, actually communicate via NTP (Network Time Protocol) to a time server and synchronize the local system time prior to returning the Date instance.

Fifth, they increase usability (read "success") of your classes, since there's less typing and fewer obfuscatory prefixes. We didn't have to write date.get_Month(), just date.Month. Hence, properties help to distinguish accessor methods from operational methods, which can represent a substantial help in the understanding of the public interfaces of nontrivial classes. Of course, this can be, should be, and usually is done by prefixing one's methods with get_ or Get in existing class definitions, but this necessary ugliness wouldn't be missed by anyone but the masochists, I would hope.

Sixth, they provide a means to enforce invariants (see section 1.3), while maintaining the syntactic compatibility—structural conformance (see section 20.9)—with existing types and client code.

Finally, they provide a modest boost to genericity (see section 35.7.1), since complex class types can be made to look like simple structures.

Having used a lot of different languages in the last year or two, I've come to really like properties, and believe that they represent a very useful abstraction. As such, I'm going to suggest that:

Imperfection: C++ does not provide properties.



      Previous section   Next section