Previous section   Next section

Imperfect C++ Practical Solutions for Real-Life Programming
By Matthew Wilson
Table of Contents
Chapter 35.  Properties


35.4. Method Properties

The picture gets a whole lot more complex when we look at method properties, so you might want to take a short nap or get a cup of your caffeine delivery system of choice before continuing.

There are six kinds of method properties, representing the permutations of internal or external, and read and/or write. The first three we examine, the internal read, write, and read/write, actually represent a hybrid of field and method properties, since they contain within themselves the field that backs up the property's value. I'm dealing with them first since they have the more straightforward implementation and are the most commonly used.

The last three represent the external read, write, and read/write situations, and are purely method based. As with external field properties, there is a waste of space, and it is in chasing this down to a minimum that we have some fun at the dark edges of the language.

35.4.1 method_property_get

A read-only internal method property has the following characteristics:

  • An internal field that backs up the property's value

  • Access to the private internals of the property to the containing class via a friendship declaration

  • An implicit conversion operator

  • A pointer to an instance (nonstatic) method of its containing class to which is delegated the value access of the implicit conversion operator

  • A pointer (or reference) to the containing class instance on which to apply the delegated function

Providing the internal field and the friendship are boilerplate to us now. However, the other three represent something more of a challenge. A straightforward implementation could pass pointers to the containing instance and the member function in the constructor, and then call them from within the conversion operator, as in:



template < . . . >


class method_property_get


{


  . . .


  operator R() const


  {


    return (m_object->*m_pfnGet)();


  }


  . . .


};



However, a quick mental arithmetic will tell us that this will triple (at least) the size of the property. In fact, some compilers require several machine words to represent a pointer to a member function, so this can lead to considerable space penalties. It's a shame, of course, since such an implementation would be simple, whereas the one we have is not at all. It is highly portable and very efficient though, so do keep reading.

Let's look more closely at the two overheads that we want to eliminate: the containing-class pointer and the member-function pointer. Since a property field will be located within the containing class, why not simply deduce the class pointer from the this pointer of the property? Passing the offset to the property instance would simply add a member variable, however, which defeats the purpose of eliding space inefficiencies. One way around this would be as a static member of the property class. However, this would mean that each property would have to be a separate class. Unless we want to do a lot of repetitious coding, we'd have to use macros. And even if using macros we'd need to define the static offset member in an implementation file, which just adds to the hassle in using the properties.

How much better would it be if we could make the offset part of the type itself? Well, since we're using templates, wouldn't it be great if we could instead use the offset as part of the template parameterizations, as in:

Listing 35.9.


class Date


{


public:


  WeekDay get_DayOfWeek() const


  {


    return WeekDay.m_value;


  }


public:


  method_property_get<WeekDay


                      . . .


                    , offsetof(Date, DayOfWeek)


                      . . .


                    >                         DayofWeek;



The parameterizing method get_DayOfWeek() needs to be declared public, but this is a perfectly acceptable situation. There's no reason why the client code should be prevented from using the method for which the property merely acts as a syntactic proxy, so we don't have to worry about declaring it private. This looks nice and neat.

Alas, the real world nips at our hamstrings here. Neglecting the strict legality and portability issues of offsetof() (see section 2.3.3) for the moment, the fact is that it cannot deduce the offset of an incomplete type in its enclosing type. At the time of instantiation of method_property_get<WeekDay, ..., offsetof(Date, DayOfWeek)> it is not complete. Hence, its offset cannot be determined. Hence it cannot be used to instantiate the template. Hence...well I think you get the idea. So our elegant—well I think it's elegant, anyway—notion is invalidated. Thankfully there's a very simple alternative: static methods. Now we can break the instantiation and the offset calculation into two phases:

Listing 35.10.


class Date


{


public:


  WeekDay get_DayOfWeek() const;


private:


  static ptrdiff_t  WeekDay_offset()


  { return offsetof(Date, DayOfWeek); }


public:


  method_property_get<WeekDay


                      . . .


                    , &WeekDay_offset


                      . . .


