C/C++ Contributing Editors


Uncaught Exceptions: One Small Step

Bobby Schmidt

Some steps are smaller than others, even if they look big to a compiler.


Copyright © 1999 Robert H. Schmidt

As I write, today is the 30th anniversary of the Apollo 11 moon walk. I remember Mom waking me up to watch the fuzzy black-and-white live pictures. I was seven at the time, and quite captivated by astronomy and the space program. I still have the Golden Guide to the Stars my grandmother gave me, from which I learned the major constellations visible beyond Ohio's haze, streetlights, and rooftops.

Three decades later, I've traded up to Redmond's clouds, streetlights, and treetops. CUJ Editor-in-Chief Marc Briand keeps inviting me out to Kansas to chase storms — but I may have to take up his offer just to chase stars. Actually, some business interests want to build a spaceport in Eastern Washington for the next-generation reusable space vehicle. Eastern Washington is closer than Kansas and about as flat, a much more convenient source for my astronomy fix.

As a counterpoint to nostalgia, my 13-year-old stepdaughter Renée has no memory of the moon landings; they are as remote in her history as D-Day is in mine. She also represents an irony, at least today. If Apollo 11 was NASA's zenith, then the Challenger explosion was its nadir — and Renée was born on the very day of that explosion.

So here's to the men on the moon in my youth, and the hope of men and women on Mars in my old age. Perhaps in Renée's old age we'll have a permanent lunar or Martian colony, complete with 13-year-olds having no memory of walking on Earth.

More Conversion Ambiguity

Q

Dear CUJ,

I've tried to customize Microsoft's COM VARIANT type, because I wanted to type-cast its value to CString, but I couldn't compile my changed version. I found the enclosed code to be the core of the problem.

It worked on Borland C++ 4.52, and somebody told me that he could compile it under GCC 2.7.2.4. But under MSVC 5.0 and GCC 2.8.1 it didn't work. I didn't find anything relevant in the draft C++ Standard.

Could you tell me something about this problem? The compiler claims the code contains ambiguity, but I don't understand why the compiler does not recognizes the type-cast operator. — Istvan Nemeth

A

Sometimes I wish C++ didn't support conversion operators. The ambiguities wrought by mixing conversion constructors and conversion operators seem never-ending.

As is my wont, I've simplified your code:

class A
    {
public:
    A(int);
    };
     
class B
    {
public:
    operator int();
    operator A();
    };
     
int main()
    {
    B b;
    (A) b; // error here
    }

Your compilers apparently can't decide if

(A) b;

means

b.operator A();

or

A(b.operator int());

I'm guessing that you expect the compilers to unambiguously choose the first interpretation.

Based on my reading of the C++ Standard, that first interpretation is indeed correct. The language rules generally prefer the "shortest" distance when choosing among conversion paths. In this example, the first path requires only one conversion hop — B to A — while the second path requires two — B to int, then int to A. I don't have GCC installed anymore, so I can't verify its behavior. Of the translators I do have installed, Visual C++ 5 works incorrectly, while both CodeWarrior 4 and EDG 2.41.2 work correctly.

On a related note, consider this example:

class A
    {
public:
    A(int);
    };
     
class B
    {
public:
    operator int();
    };
     
int main()
    {
    B b;
    A a1 = b;     // should be error
    A a2 = (int) b; // OK
    A a3(b);        // ???
    }

The only possible conversion path for

A a1 = b;

is

A a1 = A(b.operator int());

which requires two implicit conversions (B to int, then int to A). But the Standard specifically forbids multiple implicit user-defined conversions to the same value [1]. As a result, the definition should not compile, yet both VC++ and CodeWarrior accept it. EDG (correctly) does not.

Contrast this to

A a2 = int(b);

which requires the same two conversions — but only one of those conversions (int to A) is implicit. The other conversion (B to int) is explicit, thanks to the cast. Since only one implicit user-defined conversion is required, the definition is valid.

As an Exercise For The Student, I leave you with this problem: without running your compiler, decide if the definition

A a3(b);

should compile.

Counting Chickens

In my July column I ran an item titled Chicken and Egg, wherein Diligent Reader Marlon Nelson desired a function returning a pointer to itself. After some scratching around, I decided that such a thing wasn't possible. I ended my response by trawling for alternatives:

If any Diligent Reader has a portable and robust solution to this problem, please let me know.

In hindsight, I should have seen this problem as the sort that generates 50-message threads on comp.lang.c++. Not since the Hungarian Notation donnybrook have I received so much reader mail on a single topic [2]. While I can't run all of your proposed solutions, I do want to show a representative sampling [3].

Caveat: Nobody actually submitted a solution that literally describes a function returning a pointer to itself. Instead, all respondents satisfied themselves with code mimicking the appearance of such a function.

I'm thrilled to tell you that the first solution came from our own Dan Saks, whom I now officially welcome to the exalted ranks of Diligent Reader. His solution is

class itself
    {
    friend bool
    operator==(itself const &lo,
        itself const &ro)
        {
        return lo.f == ro.f;
        }
public:
    itself(itself p()) : f(p)
        {
        }
    itself operator()() const
        {
        return f();
        }
private:
    itself (*f)();
    };
     
