Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

True typedefs


True typedefs

Introduction

One of the few legitimate criticisms of C++ is the fact that typedefs are always weak. One can define two types from the same base type and, without constraint (or even a compiler warning!), mix these types. Not only can this lead to problems in erroneous assignment of one type to another, or between a typedef and its base type, but it also precludes the use of overloaded functions based on such types.

typedef int  channel_t;
typedef int  protocol_t;

int        i  = 10;
channel_t  ch = i;  // Not desired
protocol_t pr = ch; // Not desired

void log(int i)        {}
void log(channel_t i)  {} // Error
void log(protocol_t i) {} // Error
One common technique for making stronger types is to typedef pointers to (usually anonymous) structures by use of a macro, but using these types is onerous and requires casting (either C-style or reinterpret_cast<>) when assigning or testing (e.g., when matching bit patterns), as shown in Listing 1. Furthermore, these types cannot be used to represent (other than as a pointer to) types that are larger than the ambient pointer size and therefore are unsuitable for representing rich user-defined types (e.g., a string object).

This article describes a simple template class, true_typedef, that solves these issues and provides strongly typed typedefs. Strongly typed typedefs are built around a base type, which is usually, but not necessarily, a fundamental type. They may be constructed from base type values and also a method to (explicitly) access the base type value where necessary. Even when different types are built around the same base type, they are mutually incompatible and can be used to overload functions correctly.

Implementation

The implementation of the true_typedef class [1] is very simple (see Listing 2). The class has a single member of the base type, which is used to store the actual value and which may only be accessed via the base_type_value() methods. Three constructors are provided: one default, one for conversion from the base type, and one copy constructor. The conversion constructor is explicit and, along with the lack of a conversion operator, helps enforce the mechanics of the strong typing.

The class is a template, which is parameterized by a base type (T) and a unique type (U). The unique type is provided by use of the macro shown in Listing 1, which allows the use of the vulgar typedef pointer technique to be kept to a discrete minimum. Each instantiation of a true_typedef template must be provided with a distinct unique type, assuring the strong typing.

In addition to the true_typedef class, a number of operators are provided, defined as free functions, which manipulate the true_typedef instances via the base_type_value() methods. It is, needless to say, valid to include modifying operations, since the intent behind true_typedef is to be a strongly typed type, rather than a strongly valued type (i.e., an enum). The operators currently supplied are +, -, /, %, *, ^, ~, ==, !=, &, |, <, <=, >, >=, ++ (pre and post), and –– (pre and post). The binary operators are expressed in terms of both the parameterized true_typedef and its base type. Examples of some of these free functions are shown in Listing 3.

Conclusion

The true_typedef class completely answers the problems of weak typedefs: it prevents types with identical base types from being implicitly converted to each other, and it facilitates overloading on types with identical base types. In this way, the role of a type is protected.

stlsoft_gen_opaque(forename_u)
stlsoft_gen_opaque(surname_u)
typedef true_typedef<string,
         forename_u> forename_tt;
typedef true_typedef<string,
         surname_u>  surname_tt;

void storeName( forename_tt fn,
                surname_tt sn);

forename_tt    fn("Matthew");
surname_tt     sn("Wilson");

storeName(sn, fn); // Error. Phew!


stlsoft_gen_opaque(channel_u)
stlsoft_gen_opaque(protocol_u)
typedef true_typedef<int,
          channel_u>  channel_tt;
typedef true_typedef<int,
          protocol_u> protocol_tt;

void func(int i)         {}
void func(channel_tt i)  {} // OK
void func(protocol_tt i) {} // OK

channel_tt  ch(3);
protocol_tt pr(ch); // Error, Phew!

--ch++;
ch = ~ch;

pr = ch.base_type_vale(); // If we must
Furthermore, it is efficient [2] and restricts operations of the parameterized type by virtue of their absence/inaccessibility on the parameterizing type: indeed, one could define an int-based true type that allowed ++ but not –– (by wrapping in an intermediate class where –– was protected/private)!

Nevertheless, the current implementation is less than perfect for a number of reasons:

  • It is verbose (the many operator functions).
  • It is incomplete. (why not ->, !, &&, or || ?)
  • It exposes a potential superset of desired operations.
Work is in progress to parameterize the available methods by policy [3].

Also, it is not fool proof: it can be defeated if the developer mistakenly uses the same unique type in two separate true_typedef instantiations. However, in practice such mistakes have not been witnessed and would always be less subtly hidden from code inspection than cross-contamination.

Despite these criticisms, it has proved to be extremely useful in reducing subtle/hidden cross-contamination bugs and has become an invaluable item in my toolkit [4].

Acknowledgements

I'd like to thank Walter Bright, author of the excellent Digital Mars compiler [5], for providing me with feedback on the viability of adding true typedefs to the language (and to compilers), as in:

true typedef string forename_tt;
true typedef string surname_tt;

// All the methods of string are available
// to string true typedefs
forename_tt fn("Matthew");
surname_tt  sn("Wilson", 6);

// If we must access it as a string
string &fn_str = base_type_cast<string&>(fn);
Walter says it's certainly feasible, but won't be adding it to the Digital Mars compiler without a lot more requests. I leave it in your hands.

Notes and References

[1] true_typedef is part of the STLSoft Types Library (v1.3.1 onwards) and is available at <http://stlsoft.org/libraries/types_library.html>. STLSoft is an open-source organization for the development of robust, lightweight, cross-platform STL software.

[2] There should be no efficiency cost in manipulating the type, since the base_type_value() methods are inline, and the compiler will optimize all operations direct to the contained base-type instance. However, since the constructor takes only a reference to a base-type instance, it constrains initialization of instances of the class and may thus result in a (potentially inefficient) copy from a multi-argument constructed base-type instance, though this does not, of course, apply to built-in types.

[3] This may be available from the STLSoft website by the time this article goes to press.

[4] One valuable application has been to detect inefficient instances of post-increment/decrement by implementing iterators in true types for which these operators are inaccessible.

[5] Digital Mars C/C++ compiler is available for free at <http://digitalmars.com>. Walter is also the author of the D language/compiler, which has true typedefs built in!

About the Author

Matthew Wilson holds a degree in Information Technology and a Phd in Electrical Engineering and is a software development consultant for Synesis Software. Matthew's work interests are in writing bullet-proof real-time, GUI, and software-analysis software in C, C++, and Java. He has been working with C++ for over 10 years and is currently bringing STLSoft.org and its offshoots into the public domain. Matthew can be contacted via [email protected].


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.