Member Login
LOG IN
Forgot your password?
Registration FAQ

154 Users Online Today

  Archives
 
 
  Special
 
 
 
 
  About Us
 
 
 

Newsletter
Free E-mail Newsletter from BYTE.com




 
    
           
How to Read BYTE.com
Welcome to BYTE.com, online home of the world's premier computer magazine. Since 1975 we've covered emerging hardware and software systems with unmatched breadth and depth. Sign up today!
—more info—


BYTE.com > Flexible C++

Print Friendly Version

 

By Matthew Wilson

December, 2003

(Flexible C++ #5: Friendly Templates

Friendly Templates

by Matthew Wilson

In "Befriending Templates," Herb Sutter gives a great treatise on the subject of template friendship from the perspective of granting friendship from classes to template functions. In this column, I want to look at the subject from the different perspective of granting friendship from a template to one of the template's parameterizing types, as in:

// form #1
template <typename T>
class Thing
{
  friend T; // Allow T to see inside Thing<T>
private:
  int m_value;
};

Seems like an reasonable thing to do, does it not? Alas, it is not legal C++. According to the C++-98 Standard (ISO/IEC 14882), 7.1.5.3(2): "...within a class template with a template type-parameter T, the declaration ["]friend class T;["] is ill-formed" (http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/papers/2003/n1520.pdf).

However, I'm a pragmatist, not a language lawyer. What I want to do is sensible, at least in the limited circumstances in which I want to do it, so I'm not terribly interested in its illegality. I just want to get it to work with all the compilers I need.

The form just presented, which I'll call form #1, works with compilers such as Borland (5.51 and 5.6), Comeau (4.3.0.1), Digital Mars (GCC (2.95), Intel (6 and 7), Watcom (11 and 12), and Visual C++ (4.2 through 7.1). It does not work with CodeWarrior (7 and 8) or GCC (3.2). Comeau works with this form, and all others, when in its Win32 default configuration. In strict mode (--strict) it does not work with any, reflecting the fact that the technique in all of its forms is not legal C++.

Since T is a class—we're granting it friendship, so it can't exactly be an int—maybe we should mention that fact to the compiler, as in:

// form #2
template <typename T>
class Thing
{
  friend class T; // Allow T to see inside Thing<T>
private:
  int m_value;
};

This is form #2. CodeWarrior, Digital Mars, and Watcom support this form. So a bit of compiler discrimination between forms #1 and #2 would cover most bases, but we're still not satisfying GCC 3.2.

Being someone that avoids friendship like the plague, I ran out of experience pretty soon here, so I consulted the newsgroup gurus. The kind people of comp.lang.c++.moderated were very helpful, and suggested two versions, forms #3 and #4:

// form #3
template <typename T>
class Thing
{
  struct friend_maker
  {
    typedef T T2;
  };
  typedef typename friend_maker::T2   friend_type;
  friend class friend_type;
private:
  int m_value;
};


// form #4
template <typename T>
class Thing
{
  template<class T2>
  struct friend_maker
  {
    typedef T2 T3;
  };
  typedef typename friend_maker<T>::T3 friend_type;
  friend class friend_type;
private:
  int m_value;
};

The complication here is we're back to the inconsistency seen in forms #1 and #2 between whether the class specifier should be used in the class declaration. GCC and Visual C++ both require that class is not used, whereas the other compilers require that it is used. Hence, the real versions of forms #3 and #4 require some further discrimination, as in:

#if defined(__BORLANDC__) || \
    defined(__COMO__) || \
    defined(__DMC__) || \
    defined(__INTEL_COMPILER) || \
    defined(__MWERKS__) || \
    defined(__WATCOMC__)
# define TEMPLATE_FRIEND_FRIEND_MAKER_FRIEND_DECL_USES_CLASS
#elif defined(__GNUC__) || \
      defined(_MSC_VER)
 /* Not defined */
#else /* ? compiler */
# error Other compilers not discriminated ...
#endif /* compiler */


// form #3
template <typename T>
class Thing
{
  struct friend_maker
  {
    typedef T T2;
  };
  typedef typename friend_maker::T2   friend_type;
#ifdef TEMPLATE_FRIEND_FRIEND_MAKER_FRIEND_DECL_USES_CLASS
  friend class friend_type;
#else /* ? TEMPLATE_FRIEND_FRIEND_MAKER_FRIEND_DECL_USES_CLASS */
  friend friend_type;
#endif /* TEMPLATE_FRIEND_FRIEND_MAKER_FRIEND_DECL_USES_CLASS */
private:
  int m_value;
};

Since no compiler I use rejects form #3 and accepts form #4, I just stick with form #3. Table 1 summarizes the support.

Table 1: Friendship support of various compilers
Compiler Form #1 Form #2 Form #3 Form #4
Borland C++ (5.51 & 5.6) Yes      
CodeWarrior (7 and 8)   Yes Yes Yes
Comeau (4.3.0.1) non-strict only non-strict only non-strict only non-strict only
Digital Mars (8.26-8.37) Yes Yes Yes Yes
GCC 2.95 Yes      
GCC 3.2   Yes Yes  
Intel (6 and 7) Yes Yes Yes Yes
Visual C++ (4.2 - 7.1) Yes   Yes Yes, except 4.2
Watcom (11 and 12) Yes Yes Yes  

* Comeau provides, from version 4.3.3 onwards, the --friendT command-line option, which allows Friendly Templates even when the --strict option is specified.

So there you have it. The language says it's illegal, so it's not surprising that there's a fair degree of variance in the compilers' "illegal" support. I really don't like macros as a rule, especially ones that "generate" code, but in this case it's probably necessary. Hence, we can define:

#if defined(__BORLANDC__) || \
    defined(__COMO__) || \
    defined(__DMC__) || \
    (   defined(__GNUC__) && \
        __GNUC__ < 3) || \
    defined(__INTEL_COMPILER) || \
    defined(__WATCOMC__) || \
    defined(_MSC_VER)
# define    DECLARE_TEMPLATE_PARAM_AS_FRIEND(T)     friend T
#elif defined(__MWERKS__)
# define    DECLARE_TEMPLATE_PARAM_AS_FRIEND(T)     friend class T
#elif defined(__GNUC__) && \
      __GNUC__ >= 3
# define    DECLARE_TEMPLATE_PARAM_AS_FRIEND(T)     \
    struct friend_maker                             \
    {                                               \
        typedef T T2;                               \
    };                                              \
    typedef typename friend_maker::T2 friend_type;  \
    friend friend_type
#endif /* compiler */

It is then used as follows:

// form #2
template <typename T>
class Thing
{
  DECLARE_TEMPLATE_PARAM_AS_FRIEND(T);
private:
  int m_value;
};

I've defined the macro so that it needs a terminating semi-colon in the code where it is used. You may choose to do it otherwise.

Acknowledgments

Many thanks to the helpful residents of comp.lang.c++.moderated. In particular, thanks to Attila Feher, Gabriel Dos Reis, Ron Crane, and "tom_usenet" for providing explanations of why it's illegal. And to John Potter for offering form #3, and Steven Keuchel for offering form #4.

Comeau Update – new!

Since the original form of this article was published I’ve been in contact – he might call it badgering, I couldn’t possibly comment ;-)– with Greg Comeau, and as of version 4.3.3 the Comeau compiler supports the new command-line option --friendT, which means you can have strict compilation but still have friendly templates. This is great news, as I have big plans for this technique; more of that at another time …. Anyway, many thanks to Comeau Computing for being such a responsive vendor.

