Boost logo

Boost :

From: Paul Mensonides (pmenso57_at_[hidden])
Date: 2003-01-09 14:41:22


----- Original Message -----
From: "Daniel Frey" <daniel.frey_at_[hidden]>

Terje Slettebø wrote:
>
> > Just to make sure: Do you "vote" in favor of enums? I have seen problems
> > with 'static const ...', but I have never seen problems with enums
> > (although they theoretically exist).
>
> Not just theoretically. As mentioned in an earlier posting in this thread,
> BCC doesn't work well with enums, but it does work with static const.
Also,
> there's the issues pointed out by Paul Mensonides, due to the fact that
> enums are UDTs.

> Sorry, I should have been more exact. Of course we can only choose a
> "default" implementation. If a compiler is buggy, we need to work-around
> anyway. The drawback of enums, mentioned by Paul, is the one I call
> theoretically, as I have never found this to be a problem in the real
> world (well, in *my* real world :). A problem like this can be *created*
> but special code which I have yet to see in "normal" programs. OTOH I
> have already seen a colleage wondering about link-failures due to
> passing a 'static const int ...' to a function which took a 'const T&'.
> I consider this much more likely to happen in daily programming, YMMV :)

The lack of problems in the "real world" is partially because compilers
don't implement enumerations correctly. Even Comeau C++ gets this wrong (as
in the small code sample I sent earlier in this thread). The problems
associated with the use of enum are problems that cannot be directly
avoided. They break encapsulation, and it is not the library's business
whether a user does or doesn't do something that can affect the library
internally but shouldn't. This is precisely the reason that macros are so
derided--they cut across encapsulation boundaries.

This reference problem, incidently, comes primarily from arguments to
functions that are templated--either by themselves or as members of a class
template. Otherwise, it should be a non-issue because there is no good
reason to pass an integral constant by reference to const. This problem is
typically bypassed by using enumeration values instead. However, this
raises another interesting problem. Many enumerations used for this purpose
are unnamed:

template<class T> struct is_ptr {
    enum { value = false };
};

template<class T> struct is_ptr<T*> {
    enum { value = true };
};

This has its own problem in the same templated "pass by reference"
situation. Namely, a template type argument cannot refer to an unnamed
type:

template<class T> T func(const T& ref) {
    return ref;
}

int main() {
    func( is_ptr<int*>::value );
        // error: argument deduction yields unnamed type
    return 0;
}

The only way to solve this problem is to make a name for every enumeration
type, rather than just the enumerators. However, this has its own
associated problems--not even counting the annoyance of having to name the
enumeration. It can cause code bloat by instantiated templates:

template<class T> class A {
    // ...
    enum val_t { value = /* ??? */ };
};

template<class T> class B {
    // ...
    enum val_t { value = /* ??? */ };
};

template<class T> void f(const T& ref) {
    // ...
    return;
}

int main() {
    // two unique instantiations of 'f'...
    f(A<int>::value);
    f(B<int>::value);
    return 0;
}

Unfortunately, as it is now, because compilers don't support enumerations
and static constants properly, we have a limitation that says "this value
cannot be used as anything but a constant value, not even as a function
argument of any kind without a user-specified cast at the call site." The
reason is simple, unless we use only 'static const int' or enum (depending
on the compiler) overload results can change from platform to platform.
This makes the current mechanism very delicate--the only thorough solution
is to cast the enumerator to int, bool, or whatever before any arithmetic,
since there is no way that user code can break it.

There are strengths to both enumerations and static constants. Given a
perfect world (of compilers that is), enumerations only have two advantages:
1) they are syntactically easier to specify, and 2) they are pure values,
i.e. built-in temporaries, as far as const& is concerned. Enumerations have
downsides as well: 1) they are user-defined types, and therefore, user code
can change integral constant expressions inside the library to runtime
expressions, which has the potential to break the entire library, 2) they
are not strongly typed in a way that helps distinguish the purpose of the
value, and 3) they are uniquely typed, which has the potential to cause code
bloat.

Static constants, on the other hand, have couple of advantages also: 1)
they are strongly typed, which conveys meaning and overloading that matches
that meaning, and 2) they are encapsulated so as not to interfere with the
rest of the language. The downside of static constants is if used as an
actual object (i.e. address), they must have an external definition, which
is "inconvenient." ;)

I still think the benefits of static constants, despite the minor
inconvenience, outweigh the benefits of enumerations, but, at this point,
the argument is moot, since compilers don't support either properly. This
is a pity because some of the most useful metaprogramming code is that which
is mixed into runtime code. Of course, we can also opt for the worst of all
possibilities: static const values of unnamed enumeration type:

struct A {
    static const enum { v = 10 } value = v;
};

This has every problem imaginable! ;)

Personally, I use enumeration values for internal values and cast them to
the appropriate type before any arithmetic or bit manipulation and use
static constants for any external interface:

template<class T> class X {
    private:
        enum {
            a = /* some calculation */,
            b = /* some calculation */
        };
    public:
        static const bool value = int(a) || int(b);
};

template<class T> const bool X<T>::value;

Paul Mensonides


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk