[rfc] small flags library

[Uploaded to the vault (flags_v1.zip) – complete with docs and test file (no jam file yet – sorry) - tested in vc8 and MinGW GCC 3.4.2.] I’ve proposed this before and gotten a little interest – so I figured I’d try again (particularly now that 1.34.1 is out) and see if I could get some more interest! At its most simple, it’s a way to stop function users providing difficult to detect erroneous data. The best way to work out what this lib does is probably read the docs I uploaded, but for here I’ll take one of the examples I use from the docs: using boost::flags enum foo { foo_1 = 1<<0, foo_2 = 1<<1, }; enum bar { bar_1 = 1<<2, bar_2 = 1<<3, }; void func1(unsigned int options) { //do stuff } void func2(flags<foo> options) { //do stuff } int main(int argc, char * argv[]) { func1(foo_1 | foo_2 | bar_1 | bar_2); //Compiles fine func2(flags<foo>(foo_1) | foo_2 | bar_1 | bar_2); //Compile time error - cannot convert bar to foo } Additionally, sometimes it is useful to force the user to supply a flag rather than allow them to supply none (i.e. '0') - the flags_no_default provides this functionality. The library is not very big and it’s not very clever :) but the hope is to push errors back to compile time, at little or no runtime cost. If people are interested or not either way it’s cool – but since it’s now in the vault anyone can use it if they want. Hope it all makes sense! Questions/comments/criticism gratefully received. IAIN

Hello, Just a few questions about the library. 1. Is it a generalization of the std::ios_base::flags approach?
func2(flags<foo>(foo_1) | foo_2 | bar_1 | bar_2); //Compile time error
2. It'd be more convenient without <CODE>flags<foo>(...)</CODE> conversion, wouldn't it? 3. Can <CODE>flags<foo>(foo_1) | foo_2</CODE> be used as a compile-time constant? With Best Regards, Marat

1. Is it a generalization of the std::ios_base::flags approach?
As I understand it std::ios_base::flags will accept any integer - valid or otherwise. My library would be used to prevent invalid integers being passed (though would require valid flags be members of a single enum). Obviously in the case of std::ios_base::flags it would require a rewrite so's the argument would be of type boost::flags<...>
func2(flags<foo>(foo_1) | foo_2 | bar_1 | bar_2); //Compile time error
2. It'd be more convenient without <CODE>flags<foo>(...)</CODE> conversion, wouldn't it?
It would be - and initially that was the idea, just that the compiler gets confused (least vc8 does). If you are passing a single flag it's fine (you can omit the flags<foo>(...) ) - if you're passing multiple flags the only way I could think of making it work would be to provide a constructor that accepts integers - which would defeat the purpose of the library. Somebody cleverer and more well versed in C++ than I am might be able to suggest an alternative though.
3. Can <CODE>flags<foo>(foo_1) | foo_2</CODE> be used as a compile-time constant?
I'm not sure I entirely understand what you are getting at here - what is it you want to do? If you mean you want to have access to the value stored in the flags class at compile time then I'd have thought that's not possible - but I can't see why that would be a problem. Alternatively if you mean will flags<foo>(foo_1) | foo_2 compile out to a single value I think that would depend on the compiler - certainly the template is ideal for inlining and once inlined it becomes identical to the |'ing of integer values, so could easily be reduced to a single value. Otherwise you've lost me :o) Hope that helps - if not give me a shout and I'll try and give better answers. Apologies if I've misunderstood anything. Cheers, IAIN

Iain Denniston wrote:
1. Is it a generalization of the std::ios_base::flags approach?
As I understand it std::ios_base::flags will accept any integer - valid or otherwise.
Here in standard library of cygwin gcc 3.4.4 it doesn't.
My library would be used to prevent invalid integers being passed (though would require valid flags be members of a single enum). Obviously in the case of std::ios_base::flags it would require a rewrite so's the argument would be of type boost::flags<...>
Its argument is <CODE>std::ios_base::fmtflags</CODE> here.
2. It'd be more convenient without <CODE>flags<foo>(...)</CODE> conversion, wouldn't it?
[...] if you're passing multiple flags the only way I could think of making it work would be to provide a constructor that accepts integers - which would defeat the purpose of the library.
Well, I also don't think it is possible with integers, but it is possible with flags being not integers like above. Alternatively, can you make it <CODE>flags(foo_1) | foo2</CODE> (with <CODE>flags</CODE> being function) or <CODE>flags | foo_1 | foo2</CODE>? By the way, why not perform left shift in flags constructor instead of enum?
3. Can <CODE>flags<foo>(foo_1) | foo_2</CODE> be used as a compile-time constant?
I'm not sure I entirely understand what you are getting at here - what is it you want to do?
I want it to be usable as a template parameter or switch case. For instance, <CODE>class_parametrized_with_iosflags<std::ios_base::dec | std::ios_base::left></CODE> will not compile. What about <CODE>class_parametrized_with_foo<flags<foo>(foo_1) | foo_2></CODE>? With Best Regards, Marat

