C/C++ Contributing Editors


Uncaught Exceptions: Dies Irae

Bobby Schmidt

Bobby rings out the old with some traditional bugs and a new C Standard, then rings in the new with fingers crossed.


Copyright © 2000 Robert H. Schmidt

Hello? Is this on? Testing 1 2 3...

If you're reading me, I guess you safely escaped the Y2K apocalypse — or at least enough for your local postal service to deliver this magazine, and for you not to need it as kindling. Assuming it became necessary, I always reckoned I could avoid Y2K's wrath by slumming at Microsoft's main campus. Of all the people in the world, I figure Bill Gates is the last one to let his company and facilities grind to a halt.

Then again, the U.S. Justice Department may end up debilitating Microsoft worse than Y2K ever could. As I write this, Judge Jackson has recently released his Findings of Fact and appointed a mediator in the Microsoft trial. I've always believed that after much posturing, Microsoft would choose to settle out of court, if only to keep the Findings from becoming evidence in future trials.

Having worked at or with Microsoft for a decade, and being a devoted user of Apple's Macintoshes, I hold distinct and strong opinions about this case. Among them:

Regardless of Microsoft's ultimate fate, their near future will surely mirror that of Redmond's current weather: seemingly endless months of dark gloom with sunny times a distant memory.

Double Trouble

Q

Hello,

I'm currently learning the C programming language and have run across a slight problem with the Mix Power C compiler. I get an error with the following code:

#include <stdio.h>

main()
    {
    long x = 29598;
    printf("%lf\n", x);
    return 0;
    }

In Power C I get an error: "invalid function _ftoa in object library pclib.mix." I have tried to take a program straight from the manual of Power C using long and double and the same problem persisted. Could it be that perhaps the .mix libraries had gotten corrupt or is this just a simple problem of incorrect syntax? — Pete Robie

A

You sent me a followup email indicating that your Power C library was corrupt, and that a new library fixed the symptom. However, you still have some latent problems that I want to address.

First, you chose a format field of %lf. That format is not specified by the C Standard, and will result in undefined behavior. I'm guessing that the format you really want is %Lf.

Second, %Lf requires a corresponding long double argument, yet you provide it a long argument instead. printf does not detect this mismatch, and interprets the integral long argument (probably on the stack) as a floating-point long double. This causes two difficulties:

The robust solution here is actually quite simple: pass an honest-to-God long double to printf, by either

/* Version 1 */
long double x = 29598;
printf("%Lf\n", x);

or

/* Version 2 */
long x = 29598;
printf("%Lf\n", (long double) x);

Caveat: I've used translators that required explicit definition of a floating-point variable (such as long double x) before they would generate references to internal floating-point routines (like _ftoa). Microsoft's compilers in particular used to sport this particular "feature." If your translator has this same peculiarity, you may not be able to use Version 2 unless you define some long double variable elsewhere in your program.

Get Real

Q

Hi Bobby.

I ran into the following problem when I installed a new ANSI C++ compiler and tried to build my projects. I have a header file with the declaration

typedef double real ;

and a huge number of translation units that use real wherever possible:

void foo ( real n ) ;

I also include the compiler's header complex.h for its complex class. Now, this new compiler has a template complex type, with the template function

std::real<T> ( const complex<T> & ) ;

The compiler complains about each and every unqualified appearance of real, with an ambiguity. I posted this to a newsgroup, and they told me to qualify all appearances of real. I don't believe I will have to find and replace about ten directories and add :: or std:: to each real; it's a nightmare. There has to be a better solution.

Thanks. — Fernando Cacciola

A

Qualifiers such as :: and std:: are like const: robust and easy to use from the get-go, a monstrous pain to retrofit later. Fortunately, you should not have to make such a retrofit to your program.

You mention a compiler-provided header called complex.h. The Standard C++ library specifies the header <complex>, without the .h. complex.h is probably a compiler fossil from pre-Standard days. I think this header is causing your turmoil.

I'll go out on a small limb and guess that complex.h hoists its definitions into the global namespace via using clauses. To test my guess, try compiling the simple file

#include <complex.h>

complex<int> x;

If this compiles, complex.h causes std::complex to act as if it were really ::complex. As a side-effect, complex.h most likely also hoists std::real. Because the compiler sees two things it can interpret as ::real — your typedef real, and the hoisted std::real — you get diagnostics about ambiguity.

The solution is to use the real Standard header:

#include <complex>

typedef double real;

complex<int> a;          // error
std::complex<int> b;     // OK
real c;                  // OK
double d = std::real(b); // OK

This way, the bare real always references your typedef, and won't be confused with the template function std::real.

Caveat: you'll also have to write std::complex where you could have written just complex before. To mitigate this problem, add a using declaration:

#include <complex>

using std::complex;

typedef double real;

complex<int> a;          // now OK
std::complex<int> b;     // still OK
real c;                  // still OK
double d = std::real(b); // still OK

Now std::complex acts as if it were really declared as ::complex.

Array Initialization

Q

How do you initialize a class non-static member array of const elements? Since the array contains const elements, you must initialize it in the constructor member initialization list. However, I do not believe the member initialization list supports array initialization. — Marty Grove

A

You don't.

This problem stems from the conflicting initialization syntax of members and arrays. Member initializers always take the direct-initialization form [1]

a(b)

In contrast, non-member initializers can usually take either the direct initialization form or the copy initialization form [2]

a = b

Arrays are an exception — they must always take the copy initialization form:

int a[3] = {0, 1, 2};

Because members require direct initialization, and arrays require copy initialization, you cannot syntactically express initialization of array members.