                    >                         DayofWeek;



Since the instantiation is now carried out on a function pointer, there is no need to attempt to calculate the offset at the time of instantiation, so it all works just fine. The function is called at run time (notionally at least; see below) to elicit the offset, which is then used to adjust the this pointer to get hold of a pointer to the containing class on which the member function is called. Which brings us to the last part of the puzzle.

Since we've parameterized the template on a method for the offset, why can't we just do the same for the function that actually implements the property access? Well, the answer is that we can, at least on modern compilers.

The offset method is a class (static) method, and is accepted as a template parameter by a very wide range of compilers. As far as we're concerned here, class methods are equivalent to free functions. Instance (nonstatic) methods are a different kettle of fish altogether [Lipp1996]. Although nonvirtual instance methods are treated by the linker in the same way as class methods and free functions, to C++ client code the this pointer of the instance on which the method is called is passed implicitly. It is this additional complication that seems to stymie the template interpretation of several compilers. Borland (5.5, 5.6), CodeWarrior 7, Visual C++ 7.0 and earlier, and Watcom all fail to compile such constructs. However, CodeWarrior 8, Comeau, Digital Mars, GCC, Intel, VectorC, and Visual C++ 7.1 all compile them without qualms.

Enough chat; let's look at the class (shown in Listing 35.11).

Listing 35.11.


template< typename V              /* The actual property value type */


        , typename R              /* The reference type */


        , typename C              /* The enclosing class */


        , ptrdiff_t (*PFnOff)()   /* Pointer to fn providing offset of property in


 container */


        , R (C::*PFnGet)() const  /* Pointer to a const member function returning R */


        >


class method_property_get


{


public:


  typedef method_property_get<. . .>  class_type;


private:


  method_property_get()


  {}


  explicit method_property_get(R value)


    : m_value(value)


  {}


  DECLARE_TEMPLATE_PARAM_AS_FRIEND(C);


// Accessors


public:


  /// Provides read-only access to the property


  operator R() const


  {


    ptrdiff_t offset  = (*PFnOff)();


    C         *pC     = (C*)((byte_t*)this - offset);


    return (pC->*PFnGet)();


  }


// Members


private:


  V m_value;


// Not to be implemented


private:


  // This method is hidden in order to prevent users of this class from


  // becoming familiar with using operator = on the property instances


  // from within the containing class, since doing so with


  // method_property_getset<> would result in an infinite loop.


  class_type &operator =(R value);


};



The template has five parameters. V, R, and C represent the value type, the reference type, and the containing class type. The fourth parameter is the function that provides the offset of the property. It's simply declared as taking no parameters and returning a value of type ptrdiff_t. Note that we do not use size_t here, even though that's the type of the result of offsetof(), because we're deducing an offset back into the containing class from the property member. Applying a unary minus to an unsigned variable gives an unsigned result, which would point our class instance somewhere at the top of memory; not the desired result, to say the least! I've taken an early intervention approach here, mandating the offset functions return ptrdiff_t, rather than having to remember to cast appropriately in the property implementation(s).

The fifth parameter, which at first glance is the type of inscrutable gobbledygook that often gets C++ a bad name, is actually quite straightforward. It merely declares that PFnGet is a pointer to a const member function of C, which takes no arguments, and returns a value of type R.

We can now revisit our notional Date type described at the start of the chapter, and start plugging some real techniques into it. Let's imagine for this case that Date caches the values of the various date/time components in each of its properties. We'll see the single-member/ multiple-properties version when we look at external properties (see sections 35.4.5.–35.4.7).

Listing 35.12.


class Date


{


public:


  WeekDay get_DayOfWeek() const


  {


    return DayOfWeek.m_value;


  }


private:


  static ptrdiff_t  DayOfWeek_offset()


  { return offsetof(Date, DayOfWeek); }


public:


  method_property_get<WeekDay, WeekDay


                    , Date


                    , &DayOfWeek_offset, &get_DayOfWeek


                    >                         DayofWeek;