Apologies for the delayed response - things got a bit mad here.
Its argument is <CODE>std::ios_base::fmtflags</CODE> here.
Yes - but fmtflags is simply typedef'd to int somewhere deep in the STL. Certainly this is the case with STLPort and the dinkumare STL (built in VC version of STL). Hence anything that is an int or can be converted to one can be passed as an argument to the function - the precise problem I'm trying to fix.
Alternatively, can you make it <CODE>flags(foo_1) | foo2</CODE> (with <CODE>flags</CODE> being function) or <CODE>flags | foo_1 | foo2</CODE>?
By the way, why not perform left shift in flags constructor instead of enum?
Both interesting ideas - I hadn't considered doing either, worth exploring at least. I'll have a look at them :o)
I want it to be usable as a template parameter or switch case. For instance, <CODE>class_parametrized_with_iosflags<std::ios_base::dec | std::ios_base::left></CODE> will not compile. What about <CODE>class_parametrized_with_foo<flags<foo>(foo_1) | foo_2></CODE>?
Ahhhh - I see what you want now - sadly AFAIK there is no way to make that work for the flags lib. Although again I defer to those with greater C++ skills than I! Hope that helps IAIN

Iain Denniston wrote:
Apologies for the delayed response - things got a bit mad here.
Its argument is <CODE>std::ios_base::fmtflags</CODE> here.
Yes - but fmtflags is simply typedef'd to int somewhere deep in the STL. Certainly this is the case with STLPort and the dinkumare STL (built in VC version of STL). Hence anything that is an int or can be converted to one can be passed as an argument to the function - the precise problem I'm trying to fix.
Somehow it doesn't compile here, but it can be a feature of g++. Unfortunately I have no other compiler to test. with -pedantic:
test.cpp:10: error: No match for `ostream::flags(const int &)' ios_base.h:550: note: candidates are: _Ios_Fmtflags ios_base::flags() const ios_base.h:561: note: _Ios_Fmtflags ios_base::flags(_Ios_Fmtflags) <near match>
without:
test.cpp:10: error: invalid conversion from `int' to `_Ios_Fmtflags' test.cpp:10: error: initializing argument 1 of `_Ios_Fmtflags ios_base::flags(_Ios_Fmtflags)'
If limited to g++ now, then of course same feature for all platforms will be useful. With Best Regards, Marat

Somehow it doesn't compile here, but it can be a feature of g++. Unfortunately I have no other compiler to test.
Interesting - just tested it with MinGW GCC and with VC8 and as you say doesn't compile with GCC - but does with VC8. Delving into the gcc version of the STL provides the answer. From ios_base.h: // The following definitions of bitmask types are enums, not ints, // as permitted (but not required) in the standard, in order to provide // better type safety in iostream calls. A side effect is that // expressions involving them are no longer compile-time constants. which is then followed by overloads of all relevant bitwise operators specifically for the relevant enum type(s), thereby creating the better type safety mentioned. An interesting alternative solution, with the advantage of not requiring a construction (like my proposal) but with the disadvantage of requiring the witting of bitmask overloads for each enum requiring this treatment. So not a feature of GCC per-say - rather a feature of the provided STL. Makes me wonder why this is not used in all other STLs.
If limited to g++ now, then of course same feature for all platforms will be useful.
Well having looked into all of that I've decided I don't like my proposal any more :oD There is an easier, more elegant solution, which I'll go put together now. The idea now being that at the definition of the enum you can write something like: enum foo { ... } BOOST_BITCAST_ENUM(foo); Which will define all relevant operators for that enum - giving the safety required for the enum. Only difference is that now the function will be of the form: void func(foo options); (and will only accept foo enums or bitwise combinations of them) So thanks for that - your comments have been most helpful indeed! Iain

Hi Iain,
At its most simple, it's a way to stop function users providing difficult to detect erroneous data
Have you seen the proposed "enum class" ? http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2347.pdf I think that this could solve the problem that you're addressing, but I'm not certain if operator| is defined in a useful way for "enum class" - does anyone know? Until/unless this is adopted, maybe some macro-magic could be used to implement it, e.g. ENUM_CLASS(foo) { ENUM(foo_1,1<<0) , ENUM(foo_2,1<<1) }; This would need to expand to an enum wrapped in a class, as described in section 2.1 of the doc linked above. I'm not certain if this can be done with macros, but the Boost preprocessor library has lots of magic that would help. (The doc says this has been implemented in MS VC++ 8.0 beta, but I'm unclear whether that means that it's in the public version of that compiler, or whether someone at Microsoft has implemented it privately - does anyone know?) Regards, Phil.

Have you seen the proposed "enum class" ? http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2347.pdf
No I hadn't seen that - cheers - looks very interesting. Certainly more than capable of rectifying the issue I was looking at.
Until/unless this is adopted, maybe some macro-magic could be used to implement it, e.g.
ENUM_CLASS(foo) { ENUM(foo_1,1<<0) , ENUM(foo_2,1<<1) };
This would need to expand to an enum wrapped in a class, as described in section 2.1 of the doc linked above. I'm not certain if this can be done with macros, but the Boost preprocessor library has lots of magic that would help.
Yeah I can see how that might work - I'll have a play around and see if I can come up with anything, might well prove much more useful than my original proposal anyway. Cheers IAIN
participants (3)
-
Iain Denniston
-
Marat Khalili
-
Phil Endecott