I can't offer a complete solution to this problem. About the closest I can suggest is to replace the array with an array-like class object such as std::vector:

#include <vector>

class X
    {
    std::vector<int> const m;
    };

Limitations:

Here is a more complete example:

#include <vector>

std::size_t const n(3);
int const a[n] = {0, 1, 2};

class X
    {
public:
    X();
    X &operator=(X const &);
private:
    std::vector<int> const m;
    };

X::X() : m(a, a + n)
    {
    }

X &X::operator=(X const &)
    {
    return *this;
    }

Here X::m has n elements, and is initialized to the values contained in the array a.

C++ Primer

Q

I've been reading Stanley B. Lippman's and Josée Lajoie's book C++ Primer. In the section on "Explicit Template Arguments" they describe the way a return type argument needs to be explicitly declared if it's not part of the argument list. However, the following code from the book does not compile:

typedef unsigned int ui_type;

template<class T1, class T2,
   class T3>
T1 sum(T2 a, T3 b)
    {
    return (a + b);
    }

// ok: T2 is char and T3 is
// unsigned int
// T2 and T3 are inferred from the
// type of pf:
ui_type (*pf)(char, ui_type) =
    &sum<ui_type>;

int main()
    {
    return pf(1, 2);
    }

The problem is the assignment of the template function to the pointer to function. My Visual C++ compiler gives the following error message:

'initializing' : cannot convert from
' ' to 'unsigned int (__cdecl *)
(char,unsigned int)'
None of the functions with this name in
scope match the target type.

My C++ Builder 4 compiler gives me an "Access violation" message.

I am not sure if Mr. Lippman and Josée blew this one, if the compilers are not totally C++ compliant, or if I'm missing something important. I would appreciate any information on this code. Thank you. — Richard Martone

A

The code you cite is from pages 506-7 of C++ Primer (Third Edition). I've added a small main to make the sample build.

I was able to reproduce your results with Visual C++ 6. In contrast, both Metrowerks CodeWarrior Pro 5 and the EDG C++ front end 2.4.2 successfully translate and run the code. (I have C++ Builder, but still haven't installed it.)

From my understanding of template argument deduction, I believe that the authors are correct. Consider this simpler example:

template <typename T>
T f()
    {
    return 0;
    }

// OK, T is int
int (*pf)() = &f;

int main()
    {
    return pf();
    }

which elicits the same translation-time results. The type of pf is "pointer to function that returns an int and has no parameters." To be compatible with pf, the f instance chosen must take no parameters and return an int.

Happily, the translator can mint such an f instance, by replacing the template parameter T with the type argument int. Exactly one form of f is compatible with pf. As a result the translator can unambiguously deduce which instantiation is needed here. No extra explicit information is required.

While I can't find a passage in the C++ Standard that specifically allows this code, I find relevant support in these subclauses:

Membership Has Its Privileges

Q

I was forwarded the following from colleagues at work. You might find this bug in VC6 amusing. It makes you wonder how MS codes its compilers. — Yuval El-Hanany

A

I've simplified your original code to

class X
    {
    int m;
    static void f(...)
        {
        &m; // should be error
        }
    };

X::m is not static. It therefore does not exist as a stand-alone object, but instead must be bound to some X object. Within the context of an X object, m is a subobject, and &m is the address of that subobject.

In contrast, X::f is static, and is not bound to any X object. Within the context of f, m is not a subobject, and &m is not a subobject address. Indeed, the expression &m is meaningless within f, yet Visual C++ accepts the above code. The mighty EDG translator, on the other hand, correctly diagnoses the problem:

a nonstatic member reference
must be relative to a specific object

After some experimenting, I discovered that VC++ believes &m to be a pointer to member:

static void f()
   {
   int X::*p = &m; // should be error
   }

But &m is not a pointer to member. The correct pointer-to-member syntax is

static void f()
    {
    int X::*p = &X::m; // OK
    }

Fortunately, Visual C++ correctly compiles this fixed version.

Were f non-static, &m would still not be a pointer to member, but instead would be a regular pointer to int:

void f()
    {
    int *p = &m; // OK
    }

Visual C++ gets this one right too.

Put your discovery in the category of plain ol' VC++ bug. Send it in to Ron Burk at Windows Developers Journal, (www.wdj.com) and you just might get a spiffy new T-shirt for your trouble.

Erratica

The C9X Standard has finally been approved. The Committee successfully instantiated the X with a 9 in the nick of time, making the new Standard's colloquial name C99. C99 supersedes C95, itself an amalgam of the original C90 plus the ensuing two Technical Corrigenda and one Amendment.

As I write this (late November), I don't believe that C99 is available yet for purchase. You can still get a copy of the 1998 Draft Standard at <http://anubis.dkuug.dk/JTC1/SC22/open/n2620/>. The draft rationale is available from the same site as <http://anubis.dkuug.dk/JTC1/SC22/WG14/www/docs/n897.pdf>.

Once published, the Standard will go by the elegant title ISO/IEC 9899:1999. It makes the perfect solution for last-minute gift-buying dilemmas, provides a cozy reading companion on lonely nights, and authoritatively settles geek-boy bar bets. At fine booksellers everywhere — ask for it by name.

Notes

1. C++ Standard subclause 12.6.2 ("Initializing bases and members").

2. Subclause 8.5 ("Initializers").

Bobby Schmidt is a freelance writer, teacher, consultant, and programmer. He is also an alumnus of Microsoft, a speaker at the Software Development and Embedded Systems Conferences, and an original "associate" of (Dan) Saks & Associates. In other career incarnations, Bobby has been a pool hall operator, radio DJ, private investigator, and astronomer. You may summon him on the Internet via rschmidt@netcom.com.