Scoped enum question

W

woodbrian77

I'm trying to do a bitwise and of a uint8_t and a member
of a scoped enum.

Here's the type with the uint8_t:

struct cmw_user_input
{
uint8_t marshalling_bits;
::std::deque<cmw::File> headers;
::cmw::File middlefile;
uint16_t choices;

// some stuff removed.
};

Here's the enum:

enum class cmw_user_input_data_member_flags : ::std::uint8_t
{
headers=0x1
, middlefile=0x2
, choices=0x4
};

GCC and clang are OK with those, but don't like this:

void
cmw_user_input::MarshalMemberData :):cmw::SendBuffer& buf) const
{
buf.Receive(marshalling_bits);
if(marshalling_bits&cmw_user_input_data_member_flags::headers){
buf.ReceiveGroup(false,headers);
}
// some stuff removed.
}

GCC says:
error: no match for 'operator&' (operand types are 'const uint8_t {aka const unsigned char}' and 'cmw_user_input_data_member_flags')
if(marshalling_bits&(cmw_user_input_data_member_flags::headers)){
-----------------------------------------------------------------------

If I add a static_cast to the if statement:

if(marshalling_bits&static_cast<uint8_t> (cmw_user_input_data_member_flags::headers)){
buf.ReceiveGroup(false,headers);
}

the compiler is OK with it. I guess the compilers are
doing what they're supposed to, but I'm not sure what to do.
In the code above, the scoped enum is being generated
based on cmw_user_input, so I don't think it will
work to make marshalling_bits be a
cmw_user_input_data_member_flags.
Is there a better option than the cast? Tia.

Brian
Ebenezer Enterprises - John 3:16.
http://webEbenezer.net
 
S

SG

[...]
enum class cmw_user_input_data_member_flags : ::std::uint8_t
{
  headers=0x1
  , middlefile=0x2
  , choices=0x4
  };

GCC and clang are OK with those, but don't like this:

void
cmw_user_input::MarshalMemberData :):cmw::SendBuffer& buf) const
{
  buf.Receive(marshalling_bits);
  if(marshalling_bits&cmw_user_input_data_member_flags::headers){
    buf.ReceiveGroup(false,headers);
  }
  // some stuff removed.
}

