无尽的夜空,唯有月光撒满窗前

  C++博客 :: 首页 :: 联系 :: 聚合  :: 管理
  14 Posts :: 0 Stories :: 2 Comments :: 0 Trackbacks

常用链接

留言簿

我参与的团队

搜索

  •  

最新评论

阅读排行榜

评论排行榜

Tech Talk

A Portable typeof Operator

By Bill Gibbons, Pixo, Inc.
Copyright 2000 © Bill Gibbons
All rights reserved.

The Problem

Sometimes it is useful to declare a variable with a type that is not known directly, but is known to be "the type of this expression". For example:

void f(LibClass *p)
{
typeof(p->val) val = val;
/* ... */
}

where "typeof(p->val)" yields a type that is used to declare the local "val". This is particularly useful in templates, where many of the types in use are not known ahead of time.

Some compilers implement a typeof keyword as an extension, but code that uses such an extension is not portable. This article describes a portable way to write a typeof operator.

Almost Enough

C++ has almost enough functionality to do typeof. Function templates can be used to extract a type from an expression and declare a typedef of that type:

template<class T> void f(T)
{
typedef T TheType;
/* . . . */
}
void g() { f(123); } // TheType is "int"

Here the typedef "TheType" in the instantiated function template "f<int>" has the same type as the expression "123". But, there is no way to export the type from the function, so by itself this is not useful for implementing typeof.

Similarly, overloading can be used to select a function that contains a typedef that matches the type:

but there is no way to extract the type.

void f(char) { typedef int TheType; }
void f(short)
( typedef short TheType; }

void g() { f('x'); } // TheType is "char"

Class templates can be used to map a value or type to another value or type:

template<class T> struct U;// no definition needed
template<> struct U<char>
{ typedef unsigned char type; };
template<> struct U<short>
{ typedef unsigned short type; };

void f()
{
U<char>::type a; //unsigned char
U<short>::type b; //unsigned short
}

But you cannot use a class template to extract a type from an expression, as you can with function templates or overloading. (If the expression is a name with external linkage it is possible to implement typeof with class templates, but this is not very useful.)

Function templates and overloaded functions can also map an expression of one type into an expression of another type, as in:

unsigned char U(char x) { return x; }
unsigned short U(short x)
{ return x; }

void f()
{
short s = 0;
s; // expression "s" has type "short"
U(s); // expression "U(s)" has type "unsigned short"
}

This is also not sufficient, although it turns out to be part of the solution.

We need some way to map the type of an expression to something that can be used as a template argument. The only construct in the language that can do this is sizeof. It happens that sizeof is sufficient.

The Solution

The trick is to map each type to a unique integer value using overloaded functions and sizeof. Then template specializations can map the integer value to the right type:

  • The expression is passed to an overloaded function.
  • The return type of the particular function selected is "pointer to array of char", with the array size being unique to the type.
  • The sizeof operator extracts the array size as a constant. The function is never actually called and so does not need a definition.
  • The constant is used to select a class template among a set of specializations; the selected class template contains a typedef with the right type.

Simple Example

The following example shows a simple typeof that handles expressions of type short, int and long.

template<int N> struct typeof_c; // No definition, only specializations
template<> struct typeof_c<1> { typedef short V; };
template<> struct typeof_c<2> { typedef int V; };
template<> struct typeof_c<3> { typedef long V; };

typedef char CharArrayOf1[1];
typedef char CharArrayOf2[2];
typedef char CharArrayOf3[3];

typedef CharArrayOf1 *PtrCharArrayOf1;
typedef CharArrayOf2 *PtrCharArrayOf2;
typedef CharArrayOf3 *PtrCharArrayOf3;

PtrCharArrayOf1 typeof_f(short); // No definitions needed
PtrCharArrayOf2 typeof_f(int);
PtrCharArrayOf3 typeof_f(long);

#define typeof(x) typeof_c<sizeof(*typeof_f(x))>::V