A word of caution: using non-standard behaviour is a bad thing, and not to be taken lightly. The point here is that the technique is a desirable one, that almost all compilers support it, and that even Comeau, arguably the most compliant compiler in the business, now supports it with the --friendtT option. I think in this specific case that there is sufficient momentum in its support that we can safely hitch ourselves to this train. However, there are no guarantees in non-standard behaviour, and if you do not share my level of confidence in its ongoing wide support you should not use the technique.

About the Author

Matthew Wilson is a software development consultant for Synesis Software, specializing in robustness and performance in C, C++, Java, and .NET. Matthew is the author of the STLSoft libraries, and the forthcoming book Imperfect C++ (to be published by Addison-Wesley, 2004). He can be contacted via matthew@synesis.com.au or at http://stlsoft.org/.

BYTE.com > Flexible C++

CMP DevNet Spotlight
Zombie Wars
BYTE.com
Jerry Pournelle searches for a utility that can keep your computer from being used as a zombie. Also: the game, movie, and books of the month.

Flexible C++
Matthew Wilson
My approach to software engineering is far more pragmatic than it is theoretical--and no language better exemplifies this than C++.

more...

BYTE Digest

BYTE Digest editors every month analyze and evaluate the best articles from Information Week, EE Times, Dr. Dobb's Journal, Network Computing, Sys Admin, and dozens of other CMP publications—bringing you critical news and information about wireless communication, computer security, software development, embedded systems, and more!

Find out more

BYTE.com Store

BYTE CD-ROM
NOW, on one CD-ROM, you can instantly access more than 8 years of BYTE.
 

101 Perl Articles
This unique collection of 101 Perl Articles written by the world's leading experts on Perl programming has something for every Perl programmer.

Copyright © 2004 CMP Media LLC, Privacy Policy, Terms of Service
Site comments: webmaster@byte.com
SDMG Web Sites: BYTE.com, C/C++ Users Journal, Dr. Dobb's Journal, MSDN Magazine, New Architect, SD Expo, SD Magazine, Sys Admin, The Perl Journal, UnixReview.com, Windows Developer Network



MarketPlace
Wanna see your ad here?
 

HotelsLas Vegas Hotelsoffice equipmentnew york hotelsparis hotelschicago hotelsUnbiased Price ComparisonsFashion ShoppingA+, MCSE, MCSA, MCAD Certification Traininghacking information network security training computer forensicslaser eye surgery lasik chicago IllinoisDiscussion Corner: :Price ComparisonLastminute Max: LastminuteBoard123GroupTop DUI-DWI Attorneys-USAOffice Supplies Airline TicketsDigital PhotographySoftware DownloadBusiness Intelligence



web1