From 692a4f49e682f0136b9668433ef9d342ec6df714 Mon Sep 17 00:00:00 2001 From: "Frank B. Brokken" Date: Tue, 20 Oct 2009 07:54:32 +0000 Subject: [PATCH] Completed First Imressions, Namespaces next git-svn-id: https://cppannotations.svn.sourceforge.net/svnroot/cppannotations/trunk@258 f6dd340e-d3f9-0310-b409-bdd246841980 --- yo/cplusplus.yo | 4 +-- yo/first.yo | 2 -- yo/first/constcast.yo | 43 ++++++++--------------- yo/first/dynamiccast.yo | 10 ++++-- yo/first/reinterpretcast.yo | 68 ++++++++++++++++++++++++++++--------- yo/first/staticcast.yo | 12 +++++++ yo/namespaces.yo | 2 ++ 7 files changed, 91 insertions(+), 50 deletions(-) diff --git a/yo/cplusplus.yo b/yo/cplusplus.yo index e75fceae..aa607ce2 100644 --- a/yo/cplusplus.yo +++ b/yo/cplusplus.yo @@ -49,12 +49,12 @@ COMMENT( 2 ) lchapter(IntroC)(Introduction) includefile(intro) -COMMENT(>>>>>>>>>>>>> NEXT <<<<<<<<<<<<<) - COMMENT( 3 ) lchapter(FirstImpression)(A First Impression Of C++) includefile(first) +COMMENT(>>>>>>>>>>>>> NEXT <<<<<<<<<<<<<) + COMMENT( 4 ) lchapter(NAMESPACE)(Name Spaces) includefile(namespaces) diff --git a/yo/first.yo b/yo/first.yo index 61d9306f..b93f70ff 100644 --- a/yo/first.yo +++ b/yo/first.yo @@ -76,8 +76,6 @@ includefile(first/cast) subsect(The `static_cast'-operator) includefile(first/staticcast) -COMMENT(>>>>>>>>>>>>> NEXT <<<<<<<<<<<<<) - subsect(The `const_cast'-operator) includefile(first/constcast) diff --git a/yo/first/constcast.yo b/yo/first/constcast.yo index 85ad1fe2..e02b468a 100644 --- a/yo/first/constcast.yo +++ b/yo/first/constcast.yo @@ -1,30 +1,17 @@ - it() There is a special cast to do away with the tt(const) -type-modification: +The tt(const) keyword has been given a special place in casting. Normally +anything tt(const) is tt(const) for a good reason. Nonetheless situations +may be encountered where the tt(const) can be ignored. For these special +situations the tt(const_cast) should be used. Its syntax is: centt(const_cast(expression)) - it() A third cast is used to change the em(interpretation) of information: - centt(reinterpret_cast(expression)) - it() And, finally, there is a cast form which is used in combination with -polymorphism (see chapter ref(POLYMORPHISM)). The - centt(dynamic_cast(expression)) - is performed run-time to convert, e.g., a pointer to an object of a -certain class to a pointer to an object further down its so-called em(class -hierarchy). At this point in the em(Annotations) it is a bit premature to -discuss the tt(dynamic_cast), but we will return to this topic in section -ref(DYNAMICCAST). - ) + A ti(const_cast(expression)) expression is used to undo the +tt(const) attribute of a (pointer) type. - - - - The ti(const_cast(expression)) operator is used to undo the -tt(const)-ness of a (pointer) type. Assume that a function - tt(fun(char *s)) is available, which performs some operation on its -tt(char *s) parameter. Furthermore, assume that it's em(known) that the -function does not actually alter the string it receives as its argument. How -can we use the function with a string like tt(char const hello[] = "Hello -world")? - -Passing tt(hello) to tt(fun()) produces the warning - centt(passing `const char *' as argument 1 of `fun(char *)' discards const) - which can be prevented using the call - centt(fun(const_cast(hello));) + The need for a tt(const_cast) may occur in combination with functions from +the standard bf(C) library which traditionally weren't always as const-aware +as they should. A function tt(strfun(char *s)) might be available, performing +some operation on its tt(char *s) parameter without actually modifying the +characters pointed to by tt(s). Passing tt(char const hello[] = "hello";) to +tt(strfun) will produce the warning + centt(passing `const char *' as argument 1 of `fun(char *)' discards const) + A tt(const_cast) is the appropriate way to prevent the warning: + centt(strfun(const_cast(hello));) diff --git a/yo/first/dynamiccast.yo b/yo/first/dynamiccast.yo index 0846840a..51c02cca 100644 --- a/yo/first/dynamiccast.yo +++ b/yo/first/dynamiccast.yo @@ -1,2 +1,8 @@ - The ti(dynamic_cast<>()) operator is used in the context of -i(polymorphism). Its discussion is postponed until section ref(DYNAMICCAST). +Finally there is a new style cast that is used in combination with +polymorphism (see chapter ref(POLYMORPHISM)). Its syntax is: + centt(dynamic_cast(expression)) + It is used run-time to convert, a pointer to an object of a class to a +pointer to an object of a class that is found further down its so-called +em(class hierarchy) (which is also called a em(downcast)). At this point in +the em(Annotations) a tt(dynamic_cast) cannot yet be discussed extensively, +but we will return to this topic in section ref(DYNAMICCAST). diff --git a/yo/first/reinterpretcast.yo b/yo/first/reinterpretcast.yo index 2db3615e..42a1286d 100644 --- a/yo/first/reinterpretcast.yo +++ b/yo/first/reinterpretcast.yo @@ -1,17 +1,53 @@ - The ti(reinterpret_cast(expression)) operator is used to reinterpret -pointers. For example using a tt(reinterpret_cast<>()) the individual -bytes making up a tt(double) value can easily be reached. Assume tt(doubleVar) -is a variable of type tt(double), then the individual bytes can be reached -using - centt(reinterpret_cast(&doubleVar)) - This particular example also suggests the danger of the cast: it looks as -though a standard tt(C)-string is produced, but there is not normally a -trailing 0-byte. It's just a way to reach the individual bytes of the memory -holding a double value. +The third new-style cast is used to change the em(interpretation) of +information: the tt(reinterpret_cast). It is somewhat reminiscent of the +tt(static_cast), but tt(reinterpret_cast) should be used when it is em(known) +that the information as defined in fact is or can be interpreted as something +completely different. Its syntax is: + centt(reinterpret_cast(pointer expression)) -More in general: using the cast-operators is a dangerous habit, as it -suppresses the normal type-checking mechanism of the compiler. It is suggested -to i(prevent casts) if at all possible. If circumstances arise in which casts -have to be used, document the reasons for their use well in your code, to make -double sure that the cast will not eventually be the underlying cause for a -program to misbehave. + A ti(reinterpret_cast(expression)) operator is appropriately used to +reinterpret a tt(void *) to a pointer of a well-known type. Void pointers are +encountered with functions from the bf(C) library like tt(qsort). The +tt(qsort) function expects a pointer to a (comparison) function having two +tt(void const *) parameters. In fact, the tt(void const *)s point to data +elements of the array to sort, and so the comparison function may cast the +tt(void const *) parameters to pointers to the elements of the array to be +sorted. E.g., if the array is an tt(int array[]) and the compare function's +parameters are tt(void const *p1, void const *p2) +then the compare function may obtain the address of the tt(int) pointed to by +tt(p1) by using: + centt(reinterpret_cast(p1)) + + Another example of a tt(reinterpret_cast) is found in combination with the +tt(write) functions that are available for files and streams. In bf(C++) +streams are the preferred interface to, e.g., files. Output +streams (like tt(cout)) offer tt(write) members having the prototype + centt(write(char const *buffer, int length)) + To write a tt(double) to a stream using tt(write) a tt(reinterpret_cast) is +needed as well. E.g., to write the raw bytes of a variable tt(double value) to +tt(cout) we would use: + verb( + cout.write(reinterpret_cast(&value), sizeof(double)); + ) + All casts are potentially dangerous, but the tt(reinterpret_cast) is the +most dangerous of all casts. Effectively we tell the compiler: back off, we +know what we're doing, so stop fuzzing. All bets are off, and we'd better +em(do) know what we're doing in situations like these. As a case in point +consider the following code: + verb( + int value = 0x12345678; // assume a 32-bits int + + cout << "Value's first byte has value: " << hex << + static_cast( + *reinterpret_cast(&value) + ); + ) + The above code will show different results on little and big endian +computers. Little endian computers will show the value 78, big endian +computers the value 12. Also note that the different representations used by +little and big endian computers renders the previous example +(tt(cout.write(...))) non-portable over computers of different architectures. + +As a i(rule of thumb): if circumstances arise in which casts em(have) to be +used, clearly document the reasons for their use in your code, making double +sure that the cast will not eventually cause a program to misbehave. diff --git a/yo/first/staticcast.yo b/yo/first/staticcast.yo index fe370152..72b3c839 100644 --- a/yo/first/staticcast.yo +++ b/yo/first/staticcast.yo @@ -31,3 +31,15 @@ These two casts are a bit overdone. The same result is obtained by explicitly casting tt(doubleVar) to an tt(int), thus obtaining an tt(int)-value for the right-hand side of the expression: centt(intVar += static_cast(doubleVar);) + + A tt(static_cast) can also be used to undo or introduce the +signed-modifier of an tt(int)-typed variable. The bf(C) function tt(tolower) +requires an tt(int) representing the value of an tt(unsigned char). But +tt(char) by default is a signed type. To call tt(tolower) using an available +tt(char ch) we should use: + centt(tolower(static_cast(ch))) + Casts like these provide information to the compiler about how to handle +the provided data. Very often (especially with data types differing only in +size but not in representation) the cast won't require any additional +code. Additional code will be required, however, to convert one representation +to another, e.g., when converting tt(double) to tt(int). diff --git a/yo/namespaces.yo b/yo/namespaces.yo index 73a10aa7..2296212c 100644 --- a/yo/namespaces.yo +++ b/yo/namespaces.yo @@ -1,3 +1,5 @@ +COMMENT(>>>>>>>>>>>>> NEXT <<<<<<<<<<<<<) + lsect(Namespaces)(Namespaces) includefile(namespaces/intro)