A template wrapper that provides type uniqueness for otherwise synonymous types.
March 01, 2003
URL:http://www.drdobbs.com/true-typedefs/184401633
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) {} // ErrorOne 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.
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.
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 mustFurthermore, 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:
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].
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.
[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!
Listing 1: Using a macro to typedef pointers to structures
/* This macro generates an opaque, unique, type whose name is * given by the _otype_ parameter. */ #define stlsoft_gen_opaque(_otype_) \ typedef const struct __stlsoft_otype_##_otype_{ int i;} *_otype_; stlsoft_gen_opaque(MyType1) stlsoft_gen_opaque(MyType2) MyType1 mt1 = 0x00000001; // Error MyType2 mt2 = reinterpret_cast<MyType2>(0x00000001); ///... void func(){ if(mt2 & 0x07070707) // Error { // ... } if(mt2 & reinterpret_cast<MyType2>(0x07070707)) // Error { // ... } if(reinterpret_cast<unsigned long>(mt2) & 0x07070707) { // ... } }
Listing 2: The true_typedef class implementation
/* ///////////////////////////////////////////////////////////// * * ... * * Extract from stlsoft_true_typedef.h * * www: http://www.synesis.com.au/stlsoft * http://www.stlsoft.org/ * * Copyright (C) 2002, Synesis Software Pty Ltd. * (Licensed under the Synesis Software Standard Source License: * http://www.synesis.com.au/licenses/ssssl.html) * * ... * * ////////////////////////////////////////////////////////// */ ... template <ss_typename_param_k T, ss_typename_param_k U> class true_typedef { public: typedef T value_type; typedef U unique_type; typedef true_typedef<T, U> class_type; // Construction public: true_typedef() : m_value(value_type()) { } ss_explicit_k true_typedef(const T &value) : m_value(value) { } true_typedef(const class_type &rhs) : m_value(rhs.m_value) { } const class_type &operator =(const class_type &rhs) { m_value = rhs.m_value; return *this; } // Accessors public: const value_type &base_type_value() const { return m_value; } value_type &base_type_value() { return m_value; } // Members protected: value_type m_value; // Implementation private: // Not provided, as the syntax is less ambiguous when // assignment from an explicit temporary is required const class_type &operator =(const T &value); };
Listing 3: Free function examples
/* ///////////////////////////////////////////////////////////// * * ... * * Extract from stlsoft_true_typedef.h * * www: http://www.synesis.com.au/stlsoft * http://www.stlsoft.org/ * * Copyright (C) 2002, Synesis Software Pty Ltd. * (Licensed under the Synesis Software Standard Source License: * http://www.synesis.com.au/licenses/ssssl.html) * * ... * * ////////////////////////////////////////////////////////// */ ... // operator ~ template <ss_typename_param_k T, ss_typename_param_k U> inline true_typedef<T, U> operator ~(const true_typedef<T,U> &v) { return ~v.base_type_value(); } // Pre-increment template <ss_typename_param_k T, ss_typename_param_k U> inline true_typedef<T, U> &operator ++(true_typedef<T, U> &v) { ++v.base_type_value(); return v; } // Post-decrement template <ss_typename_param_k T, ss_typename_param_k U> inline true_typedef<T, U> operator --(true_typedef<T,U> &v, int) { true_typedef<T, U> r(v); v.base_type_value()--; return r; } // operator < template <ss_typename_param_k T, ss_typename_param_k U> inline ss_bool_t operator <(const true_typedef<T, U> &lhs, const true_typedef<T, U> &rhs) { return lhs.base_type_value() < rhs.base_type_value(); } template <ss_typename_param_k T, ss_typename_param_k U> inline ss_bool_t operator <(const true_typedef<T, U> &lhs, const true_typedef<T, U>::value_type &rhs) { return lhs.base_type_value() < rhs; } template <ss_typename_param_k T, ss_typename_param_k U> inline ss_bool_t operator <( const true_typedef<T, U>::value_type &lhs, const true_typedef<T, U> &rhs) { return lhs < rhs.base_type_value(); }
Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.