I have a third-party unscoped enum (that I can't modify) that I'd really like to cast to my own scoped enum. How can I provide something like a conversion operator?
What I'd like to do is something like this:
#include <iostream>
enum ThirdPartyLetter {
A=4,
B=5
};
enum class MyNumber {
ONE=1,
TWO=2
// This doesn't compile, of course
/*Number(const ThirdPartyLetter& rhs) {
if(rhs == ThirdPartyLetter::A) {
return ONE;
}
else {
return TWO;
}
}*/
};
int main() {
ThirdPartyLetter letter = ThirdPartyLetter::A;
MyNumber number = static_cast<MyNumber>(letter);
// Without the cast, this prints 4 (an invalid enum value!)
std::cout << int(number) << std::endl;
}
Is there a way to provide some kind of casting from ThirdPartyNumber to MyNumber?
An idiomatic way to do that at compile-time in C++ is using traits.
As an example:
enum Foo { ONE, TWO };
enum Bar { THREE, FOUR };
template<Foo> struct conv;
template<> struct conv<Foo::ONE> { static constexpr Bar value = Bar::THREE; };
template<> struct conv<Foo::TWO> { static constexpr Bar value = Bar::FOUR; };
If you want to do that at runtime, maybe a switch is well suited.
Anyway, you can still use traits to centralize your conversion logic and do something like this:
Bar get(Foo choice) {
switch(choice) {
case Foo::ONE:
return conv<ONE>::value;
case Foo::TWO:
return conv<TWO>::value;
}
}
Related
I have two vectors: A vector of a type that acts as a union, whose type I can retrieve.
The other vector is a vector of variants of another type.
I want to use std::transform to do a conversion to one of my variant types.
However, I receive a compile error that std::transform or rather the lambda can not deduce the return type.
Here is an example:
#include <variant>
#include <vector>
#include <algorithm>
#include <stdexcept>
// third party types
struct fooData {};
struct barData {};
struct baz {
int foo_or_bar;
fooData f;
barData b;
fooData* asFoo() {
return &f;
}
barData* asBar() {
return &b;
}
};
//my types
struct bar {
};
struct foo {
};
//converter
struct converter {
bar operator()(const barData& b) {
return bar{};
}
foo operator()(const fooData& f) {
return foo{};
}
};
int main() {
using entry = std::variant<bar,foo>;
//vector of third party type
std::vector<baz> bazzs{};
//vector of my variant type
std::vector<entry> target{};
std::transform(bazzs.begin(),bazzs.end(),std::back_inserter(target), [](baz& bazzer){
//both foo and bar are part of the variant, but it refuses to compile
switch(bazzer.foo_or_bar) {
case 0:
return converter{}(*(bazzer.asBar()));
case 1:
return converter{}(*(bazzer.asFoo()));
default:
throw std::runtime_error("ERROR");
}
});
}
How can I make this work?
Add a trailing return type specifier to your lambda:
// vvvvvvvv -- Add This
[](baz& bazzer) -> entry {
// ...
}
A lambda, like any function, has to return one single type. As it currently stands it returns different types though depending on which case gets chosen: either bar in case 0 or foo in case 1. Since not all code paths return the same type, the compiler can't decide which type the lambda should return unless you tell it.
I'd like the compiler to give a warning like:
"Banana is not a Color."
I understand that in the context of a switch statement the labels are promoted to int an the compiler is happy with 0 and doesn't care if it is "Green" or "Banana".
I was hoping -Wconversion for GCC would do the trick.
enum Color
{
Green = 0
};
enum Fruit
{
Banana = 0
};
int main()
{
Color c = Green;
switch (c)
{
case Banana:
std::cerr << "Banana" << std::endl;
break;
}
return 0;
}
Strongly typed enums:
C++11 introduces strongly typed enums, using enum class:
#include <iostream>
enum class Color
{
Green = 0
};
enum class Fruit
{
Banana = 0
};
int main() {
Color c = Color::Green;
switch (c)
{
case Fruit::Banana:
std::cerr << "Banana" << std::endl;
break;
}
return 0;
}
This code will fail exactly as you hoped:
test.cc:18:17: error: could not convert '(Fruit)0' from 'Fruit' to
'Color'
Note: enum class doesn't cause Green and Banana to be in the enclosing namespace anymore, so you have to explicitly write Color:: and Fruit:: now, but you do also get the typesafety.
The problems of warning in C++03
I don't think warning on this in C++03 would make much sense, it would basically just become noise.
People use enums as compile-time constants quite often, even for things like bit-fields. For the warning to be meaningful you'd have to catch things like enum { foo=0xf }; int c = foo; and many codebases are scattered with int/enum conversions. (Allowing this would defeat the point of any stronger type checking).
Worse still though would be enums used in almost any kind of meta programming context, where anonymous enums are not only used freely interchangeably with int types on a regular basis:
template <int I>
struct is_odd {
enum { value = !(I % 2) };
};
template <int I>
struct foo {
static void bar() { /* I is true */ }
};
template <>
struct foo<0> {
static void bar() { /* I is false */ }
};
int main() {
foo<is_odd<201>::value>::bar();
int i = is_odd<200>::value;
}
but they're also used recursively as local storage:
template <int N>
struct factorial {
enum {
// This enum is *not* compatible with the one in N-1
value = N * factorial<N - 1>::value
};
};
template <>
struct factorial<0> {
enum { value = 1 };
};
Which is a part of the reason why enum class was required in order to introduce a non-breaking way of adding type-safety over the current state of enums in C++. There would be so many warnings from existing code that a warning would be next to useless because of things like this.
Even in the fairly simple switch statement example you showed, something like this is legal:
#include <iostream>
enum Color { Green = 0x1, Red = 0x2 };
enum Fruit { Banana = 0x3 };
int main() {
int v = Green|Red;
Color c = Color(v);
switch (c) {
case Banana:
std::cerr << "Banana" << std::endl;
break;
}
return 0;
}
Although this is legal here it's not hugely meaningful, but things like that are used fairly regularly and meaningfully in "bit-twiddling" C code still. The point of this example is that by allowing one int<->enum conversion anywhere it effectively means that being strict about the type of the enum later on is rendered meaningless. In the general case you can't detect if this kind of conversion has happened (it might have been in a different translation unit).
enum class is by far the nicest way of introducing such strictness cleanly without adverse effects on existing code.
In C++11 (the new C++ standard) you can use enum class to create a strongly typed enum. See the Wikipedia article on C++11.
Example:
enum class Color { Green };
enum class Fruit { Banana };
// Put your main here - it would now fail
I have a enum type:
enum class MyEnumType { A , B , C };
and i want to map these enums to description attributes; i like this approach a lot:
template <typename T>
struct MyEnumTypeDescription
{
inline const char* get() { static_assert("description not implemented for this type"); };
};
template<>
const char* MyEnumTypeDescription<MyEnumType::A>::get() { return "A"; }
template<>
const char* MyEnumTypeDescription<MyEnumType::B>::get() { return "B"; }
....
a bit verbose but not that bad, right?
Now, the part that is cumbersome is when i want to get a description from a enumerator at run-time, it means i need to create a big-switch function
const char* getDescriptionFromEnumerator( MyEnumType t )
{
case MyEnumType::A:
return MyEnumTypeDescription<MyEnumType::A>::get();
.....
}
is there some metaprogramming (template or macro) magic that would help me avoid all this boilerplate and error-prone coding?
I would suggest to map it to an array:
enum MyEnumType { A , B , C };
const char *pEnumDescription[] = { "A", "B", "C" };
And based on index you can get the type at runtime.
const char* getDescriptionFromEnumerator(MyEnumType t)
{
return pEnumDescription[t]; // just one statement instead of switch/case
}
I have this "better" enum class that
cannot contain invalid values, and
cannot be used until enum value is not set explicitly,
as follows:
class Symmetry
{
public:
enum Type {
GENERAL, SYMMETRIC, HERMITIAN,
SKEW_SYMMETRIC, SKEW_HERMITIAN, UNINITIALIZED
};
Symmetry() { t_ = UNINITIALIZED }
explicit Symmetry(Type t) : t_(t) { checkArg(t); }
Symmetry& operator=(Type t) { checkArg(t); t_ = t; return *this; }
operator Type() const {
if (t_ == UNINITIALIZED) throw runtime_error("error");
return t_;
}
private:
Type t_;
void checkArg(Type t) {
if ((unsigned)t >= (unsigned)UNINITIALIZED)
throw runtime_error("error");
}
};
This allows me to write the following code:
Symmetry s1(Symmetry::SYMMETRIC);
Symmetry s2;
s2 = Symmetry::HERMITIAN;
Symmetry s3;
if (Symmetry::GENERAL == s3) // throws
My problem is that a compiler allows constructs such as:
Symmetry s1((Symmetry::Type)18); // throws
Symmetry s2;
s2 = (Symmetry::Type)18; // throws
I solved this problem by throwing exceptions, but I would prefer such a code not to compile at all (a compile time error). Is there a way how to manage this?
Potentially a crummy solution, but it would solve your immediate problem. Rather than having an inner enum type, define a little helper class with a private constructor, and make the outer class a friend. Then the "enum" values can be static const members in your outer class. Something like this:
(DISCLAIMER: untested, so there may be various compilation issues, but you should get the idea)
class Symmetry
{
public:
class Type
{
private:
Type() {};
friend class Symmetry;
};
static const Type GENERAL;
static const Type SYMMETRIC;
static const Type HERMITIAN;
};
You will need some way of determining equality, but this should be fairly easy.
My attempt using templates: (tested. However, this can be further improved!)
template<int N>
struct Symmetry
{
enum Type
{
GENERAL, SYMMETRIC, HERMITIAN,
SKEW_SYMMETRIC, SKEW_HERMITIAN
};
template<Type e> struct allowed;
template<> struct allowed<GENERAL> { static const int value = GENERAL; };
template<> struct allowed<SYMMETRIC> { static const int value = SYMMETRIC; };
template<> struct allowed<HERMITIAN> { static const int value = HERMITIAN; };
template<> struct allowed<SKEW_SYMMETRIC> { static const int value = SKEW_SYMMETRIC; };
template<> struct allowed<SKEW_HERMITIAN> { static const int value = SKEW_HERMITIAN; };
allowed<(Type)N> m_allowed;
operator int()
{
return N;
}
};
Symmetry<0> e0; //okay
Symmetry<1> e1; //okay
Symmetry<100> e4; //compilation error!
Symmetry<e0.SKEW_HERMITIAN> e3; //okay
Symmetry<e0.SKEW_SYMMETRIC> e3; //okay
Usage:
int main()
{
Symmetry<0> e0;
Symmetry<e0.HERMITIAN> e1;
switch (e1)
{
case e0.HERMITIAN:
{
cout << "It's working" << endl;
}
break;
}
}
No. If you allow any cast to be used, as your last example does, then there will always be some cast that can be used to subvert your type.
The solution is to not be in the habit of using these casts and to very suspiciously consider any code that uses these casts indiscriminately. View this type of casting as the nuclear bomb in your arsenal: it's important to have, but you always handle it with care and never want to deploy it more than rarely.
What warning options does your compiler have for casting? What lint tools are you using which may detect this misuse of casts?
That said, it appears you really want to hide the inner Type so users are less tempted to even use it. Realizing that, it's straight-forward to make that type name private, even while not preventing all cast misuse, by slightly tweaking your original:
struct Symmetry {
enum {
UNINITIALIZED,
GENERAL, SYMMETRIC, HERMITIAN,
SKEW_SYMMETRIC, SKEW_HERMITIAN
};
private:
typedef decltype(UNINITIALIZED) Hidden;
Hidden _value;
public:
Symmetry(Hidden value = UNINITIALIZED) : _value (value) {}
Symmetry& operator=(Hidden value) { _value = value; return *this; }
operator Hidden() const {
if (_value == UNINITIALIZED) {
throw std::logic_error("uninitialized Symmetry");
}
return _value;
}
bool initialized() const { return _value != UNINITIALIZED; }
// required if you want to check for UNINITIALIZED without throwing in
// the above conversion
};
This is a complete implementation, no details omitted or unknown, or issues with initialization order. The only caveat is decltype – with a pre-C++0x compiler, you'll have to use something implementation-specific or a library which wraps something implementation-specific.
And a smaller issue: change from runtime_error to logic_error, as using uninitialized values should be preventable beforehand and thus falls in the latter category.
So, a simple question really, illustrated by the example below. When you compile this, the compiler appropriately(?) reports a warning (that we are comparing barfoo<int>::bar with barfoo<foo>::bar), now given bar is an enum - can I safely ignore this warning?
#include <iostream>
using namespace std;
struct foo
{
};
template <typename bob = int>
struct barfoo
{
enum bar { ONE, TWO, THREE };
bar action() const { return TWO; }
};
template <barfoo<>::bar eAction = barfoo<>::ONE>
struct IsAction
{
template <typename bf>
static bool check(bf const& cState)
{
return cState.action() == eAction;
}
};
int main(void)
{
barfoo<foo> c;
cout << IsAction<>::check(c) << endl;
return 0;
}
Given I'm a stickler for removing warning messages, is there a way to satisfy the compiler without moving the enum outside?
The numeric representation of the enums will be the same, so it's safe to compare them directly (or even cast between them, although you may need to go through int to satisfy the compiler). If you want to silence the warning, one approach would be to cast them both to ints before doing the comparison: (int)cState.action == (int)eAction. You might be able to add a templated operator== for the enum to do this automatically - not sure on this point, though.
Alternately, depending on how you define "without moving the enum outside", you could derive from a non-templated base class that serves to hold the enum's definition, as in http://codepad.org/8bVlcas3
I would move it outside but to a base-class:
struct barenum
{
enum bar { ONE, TWO, THREE };
protected: // because we are going to derive from it without a virtual destructor
~barenum() {}
};
template <typename bob = int>
struct barfoo : barenum
{
bar action() const { return TWO; }
};
does moving the enum into a parent of barfoo count?
#include <iostream>
using namespace std;
struct foo
{
};
struct barfoobase
{
enum bar { ONE, TWO, THREE };
};
template <typename bob = int>
struct barfoo : public barfoobase
{
bar action() const { return TWO; }
};
template <barfoobase::bar eAction = barfoobase::ONE>
struct IsAction
{
template <typename bf>
static bool check(bf const& cState)
{
return cState.action() == eAction;
}
};
int main(void)
{
barfoo<foo> c;
cout << IsAction<>::check(c) << endl;
return 0;
}
edit:
Oops, that answer has already been posted...