Boost logo

Boost :

Subject: Re: [boost] Scoped Enum Emulation
From: Vicente J. Botet Escriba (vicente.botet_at_[hidden])
Date: 2012-01-26 13:24:47

Le 26/01/12 14:56, Stewart, Robert a écrit :
> Vicente J. Botet wrote:
>> Le 25/01/12 13:26, Stewart, Robert a écrit :
>>> Vicente J. Botet wrote:
>>> It looks decent, but shouldn't int be a computed type based
>>> upon the size and signed-ness of enum_type? Of course, you
>>> could also provide macros to specify the underlying type and
>>> use that, instead of enum_type, as the type of v_. That
>>> would increase compatibility with strongly typed enums in
>>> C++11.
>> The implicit underlying type is int. And yes, another set of
>> macros could be provided to allow to specify the underlying
>> type.
> I hadn't read enough about scoped enumerations before to see that the default type is exactly int, unlike unscoped enumerations.
>>> Why convert implicitly to int rather than to enum_type?
>>> You could convert to the computed type I mentioned above, but
>>> converting to enum_type would ensure the most appropriate
>>> conversions, wouldn't it?
>> The conversion operator should be explicit and to the
>> underlying type, which is int. I have to change it to use
>> explicit conversion when the compiler supports it.
> I see that now.
>>> It would be nice if the macros would produce strongly typed
>>> enums, when available, and devolve to emulation when not.
>> Have you miss the else part?
>> #define BOOST_DECLARE_STRONG_ENUM_BEGIN(x) enum class x
> Yes, I overlooked that.
>> Next follows the new macros with the sugested improvements
> Did you mean the following?
No. The true part is the emulated one. The else uses enum class.
> I wonder if preprocessor trickery could detect the difference between (NT) and (NT, UT) thereby permitting one macro to produce the right output regardless of whether the underlying type was given. Doing so might require extra parentheses, which might be perceived as worse than two macros.
Yes. Maybe this can be done easily.
>> struct NT { \
>> typedef UT underlying_type; \
>> enum enum_type
> Since you're not asking the user to repeat UT, maybe this should just be BOOST_DECLARE_STRONG_ENUM_END. That is, use two different macros to introduce the scoped enumeration (if preprocessor tricks can't reduce that to one), but just use one macro to finalize them.
I agree.
> Also, I think "DEFINE" is better than "DECLARE" because you're generating a definition (which is an implicit declaration). Furthermore, doing so would mean that "FORWARD_DECLARE" could be just "DECLARE", if you like.
I could consider that.
>> ; \
>> underlying_type v_; \
>> inline NT() {} \
> Why "inline"? The definitions are provided inline, so the keyword adds nothing. ISTR that some compiler -- perhaps a very old one -- complains about such use of the inline keyword.
You are right. These inline seems unnecessary. I don't remember why I
used them.
>> inline NT(enum_type v) : v_(v) {} \
>> inline underlying_type underlying() const {return v_;} \
> I've always preferred naming such functions with an "as_" prefix. In this context, you actually know the name of the underlying type, so you could even do as_##UT, though that wouldn't work right for unsigned int, signed int, etc. Maybe the preprocessor can help, for example, to collapse "unsigned int" to "unsigned" so you could generate "as_unsigned" even when UT is "unsigned int".
I have no used underlying yet. It was just there to show a workaround
when there is no explicit conversion. Anyway to make the user code
portable, he can not use the member function as enum class don't have
it. That mean s that we need a non-member function, let me call it

namespace boost
   template <typename UT, typename NT>
   UT underlying_cast(NT v)
     return v.underlying();
   template <typename UT, typename NT>
   UT underlying_cast(NT v)
     return static_cast<UT>(v);

The portable explicit conversion to the underlying type becomes

   int i = boost::underlying_cast<int>(e);

> It looks to me as if you could simplify the above since the only difference between the sections, with and without BOOST_NO_EXPLICIT_CONVERSION_OPERATORS defined, is one operator. Thus:
> inline explicit operator underlying_type() const \
> {return underlying();}
> #else
> #endif
> ; \
> underlying_type v_; \
> inline NT() {} \
> inline NT(enum_type v) : v_(v) {} \
> inline underlying_type underlying() const {return v_;} \
> };
Yes. We can use instead a macro to replace just explicit as it
BOOST_CONSTEXPR is used for constexpr

      BOOST_EXPLICIT operator underlying_type() const \
      {return underlying();}

At the end I'm wondering if the implicit conversion should even be
provided, so I will adopt the internal macro.
After removing the implicit conversion I have added

     friend inline bool operator ==(NT lhs, enum_type rhs) {return
enum_type(lhs.v_)==rhs;} \
     friend inline bool operator ==(enum_type lhs, NT rhs) {return
lhs==enum_type(rhs.v_);} \
     friend inline bool operator !=(NT lhs, enum_type rhs) {return
enum_type(lhs.v_)!=rhs;} \
     friend inline bool operator !=(enum_type lhs, NT rhs) {return
lhs!=enum_type(rhs.v_);} \

> NP
> One last thought: why "strong"? The standard calls them "scoped enumerations" so why not "SCOPED_ENUM_"?
I have no problems with renaming them.

I will commit something on the sandbox soon so that we can continue
improving it.


Boost list run by bdawes at, gregod at, cpdaniel at, john at