Consider what happens to "typeof(123L)":

  • The macro expands to "typeof_c<sizeof(*typeof_f(123L))>::V".
  • The call "typeof_f(123L)" selects the following declaration from the set of overloaded function declarations:

PtrCharArrayOf3 typeof_f(long);

  • The type of "typeof_f(123L)" is "PtrCharArrayOf3", or pointer to array of char length 3.
  • The type of "*typeof_f(123L)" is "an array of char length 3".
  • "sizeof(*typeof_f(123L))" is an integral constant expression with the value 3. Since the operand of sizeof is not evaluated there is no need to have a definition for typeof_f.
  • The template-id "typeof_c<sizeof(*typeof_f(123L))>" is effectively "typeof_c<3>" which selects the specialization

template<> struct typeof_c<3> { typedef long V; };

  • The qualified-id "typeof_c < sizeof(*typeof_f(123L)) >::V" is the above typedef which has type long.
  • So "typeof(123L)" refers to a typedef of type long.

Note that all of these operations happen at compile time. The generated code will be the same as if the type were written as "long" instead of "typeof(123L)".

Improvements

The typedefs in the preceding example are not really necessary and they pollute the global namespace. We can eliminate them at the risk of confusing human readers (and maybe a few compilers):

char(*typeof_f(short))[1]; // fct returns ptr to array of char len 1
char(*typeof_f(int ))[2]; // length 2
char(*typeof_f(long ))[3]; // length 3

If we declare the parameters as references to const instead of using simple pass by value, it becomes possible to use expressions which cannot be passed by value - for example, objects of classes with private copy constructors. So in the general case, given type T, we want to write the typeof_f declaration for T as:

char(*typeof_f(const T&))[nnn];

Declaring the required specializations and functions can be handled much more easily with a macro:

#define REGISTER_TYPEOF(N,T) \
template<> struct typeof_c<N> { typedef T V; }; \
char(*typeof_f(const T&))[N];

However, we run into trouble with some compound types. For example, if we expand:

REGISTER_TYPEOF( 3, void (*)() )

we get:

template<> struct typeof_c<3> { typedef void (*)() V; };
char(*typeof_f(const void(*)()&))[3];

Both of these declarations are ill-formed because combining C++ types is not as simple as substituting a type for a type name. We can make the macro work again by introducing another class template:

template<class T> struct WrapType { typedef T U; };

This template takes advantage of the fact that when you instantiate a template with a compound type, e.g.

WrapType<void (*)()>

you create a simple name (the template parameter, in this case, T) for the type. We can't get to T directly from outside the template but we can get to typedef U that has the same type. In example:

WrapType<void (*)()> :: U

has type "void (*)()". So if we replace instances of T in the macro with "WrapType<T>::U" we have the original type but in a form that we can use to build other types.

Our macro now looks like this:

#define REGISTER_TYPEOF(N,T) \
template<> struct typeof_c<N> { typedef WrapType<T>::U V; }; \
char (*typeof_f(const WrapType<T>::U &))[N];

See the program listing at the end for a more complete example using the above macro and several test types.

Limitations

This approach does require that each type that might be used in typeof be explicitly registered with the
REGISTER_TYPEOF macro. This limits, or at least places additional burden, on the use of this technique in general-purpose template libraries.

#define REGISTER_TYPEOF(T) \
template<> struct typeof_c<__LINE__> \
{ typedef WrapType<T>::U V; }; \
char (*typeof_f(const WrapType<T>::U &))[__LINE__];

The need to assign a distinct ordinal to each type could be a nuisance. If all the REGISTER_TYPEOF uses are in a single include file the macro could be simplified by using __LINE__ instead of N:

It does not matter that the ordinals may be large and/or discontiguous; the only restriction is that they be positive and distinct.

If the type of the expression given to typeof has not been registered, the resulting error message may be cryptic; but it should at least refer to typeof_f that should give a clue to the problem.