itself f()
    {
    return f;
    }
     
int main()
    {
    f == f();
    }

Unfortunately this code fails to compile on some of our systems. I traced the problem to the private data member declaration; changing that declaration to

class itself (*f)();

allows the code to compile on all of our systems.

Inside f, the expression

return f;

gives the appearance that f is literally returning itself. Beneath the appearance, however, f does not return f directly, but instead returns an itself object which in turn contains a pointer to f. The effect is the same as if the return statement were really

return itself(f);

This trick — hiding f's address in a class object — is one that most reader solutions share. While the trick satisfies the aesthetic requirements of the original problem, it also requires some agent to extract the encapsulated function pointer. Dan achieves this within operator==, which splits open its two itself arguments to compare the pointers within.

Note that Dan's solution is not complete. For example, if I want to write

f != f()

I need to create my own operator!= function. In fact, everywhere I might want to use an itself object in an expression, I'm faced with possibly adding or tweaking user-defined operators.

One remedy: use the language's built-in operators instead. For that remedy to work, the returned itself object must convert into some type compatible with those operators. The obvious target for such a conversion is the underlying function pointer, so that

f == f()

actually uses the language's built-in operator== for comparing pointers.

Since the type I want itself to convert to is the type of a function pointer — specifically, the type itself (*)() — my first thought was to add the conversion operator

class itself
    {
    // ...
public:
    operator itself (*)()()
        {
        return f;
        }
    }

Dan had the same thought but dismissed it: the code did not translate on any system we tried, including EDG. Reading through the C++ Standard's grammar, we finally convinced ourselves that such operator declarations are syntactically invalid.

At this point I got my patented Bright Idea: wrap the function pointer within a typedef, thereby keeping the same semantics while satisfying the grammar's syntactic requirements. Using such a typedef, my final solution is

class itself
    {
private:
    typedef itself (*pointer)();
public:
    itself(pointer p) : p_(p)
        {
        }
    operator pointer() const
        {
        return p_;
        }
    itself operator()() const
        {
        return p_();
        }
private:
    pointer p_;
    };
     
itself f()
    {
    return f;
    }
     
int main()
    {
    f == f();
    itself p = f();
    p == p();
    p = p();
    }

Apparently great minds think alike, for several readers similarly use a typedef in their solutions. Among those readers is Herb Sutter, whose solution is the same as mine with two twists: he defines the pointer typedef outside the scope of itself, and omits the function operator:

class itself;
     
typedef itself (*pointer)();
     
class itself
    {
public:
    itself(pointer p) : p_(p)
        {
        }
    operator pointer() const
        {
        return p_;
        }
private:
    pointer p_;
    };

As a result, he can't use the function-call syntax on an itself object, but can call through the pointer instead:

int main()
    {
    itself p = f();
    p(); // error
    pointer q = f();
    q(); // OK
    }

Because it can't directly manipulate an object of f's return type (itself), I'd argue that Herb's solution is less aesthetic than mine. At the same time, I'd also argue that his solution is more efficient: instead of constructing, destroying, and referencing a class object, his solution manipulates a function pointer.

Herb ends his email with a challenge:

Of course, normally a special-purpose itself proxy class like this (that contains some old object and doesn't really care much about its type) just cries out to be templatized into a general-purpose Holder proxy. Alas, I don't think it's possible here without breaking the solution, because the typedef would have to look something like:

typedef Holder<pointer> (*pointer)();

which is self-referential. Anyone care to demonstrate that it can be done?

A few readers submitted solutions dispensing with function pointers altogether. Representative of those solutions is this from Volker Simonis:

class itself
    {
public:
    itself operator()()
        {
        return *this;
        }
    operator bool()
        {
        return true;
        }
    };
     
int main(void)
    {
    itself p;
    while (p = p())
        ;
    }

The solution deviates from the original question's spirit, since it has no non-member function from which to return a value. However, the solution does allow for a compact itself definition, and the resulting p "call" within main has the desired syntax.

Eric Hopper proposes a solution that works in C:

struct itself;
     
typedef struct itself
    (*pointer)(void);
     
typedef struct itself
    {
    pointer f_;
    }
itself;
     
itself f(void)
    {
    itself p = {f};
    return p;
    }
     
int main(void)
    {
    itself p = {f};
    while ((p = p.f_()).f_ != f)
        ;
    return 0;
    }

where the C statements in main are equivalent to the C++ statements

itself p = f;
while ((p = p()) != f)
    ;

Because C doesn't have conversion operators or conversion constructors, the value f must be explicitly converted into the object type itself:

itself p = {f};

This explicit conversion denudes the previous C++ abstraction, since the return expression does not appear to be returning the function itself.

Notes

[1] Subclause 12.3p4 ("Conversions").

[2] My other recent mail magnet: MSVC vs. for-scoped objects. The lesson here, I guess, is that the best way to draw a crowd is by either dissing Microsoft or running brain teasers.

[3] As you can well imagine, each reader's solution uses a unique set of names. To better show their significant differences, I've "normalized" all solutions to use the same names for the same purposes.

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.