  . . .


};



I've defined a private static offset method, DayOfWeek_offset, which provides the necessary offset. The DayOfWeek property is defined as a method_property_get, which has value and reference type of WeekDay, uses the offset method, and implements read access via the get_DayOfWeek method. Though this is now a reasonably portable solution, and all the requisite pieces are defined within close proximity, it leaves a bit to be desired in the amount of mess surrounding the property definition.

But make no mistake. The cost of this technique is only in developer effort, and readability. The proof of such techniques is in their performance. And it performs very well indeed. On all compilers tested, the space penalty is zero. In terms of speed, all the compilers are similarly able to optimize out both the call to the offset method and the nonvirtual member function, so there's no speed cost either. It is 100 percent efficient.

Most compilers are content with this syntax, but Digital Mars and GCC have a brain-freeze with it. Digital Mars cannot use a private method to parameterize the property template, and GCC must have the fully qualified method names, so you'll need to use the following form if you want to be portable:

Listing 35.13.


class Date


{


public:


  WeekDay get_DayOfWeek() const;


  . . .


public:


  static ptrdiff_t  DayOfWeek_offset() { return offsetof(Date, DayOfWeek); }


public:


  method_property_get<WeekDay


                    , WeekDay


                    , Date


                    , &Date::DayOfWeek_offset


                    , &Date::get_DayOfWeek


                    >                              DayofWeek;



Even the former version is hardly what one could call succinct, and this one's even worse. Given that the definition of the offset method is, to users of the containing class, nothing but a distraction, one answer to all this is to use a macro (with a suitably heavy heart, of course).

Listing 35.14.


#define METHOD_PROPERTY_DEFINE_OFFSET(C, P) \


                                            \


  static ptrdiff_t P##_offset##C()          \


  {                                         \


    return offsetof(C, P);                  \


  }





#define METHOD_PROPERTY_GET(V, R, C, GM, P) \


                                            \


  METHOD_PROPERTY_DEFINE_OFFSET(C, P)       \


                                            \


  method_property_get<  V                   \


                      , R                   \


                      , C                   \


                      , &C::P##_offset      \


                      , &C::GM              \


                      >         P



Now using the property is quite clean:



class Date


{


public:


  WeekDay get_DayOfWeek() const;


  . . .


public:


  METHOD_PROPERTY_GET(WeekDay, WeekDay, Date, get_DayOfWeek, DayOfWeek);


  . . .



Note that the macro does not affect the access of the offset method. I know it seems quite popular these days to do so—mentioning no names, you understand—but I don't like placing access control specifiers into macros. Macros are bad enough at hiding things; masking access control seems way beyond the pale. It's possible, though not likely, that you might wish to make a property protected, so to stipulate public in the macro would be a mistake. In any case, making hidden accessibility changes violates the principles outlined in section 17.1. The slight downside is that the offset method will be public, and on some code sensing development environments this will clutter the containing class "interface," but that's insufficient motivation to make hidden access control changes.

It could even be made a little bit clearer, if we were to use the class_type idiom from section 18.5.3 to replace the macro parameter C, as this would obviate the need to specify the class type to the macro:



class Date


{


public:


  typedef Date class_type;


  . . .


  METHOD_PROPERTY_GET(WeekDay, WeekDay, get_DayOfWeek, DayOfWeek);



Despite this improvement, the fuller definition is the one used. Even though I religiously define a class_type, it's not a widely accepted practice, and METHOD_PROPERTY_GET will be less intrusive if it does not make extra demands on its user to change other parts of their classes.

35.4.2 method_property_set

There's really very little to say about the write-only version, given what we've seen with field_property_set and method_property_get, so I'll just show you the definition of the method_property_set template:

Listing 35.15.


template< typename V            /* The actual property value type */


        , typename R            /* The reference type */


        , typename C            /* The enclosing class */


        , ptrdiff_t (*PFnOff)() /* Pointer to fn providing offset of property in container */


        , void (C::*PFnSet)(R ) /* Pointer to a member function taking R */


        >


class method_property_set


{


  . . .


public:


  /// Provides write-only access to the property


  class_type &operator =(R value)


  {


    ptrdiff_t offset  = (*PFnOff)();


    C         *pC     = (C*)((byte_t*)this - offset);


    (pC->*PFnSet)(value);


    return *this;


  }


// Members


private:


  V m_value;


};



It employs the same mechanism of using static offset method and instance assignment method.

35.4.3 method_property_getset

We know that a combination of the read-only and write-only field properties is a waste of time, but the same cannot be said of method properties, where the read and/or write methods can provide translation and validation. Hence the logical step is to combine the definitions of the method_property_get and method_property_set templates, into the method_property_getset template, as shown in Listing 35.16.

Listing 35.16.


template< typename V              /* The actual property value type */


        , typename RG             /* The get reference type */


        , typename RS             /* The set reference type */


        , typename C              /* The enclosing class */


        , ptrdiff_t (*PFnOff)()   /* Pointer to function providing offset */


        , RG (C::*PFnGet)() const /* Pointer to a const member function


        returning R */


        , void (C::*PFnSet)(RS)   /* Pointer to a member function taking R */


        >


class method_property_getset


{


  . . .


// Accessors


public:


  /// Provides read-only access to the property


  operator RG () const


  {


    ptrdiff_t offset  = (*PFnOff)();


    C         *pC     = (C*)((byte_t*)this - offset);





    return (pC->*PFnGet)();


  }


  /// Provides write-only access to the property


  class_type &operator =(RS value)


  {


    ptrdiff_t offset  = (*PFnOff)();


    C         *pC     = (C*)((byte_t*)this - offset);


    (pC->*PFnSet)(value);


    return *this;


  }


// Members


private:


  V m_value;


};



Other than the fact that there are two instance method pointers, in addition to the single offset class method pointer, the other notable difference is that there are two reference type parameters, one for writing and one for reading. This is the case where the value is to be assigned as one type and accessed by another, as was mentioned when we discussed method_property_get.

The corresponding convenience macro is defined thus:



#define METHOD_PROPERTY_GETSET(V, RG, RS, C, GM, SM, P) \


                                                        \


  METHOD_PROPERTY_DEFINE_OFFSET(C, P)                   \


                                                        \


  method_property_getset< V                             \


                        , RG                            \


                        , RS                            \


                        , C                             \


                        , &C::P##_offset                \


                        , &C::GM                        \


                        , &C::SM                        \


                        >       P



Let's look at another property for our Date class, one that uses a read/write property:

Listing 35.17.


class Date


{


public:


  int   get_DayOfMonth() const


  {


    return DayOfMonth.m_value;


  }


  void  set_DayOfMonth(int day)


  {


    if(!ValidateDay(. . ., day)) // Do some validation


    . . .


  }


  . . .


  METHOD_PROPERTY_GETSET(int, int, int, Date, get_DayOfMonth, set_DayOfMonth,


  DayOfMonth);


  . . .


};



I hope by now you're starting to see how kind of neat this stuff can be.

35.4.4 Loop the Loop!

One point worth noting is that in the original versions of the field_property_set and method_property_set templates I defined a private implicit conversion operator, in order that the containing class implementation could have the convenience of access to the property's internal value, as in:

Listing 35.18.


template <. . .>


class method_property_set


{


private:


  operator R () const


  {


    return m_value;


  }


  . . .


};





class Container


{


public:


  method_property_set<int, . . .>   Prop;


public:


  void SomeMethod()


  {


    int pr_val = Prop; // Uses the implicit conversion operator


  }


  . . .


};



I'm sure you've guessed the gotcha. When using the method_property_getset, such a call would call back into the containing class to acquire the proxied value. If the implicit conversion was inside that method, we're in an infinite loop. Naturally, it seemed prudent to remove this facility from all other classes, in which it was harmless, for consistency. The same thing could apply in the reverse case so the assignment operator is also removed from get property templates.

35.4.5 method_property_get_external

External method properties are used when the containing class needs to represent the public face of its internal data in a different form. Our original Date class is a very good example of this. The implementations of the external method properties borrow the same offset and member function techniques from the internal equivalents, but are simplified since they do not have any fields themselves. We'll use that fact to our advantage later.

The method_property_get_external property template is defined as follows:

Listing 35.19.


template< typename R              /* The reference type */


        , typename C              /* The enclosing class */


        , ptrdiff_t   (*PFnOff)() /* Pointer to fn providing offset of property in


 container */


        , R (C::*PFnGet)() const  /* Pointer to a const member function returning R */


        >


class method_property_get_external


{


public:


  typedef method_property_get_external<R, C, PFnOff, PFnGet>  class_type;


public:


  /// Provides read-only access to the property


  operator R () const


  {


    ptrdiff_t offset  = (*PFnOff)();


    C         *pC     = (C*)((byte_t*)this - offset);


    return (pC->*PFnGet)();


  }


// Not to be implemented


private:


  // This method is hidden in order to prevent users of this class from


  // becoming familiar with using operator = on the property instances


  // from within the containing class, since doing so with


  // method_property_getset<> would result in an infinite loop.


  class_type &operator =(R value);


};



This can be plugged into the original version of the Date class, using the appropriate macro, as in:

Listing 35.20.


class Date


{


public:


  . . .


  WeekDay get_DayOfWeek() const


  {


    return static_cast<WeekDay>(localtime(&m_time)->tm_wday);


  }


// Properties


public:


  METHOD_PROPERTY_GET_EXTERNAL(WeekDay, Date, get_DayOfWeek, DayOfWeek);


private:


  time_t  m_time;


};



The property has no fields of its own, and delegates access to Date's get_DayOfWeek() method. get_DayOfWeek() uses the m_time member to convert to the day of the week. (Remember, this is for illustrative purposes; it's not exactly an efficient way of implementing a date/time class.)

External method properties are very flexible, by virtue of the fact that they implement a decoupling between the containing class's fields and its public (property) interface. They are just as speed efficient as internal method properties, since the mechanism is virtually identical. The one problem is that, as we discussed earlier in the chapter, they have non-zero size, even though they have no member variables—there's no EMO (see section 35.2.2). The minimum size that the compiler can give them is, of course, one byte. Hence, our Date class increases by 1 byte per property. This is not appealing.

However, remember that we saw in section 1.2.4 how a union can be used to constrain types to be POD (see Prologue). We can use this in reverse. Since our parameterized external method templates have no members, and do not derive from any other types (which might have various non-POD attributes), we are free to place them in a union. Hence,



class Date


{


// Properties


public:


  union


  {


    METHOD_PROPERTY_GET_EXTERNAL(WeekDay, Date, get_DayOfWeek, DayOfWeek);


    METHOD_PROPERTY_GET_EXTERNAL(int, Date, get_Year, Year);


    . . .


  };





};



There's still a price to be paid, since the union itself cannot be given a zero size. But the price is minimal—1 byte, of course—and is paid once, irrespective of how many properties there are. It's not perfect, but it's pretty close[6].

[6] In fact, you could share the union with one of the enclosing class's members, as long as it is of POD type (see Prologue), and thereby achieve zero overhead.

There's one slight complication—isn't there always?—in that the macro-union composition shown above would not work. Remember that the macros contain within themselves the offset function. Clearly, it is not valid to define the offset function within the union, as it will then be a member of the union, and not the containing class. Since the union is anonymous, we'd have a hell of a time determining its name for the other parts of the macro in a portable way. This can be solved by separating the offset function definition and the property definition into two macros. We saw the METHOD_PROPERTY_DEFINE_OFFSET() macro in section 35.4.1, and the other is defined as follows:



#define METHOD_PROPERTY_GET_EXTERNAL_PROP(R, C, GM, P)  \


                                                        \


  method_property_get_external< R                       \


                              , C                       \


                              , &C:: P##_offset##C()    \


                              , &C::GM                  \


                              >       P



Armed with these macros the Date class can be rewritten as:

Listing 35.21.


class Date


{


// Properties


public:


  METHOD_PROPERTY_DEFINE_OFFSET(Date, DayOfWeek)


  METHOD_PROPERTY_DEFINE_OFFSET(Date, Year)


  union


  {


    METHOD_PROPERTY_GET_EXTERNAL_PROP(WeekDay, Date, get_DayOfWeek, DayOfWeek);


    METHOD_PROPERTY_GET_EXTERNAL_PROP(int, Date, get_Year, Year);


    . . .


  };


  . . .


};



And that's it! It's speed efficient, and it only wastes 1 byte per containing class, irrespective of the number of properties.

35.4.6 method_property_set_external

Once again, we've seen enough in the previous property types to comprehend the implementation of method_property_set_external, so I'll just show it to you:

Listing 35.22.


template< typename R            /* The reference type */