GCC says:
error: no match for 'operator&' (operand types are 'const uint8_t
{aka const unsigned char}' and 'cmw_user_input_data_member_flags')
   if(marshalling_bits&(cmw_user_input_data_member_flags::headers)){
-----------------------------------------------------------------------

If I add a static_cast to the if statement:

if(marshalling_bits&static_cast<uint8_t>
  (cmw_user_input_data_member_flags::headers)){
    buf.ReceiveGroup(false,headers);
  }

the compiler is OK with it.   I guess the compilers are
doing what they're supposed to, but I'm not sure what to do.

Yes, I think so. One point of scoped enums is better type safety.
That means that they don't implicitly convert to integers anymore.
In the code above, the scoped enum is being generated
based on cmw_user_input, so I don't think it will
work to make marshalling_bits be a
cmw_user_input_data_member_flags.
Is there a better option than the cast?  Tia.

The cast is exactly what you need. You could also provide an
overloaded operator, but what should be the return type of

??? operator&(uint8_t lhs, cmw_user_input_data_member_flags rhs);

Is it uint8_t or cmw_user_input_data_member_flags? I guess you could
make it return an uint8_t since it's more general than a
cmw_user_input_data_member_flags. But then you're almost back to the
kind of type safety the "old" enums offered.
 
W

woodbrian77

Yes, I think so. One point of scoped enums is better type safety.
That means that they don't implicitly convert to integers anymore.

They are kind of a pain, but I guess they can also
save you from some problems. An ounce of prevention ...
The cast is exactly what you need. You could also provide an
overloaded operator, but what should be the return type of

??? operator&(uint8_t lhs, cmw_user_input_data_member_flags rhs);


Is it uint8_t or cmw_user_input_data_member_flags? I guess you could
make it return an uint8_t since it's more general than a
cmw_user_input_data_member_flags. But then you're almost back to the
kind of type safety the "old" enums offered.

I found out I can forward declare the scoped enum.

enum class cmw_user_input_data_member_flags : std::uint8_t;

struct cmw_user_input
{
cmw_user_input_data_member_flags marshalling_bits;
::std::deque<cmw::File> headers;
::cmw::File middlefile;
uint16_t choices;
};

So I'm going to try that and then generate something
like this:

uint8_t operator&(cmw_user_input_data_member_flags lhs,cmw_user_input_data_member_flags rhs)
{
return static_cast<uint8_t>(lhs) & static_cast<uint8_t>(rhs);
}
 
Ö

Öö Tiib

I found out I can forward declare the scoped enum.

enum class cmw_user_input_data_member_flags : std::uint8_t;

C++11 allows to forward declare old style enums with underlying type specified
as well:

enum cmw_user_input_data_member_flags : std::uint8_t;

So if old enum is more convenient for you then use it. On any case try to keep
style of your code base uniform. For example have every enum in it
'enum class' or none. Code that has style is always better to work with than
tasteless mix of styles.
 
M

Marcel Müller

uint8_t operator&(cmw_user_input_data_member_flags lhs,cmw_user_input_data_member_flags rhs)
{
return static_cast<uint8_t>(lhs)& static_cast<uint8_t>(rhs);
}

Since the opposite conversion from uint8_t to enum class is also not
implicit, you might prefer
cmw_user_input_data_member_flags
operator&(cmw_user_input_data_member_flags
lhs,cmw_user_input_data_member_flags rhs)

Because I need this type of operators quite often I have a macro for
this purpose.

#define FLAGSENUM(T) \
inline static T operator|(T l, T r) \
{ return (T)((unsigned)l|(unsigned)r); } \
inline static T operator&(T l, T r) \
{ return (T)((unsigned)l&(unsigned)r); } \
inline static T& operator|=(T& l, T r) \
{ return l = (T)((unsigned)l|(unsigned)r); } \
inline static T& operator&=(T& l, T r) \
{ return l = (T)((unsigned)l&(unsigned)r); } \
inline static T operator*(bool l, T r) \
{ return (T)(-l&(unsigned)r); } \
inline static T operator*(T l, bool r) \
{ return (T)((unsigned)l&-r); } \
inline static T operator~(T a) \
{ return (T)~(unsigned)a; }

Well, I know, macros are evil, but AFAIK there is definitely no way
around without repeating myself over and over.


Marcel
 
Ö

Öö Tiib

Because I need this type of operators quite often I have a macro for
this purpose.

#define FLAGSENUM(T) \
inline static T operator|(T l, T r) \
{ return (T)((unsigned)l|(unsigned)r); } \
inline static T operator&(T l, T r) \
{ return (T)((unsigned)l&(unsigned)r); } \
inline static T& operator|=(T& l, T r) \
{ return l = (T)((unsigned)l|(unsigned)r); } \
inline static T& operator&=(T& l, T r) \
{ return l = (T)((unsigned)l&(unsigned)r); } \
inline static T operator*(bool l, T r) \
{ return (T)(-l&(unsigned)r); } \
inline static T operator*(T l, bool r) \
{ return (T)((unsigned)l&-r); } \
inline static T operator~(T a) \
{ return (T)~(unsigned)a; }

Well, I know, macros are evil, but AFAIK there is definitely no way
around without repeating myself over and over.

May be traits + templates can help?

// default to no traits
template< typename enum_type >
struct enum_traits {};

// make operator or if it fits to that enum type
template< typename T >
inline typename std::enable_if< enum_traits< T >::is_flags, T >::type
operator|(T l, T r)
{
typedef std::underlying_type<T>::type U;
return (T)((U)l|(U)r);
}

// ...
// make all other operators and methods in same style

Then I trust you only have to specialize enum_traits with 'is_flags' for having
operator bitwise or for them:

enum class MyFlags : unsigned char
{
a = 1,
b = 2,
c = 4
};

// that is short so no point to have macro instead
template<> struct enum_traits< MyFlags >
{
static bool const is_flags = true;
};

Feels that it must work. Don't have C++ compiler here, so it is untested.
 
W

woodbrian77

class cmw_user_input_data_member_flags
{
int16_t val;

public:
constexpr static int16_t headers = 0x1, middlefile = 0x2, choices = 0x4;

constexpr cmw_user_input_data_member_flags(): val(0)
{}
};

Is there a way to restrict things so the value of val is only
set using the flags: headers, middlefile, ... ? I want
something like enum class, but with more flexibility than enum
class.
 
Ö

Öö Tiib

class cmw_user_input_data_member_flags
{
int16_t val;

public:
constexpr static int16_t headers = 0x1, middlefile = 0x2, choices = 0x4;

Typically here is also 'none = 0'
constexpr cmw_user_input_data_member_flags(): val(0)

This is typically 'val(none)'.

Note that such design does not protect you from misusing the
"enumerators" of it in wrong place.
Is there a way to restrict things so the value of val is only
set using the flags: headers, middlefile, ... ? I want
something like enum class, but with more flexibility than enum
class.

Flexibility? The programs that we write are just functions that call other
functions. Functions can have three kinds of parameters 'in', 'out' and
'in-out'. There are no other kinds of parameters.

Additionally one 'out' parameter can be chosen to be special (as return
value) and one 'in-out' parameter can be chosen to be special (as 'this'
of member function). The "specialness" of those two parameters is
actually only syntax difference that makes code bit easier to read.
For a compiler these two are quite ordinary parameters as well.

'enum class' is a type that can not be 'this' in member function call. If
you need 'in-out' parameter of type 'enum class' then you are limited
to ordinary parameters of functions or overloaded operators. That is
the only lacking flexibility. Everything else can be done with it.
 
W

woodbrian77

Typically here is also 'none = 0'


This is typically 'val(none)'.

Thanks.


Note that such design does not protect you from misusing the
"enumerators" of it in wrong place.

In that stackoverflow link, Ben Voigt wrote:

"No, but you can make a normal class type act like an enum class,
using constexpr members and constructors. And then you can add
all the additional member functions you want."

I don't know how to make a normal class act more like an
enum class than what I've got now. As you mention, what
I have now doesn't have the same semantics as an enum class.
Flexibility? The programs that we write are just functions that call other
functions. Functions can have three kinds of parameters 'in', 'out' and
'in-out'. There are no other kinds of parameters.

Now you boil it down. I hope you aren't too hard boiled.
 
Ö

Öö Tiib

In that stackoverflow link, Ben Voigt wrote:

"No, but you can make a normal class type act like an enum class,
using constexpr members and constructors. And then you can add
all the additional member functions you want."

I don't know how to make a normal class act more like an
enum class than what I've got now. As you mention, what
I have now doesn't have the same semantics as an enum class.

You can enwrap actual 'enum class', in that way you can loan all its features:

class wrapper
{
public:
enum class inner: int16_t {n=0,h=1,m=2,c=4,a=7};
constexpr static inner none = inner::n, headers = inner::h, middlefile = inner::m
, choices = inner::c, all = inner::a;

... etc.
Now you boil it down. I hope you aren't too hard boiled.

In sense that cynical? Perhaps. For me enum is just like somewhat
restricted int. Otherwise it is as light and efficient. The functions
manipulating enums should be inline because they usually compile to
very few instructions. Overloaded operators fit very well.

C++ lets us to play with semantics a lot but it is often just details
of parameter passing. Sometimes it may give good effects, sometimes
cause inefficiency and sometimes it may distract ... you have to judge
yourself.
 
L

Luca Risolia

class cmw_user_input_data_member_flags
{
int16_t val;
public:
constexpr static int16_t headers = 0x1, middlefile = 0x2, choices = 0x4;
constexpr cmw_user_input_data_member_flags(): val(0) {}
};
Is there a way to restrict things so the value of val is only
set using the flags: headers, middlefile, ... ? I want
something like enum class, but with more flexibility than enum
class.

Do you mean something like this?

class cmw_user_input_data_member_flags {
public:
enum E : int16_t {
headers = 0x1,
middlefile = 0x2,
choices = 0x4
};
constexpr cmw_user_input_data_member_flags() noexcept {};
constexpr cmw_user_input_data_member_flags(E v) noexcept : val{v} {};

cmw_user_input_data_member_flags(const cmw_user_input_data_member_flags& o)
noexcept : val{o.val} {};

cmw_user_input_data_member_flags& operator=(E v) noexcept
{val = v; return *this;};

cmw_user_input_data_member_flags&
operator=(cmw_user_input_data_member_flags& o) noexcept
{val = o.val; return *this;};

// other operators: &, |, etc...

operator E() const noexcept {return val;};
protected:
E val{};
private:
//operator long int(); // eventually
};
 
W

woodbrian77

Do you mean something like this?

Looks interesting.
class cmw_user_input_data_member_flags {
public:
enum E : int16_t {
headers = 0x1,
middlefile = 0x2,
choices = 0x4
};
constexpr cmw_user_input_data_member_flags() noexcept {};
constexpr cmw_user_input_data_member_flags(E v) noexcept : val{v} {};

cmw_user_input_data_member_flags(const cmw_user_input_data_member_flags& o)
noexcept : val{o.val} {};

cmw_user_input_data_member_flags& operator=(E v) noexcept
{val = v; return *this;};

cmw_user_input_data_member_flags&
operator=(cmw_user_input_data_member_flags& o) noexcept
{val = o.val; return *this;};

// other operators: &, |, etc...

operator E() const noexcept {return val;};
protected:
E val{};

Are the {} some sort of initialization?

I tried changing that to

E val{0};

and clang croaked:
ew2.cc:27:11: error: cannot initialize a member subobject of type
'cmw_user_input_data_member_flags::E' with an rvalue of type 'int'
E val{0};
^
0 libLLVM-3.2.so 0x00007fc35ddd90bf
1 libLLVM-3.2.so 0x00007fc35ddd9529
2 libpthread.so.0 0x00007fc35d0811e0
3 clang 0x0000000000f3ade0
4 clang 0x0000000000f551ec
5 clang 0x0000000000f55ab5
6 clang 0x0000000000f562c0 clang::Expr::isPotentialConstantExpr(clang::FunctionDecl const*, llvm::SmallVectorImpl<std::pair<clang::SourceLocation, clang::partialDiagnostic> >&) + 1200
7 clang 0x00000000009cf02d clang::Sema::CheckConstexprFunctionBody(clang::FunctionDecl const*, clang::Stmt*) + 1005
8 clang 0x000000000098b7b7 clang::Sema::ActOnFinishFunctionBody(clang::Decl*, clang::Stmt*, bool) + 1527
9 clang 0x00000000008e3306 clang::parser::parseFunctionStatementBody(clang::Decl*, clang::parser::parseScope&) + 182
10 clang 0x00000000008f13da clang::parser::parseLexedMethodDef(clang::parser::LexedMethod&) + 490
11 clang 0x00000000008f1170 clang::parser::parseLexedMethodDefs(clang::parser::parsingClass&) + 128
12 clang 0x00000000008a45e4 clang::parser::parseCXXMemberSpecification(clang::SourceLocation, unsigned int, clang::Decl*) + 1108
13 clang 0x00000000008a5c57 clang::parser::parseClassSpecifier(clang::tok::TokenKind, clang::SourceLocation, clang::DeclSpec&, clang::parser::parsedTemplateInfo const&, clang::AccessSpecifier, bool, clang::parser::DeclSpecContext) + 3127
14 clang 0x0000000000893360 clang::parser::parseDeclarationSpecifiers(clang::DeclSpec&, clang::parser::parsedTemplateInfo const&, clang::AccessSpecifier, clang::parser::DeclSpecContext, clang::parser::LateParsedAttrList*) + 1888
15 clang 0x00000000008814cf clang::parser::parseDeclOrFunctionDefInternal(clang::parser::parsedAttributesWithRange&, clang::parsingDeclSpec&, clang::AccessSpecifier) + 79
16 clang 0x0000000000881d21
17 clang 0x0000000000881d4f clang::parser::parseDeclarationOrFunctionDefinition(clang::parser::parsedAttributesWithRange&, clang::parsingDeclSpec*, clang::AccessSpecifier) + 31
18 clang 0x00000000008837a7 clang::parser::parseExternalDeclaration(clang::parser::parsedAttributesWithRange&, clang::parsingDeclSpec*) + 103
19 clang 0x000000000088424f clang::parser::parseTopLevelDecl(clang::OpaquePtr<clang::DeclGroupRef>&) + 191
20 clang 0x000000000087ca7e clang::parseAST(clang::Sema&, bool, bool) + 270
21 clang 0x0000000000613b11 clang::FrontendAction::Execute() + 97
22 clang 0x00000000005fa23a clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) + 282
23 clang 0x00000000005e3362 clang::ExecuteCompilerInvocation(clang::CompilerInstance*) + 1378
24 clang 0x00000000005de388 cc1_main(char const**, char const**, char const*, void*) + 1160
25 clang 0x00000000005dd2ab main + 7259
26 libc.so.6 0x00007fc35c7cea15 __libc_start_main + 245
27 clang 0x00000000005ddd89
Stack dump:
0. Program arguments: /usr/bin/clang -cc1 -triple x86_64-unknown-linux-gnu -emit-obj -mrelax-all -disable-free -disable-llvm-verifier -main-file-name new2.cc -mrelocation-model static -mdisable-fp-elim -fmath-errno -masm-verbose -mconstructor-aliases -munwind-tables -fuse-init-array -target-cpu x86-64 -target-linker-version 2.23.1 -momit-leaf-frame-pointer -resource-dir /usr/bin/../lib/clang/3.2 -fmodule-cache-path /var/tmp/clang-module-cache -internal-isystem /usr/lib64/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2 -internal-isystem /usr/lib64/gcc/x86_64-unknown-linux-gnu/4..7.2/../../../../include/c++/4.7.2/x86_64-unknown-linux-gnu -internal-isystem /usr/lib64/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/backward -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/clang/3.2/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -std=c++0x -fdeprecated-macro -fdebug-compilation-dir /srv/http/misc -ferror-limit 19 -fmessage-length 91 -mstackrealign -fobjc-runtime=gcc -fcxx-exceptions -fexceptions -fdiagnostics-show-option-fcolor-diagnostics -o /tmp/new2-ciRU2v.o -x c++ new2.cc
1. new2.cc:30:2: current parser token ';'
2. new2.cc:3:1: parsing struct/union/class body 'cmw_user_input_data_member_flags'
3. new2.cc:10:59: parsing function body 'cmw_user_input_data_member_flags'
clang: error: unable to execute command: Segmentation fault (core dumped)
clang: error: clang frontend command failed due to signal (use -v to see invocation)
clang version 3.2 (tags/RELEASE_32/final)
Target: x86_64-unknown-linux-gnu
Thread model: posix
clang: note: diagnostic msg: PLEASE submit a bug report to http://llvm.org/bugs/ and include the crash backtrace, preprocessed source, and associated run script.
clang: note: diagnostic msg:
 
L

Luca Risolia

Are the {} some sort of initialization?

Yes, they are used for "uniform initialization" in C++11.
I tried changing that to

E val{0};

Create another flag, "default_" for example, for that purpose:

enum E : int16_t {
default_ = 0x0,

E val{default_};

Another small improvement to the interface is to make one constructor
explicit:

constexpr explicit cmw_user_input_data_member_flags(E v)

Examples:

int main() {
using Flags = cmw_user_input_data_member_flags;
Flags x{Flags::headers};
//Flags y{0x1}; // not valid
Flags y; // holds default_
y = Flags::choices;
}
 
W

woodbrian77

Another small improvement to the interface is to make one constructor
explicit:

constexpr explicit cmw_user_input_data_member_flags(E v)

Examples:

int main() {
using Flags = cmw_user_input_data_member_flags;
Flags x{Flags::headers};
//Flags y{0x1}; // not valid
Flags y; // holds default_
y = Flags::choices;
}

int
main()
{
using Flags = cmw_user_input_data_member_flags;
Flags x{Flags::headers};
//Flags y{0x1}; // not valid
Flags z; // holds default_
z = Flags::choices;
//Flags z = Flags::choices;
}


I made that ctor explicit and added another line to
your examples. If I uncomment that line I get an
error

new2.cc:39:9: error: no viable conversion from 'cmw_user_input_data_member_flags::E' to
'Flags' (aka 'cmw_user_input_data_member_flags')
Flags z = Flags::choices;
^ ~~~~~~~~~~~~~~
new2.cc:13:4: note: candidate constructor not viable: no known conversion from
'cmw_user_input_data_member_flags::E' to 'const cmw_user_input_data_member_flags &'
for 1st argument
cmw_user_input_data_member_flags(cmw_user_input_data_member_flags const& o)
^
 
L

Luca Risolia

int
main()
{
using Flags = cmw_user_input_data_member_flags;
Flags x{Flags::headers};
//Flags y{0x1}; // not valid
Flags z; // holds default_
z = Flags::choices;
//Flags z = Flags::choices;
}


I made that ctor explicit and added another line to
your examples. If I uncomment that line I get an
error

Strange.

The code I proposed compiles without any errors here:

http://ideone.com/eV9DCH

It also compiles on my GCC 4.7.3 and CLang 3.2 with
the -std=c++11 flag turned on.
 
L

Luca Risolia

//Flags z = Flags::choices;

I made that ctor explicit and added another line to
your examples. If I uncomment that line I get an
error

Well, you get an error because that's the point
of making the constructor explicit. Use

Flags z{Flags::choices};

instead.

The reason why I prefer the constructor to be explicit is that you have the
operator E() conversion defined already. Having implicit conversions enabled
in both directions is not good design usually. But you can adapt the class to
your needs, of course, if you know what you are doing.
 
L

Luca Risolia

Luca said:
Well, you get an error because that's the point
of making the constructor explicit. Use

Flags z{Flags::choices};

I forgot to mention that since C++11 you can make operator E() explicit:
explicit operator E() const noexcept {return val;};

instead of the constructor.

constexpr cmw_user_input_data_member_flags(E v) noexcept [...]
Flags z = Flags::choices; // OK
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,962
Messages
2,570,134
Members
46,690
Latest member
MacGyver

Latest Threads

Top