Conclusion

For applications that need a typeof operator this technique provides most of the power of a compiler extension while keeping the code portable. This general approach of using function overloading and sizeof to extract expression type information can be used in other places.

The implementation of this technique can be somewhat obscure but the uses (such as reference to typeof) can be kept simple. Since all of these operations occur at compile time there is no runtime cost for using them.

A complete source code example can be found on page 4 of the printed version of the newsletter.

Editorial

By Reg. Charney

Bigger and Better

As you have probably noticed, things have changed again!. Based on the possibility of new sponsor, we have expanded things to increase this newsletter by 50% and to add color, at least to the first and last page. We have also been able to expand the length and content of our technical offerings.

We are also planning to include several new sections. They include a book review section, and a technical summary section. The technical summary will try to summarize technical topics on a number of the C, C++ and Java reflectors that exist on the net. Let us know what more you would like.

Not all rosy

As this issue is about to go to press, one of our new potential sponsors has backed out - or more accurately, failed to pay the invoice, answer emails, or respond to phone messages. It is a disappointment, as you can imagine. It is also too late to change the content or layout of this issue. However, with your assistance, we can grow stronger from the experience.

If you know firms that are committed to open source development or in the betterment of the professional programming community and the people they hire, please let them know about the ACCU and in this newsletter. Also, please tell me and I will contact them. We need sponsors and also advertisers. The benefits package that we are offering sponsors is quite impressive.

Electronic Newsletter

We can now send you future issues of this newsletter as PDF files. To subscribe, please email me at

accent-subscribe_AT_CharneyDay.com

Past issues are also available as PDF files, including versions in A4 format for our international readers. In the body of the message say if you want the A4 format and/or past issues.

Trends

By Reg. Charney

Languages

Again, Java leads the pack in normalized demand. However, this month HTML, Perl and XML all are in greater demand than C/C++. In fact, the rate of demand for the languages has changed dramatically. The change in demand for XML outstrips all other languages with Perl, the glue for Internet applications, an unexpected second. Perl's importance can be seen as a growth in tools for infrastructure implementation. Note also that one of the few increasing changes in demand is in Python, another glue language.

Figure #1

Figure #2

Platforms

In terms of demand, you already know most of the big players - Windows and Unix. The surprise occurs in the changing demand for platform-specific skills. The big winner here is Linux followed closely by Windows 2000. This says that there is a pent up demand for Linux skills that is outstripping all other platforms. Only Windows 2000 comes close, but that is to be expected, based on the huge installed base of Windows software already in the market and which firms need to upgrade.

Figure #3

Figure #4

The second clear fact is that Solaris dominates the Unix marketplace and demand for expertise in it continues to grow, but at half the speed of Windows 2000 and Linux.

How We Calculate

The source for these charts come from Internet online job sites. Thus, these numbers represent demand for new hires, not who are currently employed doing what. However, some estimates can be made from these figures, but that is a subject of another article.

These online sites are searched by keywords and the number of "hits" are recorded. Often, defining a skill is fuzzy. For example, the category "Windows 98" is actually the Boolean expression "Windows98 or Win98 or 98". Thus, an ad asking for experience with "W98" would be missed. Since getting some measurements is better than nothing, we compute raw demand for a skill. I often skip this chart because it is not very interesting-just a series of numbers without much context.

The first interesting chart is the Normalized Demand set of charts. This is the demand for a particular skill divided by the demand for all skills in that category. The second chart of interest is the change in demand for a skill since the last time we did a measurement of that skill. You can think of these charts as Skill Velocity Charts - how fast is the demand for the skill changing. The last chart is the Skill Acceleration chart that represents the change in changing demand over the last period we are measuring. It measures how fast demand is changing for each skill set within a category.

posted on 2012-03-02 10:26 skyline 阅读(515) 评论(0)  编辑 收藏 引用

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理