        , typename C            /* The enclosing class */


        , ptrdiff_t (*PFnOff)() /* Pointer to function providing offset in container */


        , void (C::*PFnSet)(R ) /* Pointer to a member function taking R */


        >


class method_property_set_external


{


public:


  typedef method_property_set_external<R, C, PFnOff, PFnSet>  class_type;


public:


  /// Provides write-only access to the property


  method_property_set_external &operator =(R value)


  {


    ptrdiff_t offset  = (*PFnOff)();


    C         *pC     = (C*)((byte_t*)this - offset);





    (pC->*PFnSet)(value);





    return *this;


  }


};



Although we've struggled to think of a real use for the previous write-only property types, this type may well have a compelling application. When dealing with container types, it is often the case that the container maintains two separate concepts regarding its element storage. The size of the container indicates how many elements it contains, whereas the capacity indicates how much memory is allocated for actual and potential element storage. Although most modern container libraries that provide public access to the capacity provide both read and write access (e.g., std::vector's reserve() and capacity()), there's a school of thought that says that allowing client code access to the amount of storage of a container it is using is a breaking of that container's encapsulation and a coupling of the client code and the container's internal implementation. One (somewhat contrived, I admit) way to enable the client code to advise the container of the demands of its context, without becoming dependent on the advice, would be to implement a write-only Capacity property, using the method_property_set_external template, as in:

Listing 35.23.


class Container


{


public:


  size_t get_Size() const;


  void   set_Capacity(size_t minElements);


// Properties


public:


  union


  {


    METHOD_PROPERTY_GET_EXTERNAL_PROP(size_t, Container, get_Size, Size);


    METHOD_PROPERTY_SET_EXTERNAL_PROP(size_t, Container, set_Capacity,


    Capacity);


  };


};



There are other, diagnostic, uses of write-only properties, which we'll see at the end of the chapter.

35.4.7 method_property_getset_external

As with method_property_getset, method_property_getset_external is a combination of its get and set brethren, and has corresponding macros METHOD_PROPERTY_GETSET_EXTERNAL(), which defines the property and its offset function, and METHOD_PROPERTY_GETSET_EXTERNAL_PROP(), which defines only the property itself.

Listing 35.24.


template< typename RG             /* The reference type */


        , typename RS             /* The reference type */


        , typename C              /* The enclosing class */


        , ptrdiff_t (*PFnOff)()   /* Pointer to function providing offset in container */


        , RG (C::*PFnGet)() const /* Pointer to a const member function returning R */


        , void (C::*PFnSet)(RS )  /* Pointer to a member function taking R */


        >


class method_property_getset_external


{


  . . .


public:


  /// Provides read-only access to the property


  operator RG () const


  {


    ptrdiff_t offset  = (*PFnOff)();


    C         *pC     = (C*)((byte_t*)this - offset);


    return (pC->*PFnGet)();


  }


  /// Provides write-only access to the property


  class_type &operator =(RS value)


  {


    ptrdiff_t offset  = (*PFnOff)();


    C         *pC     = (C*)((byte_t*)this - offset);


    (pC->*PFnSet)(value);


    return *this;


  }


};



We'd use this in our Date class for read/write properties:

Listing 35.25.


class Date


{


// Properties


public:


  METHOD_PROPERTY_DEFINE_OFFSET(Date, DayOfWeek)


  METHOD_PROPERTY_DEFINE_OFFSET(Date, DayOfMonth)


  METHOD_PROPERTY_DEFINE_OFFSET(Date, Year)


  union


  {


    METHOD_PROPERTY_GET_EXTERNAL_PROP(WeekDay, Date, get_DayOfWeek, DayOfWeek);


    METHOD_PROPERTY_GET_EXTERNAL_PROP(int, Date, get_Year, Year);


    METHOD_PROPERTY_GETSET_EXTERNAL_PROP(int, int, Date, get_DayOfMonth


                                       , set_DayOfMonth, DayOfMonth);


    . . .


  };


  . . .


};



You can see that we're just about done providing our putative Date class with all the properties that the original notional definition had. The only thing remaining is to implement class (static) properties.


      Previous section   Next section