简体   繁体   中英

C++ casting a floating point value to an enum - but not with VS 2010

I have some template code that works fine with Xcode 4.5 and LLVM 3.0, but fails with the VS 2010 Express C++ toolchain (v 10.0.30319.1).

I'm using a third-party API that I have no control over. It provides values to my code as black-box 'blobs' that can only be interpreted by API functions:

// API_Secret is a black-box encapsulation of a floating-point number or a boolean value.
// It is provided by a third-party API, with associated access functions.
// For all intents and purposes, it's a complete black box.
// This enum represents the internal 'type' of a secret value.
enum API_SecretTypeEnum {
  API_Number,
  API_Boolean,
};
// Other API declarations:
API_SecretTypeEnum API_GetType(const API_Secret &value);
double API_AsNumber(const API_Secret &value);
bool API_AsBoolean(const API_Secret &value);

// my code:
template <typename ValueType>
class Extractor {
 public:
  ValueType extract(API_Secret value) {
    if (API_GetType(value) == API_Number) {
      return static_cast<ValueType>(API_AsNumber(value));
    } else if (API_GetType(value) == API_Boolean) {
      return API_AsBoolean(value) ? ValueType(1) : ValueType(0);
    }
    return ValueType();
  }
};

// boolean specialization - not 100% sure it's needed though
template<>
class Extractor <bool> {
 public:
  bool extract(API_Secret value) {
    return API_AsBoolean(value);
  }
};

Then, later:

API_Secret API_GetSomeValue(int some_sort_of_handle);

// get some black-box values from the API
API_Secret secret0 = API_GetSomeValue(0);
API_Secret secret1 = API_GetSomeValue(1);
API_Secret secret2 = API_GetSomeValue(2);

// for certain external reasons we expect this to be an integer representation:
Extractor<int> e0;
int v0 = e0.extract(secret0);

// for certain external reasons we expect this to be a double representation:
Extractor<double> e1;
double v1 = e1.extract(secret1);

// for certain external reasons we expect this to be a boolean representation:
Extractor<bool> e2;
bool v2 = e2.extract(secret2);

Now for the difference between Xcode, LLVM and VS 2010. In Xcode & LLVM, the following will compile (as part of the full program):

enum MyEnum {
  Enum0,
  Enum1,
};
Extractor<MyEnum> ee;
MyEnum ve = ee.extract(secret0);

Ie the class template uses a static_cast to convert from a floating point number to an enum . This seems to work fine, and the explanation section of this page suggests this is valid:

8) Integer, floating-point , or enumeration type can be converted to any enumeration type (the result is unspecified if the value of expression, converted to the enumeration's underlying type, is not one of the target enumeration values)

However with VS2010, the following compiler error is encountered:

error C2440: 'static_cast' : cannot convert from 'double' to 'MyEnum'

  Conversions between enumeration and floating point values are no longer allowed 

And this MSDN article seems to confirm this by not mentioning floating-point types and explicitly stating 'integral' value:

The static_cast operator can explicitly convert an integral value to an enumeration type. If the value of the integral type does not fall within the range of enumeration values, the resulting enumeration value is undefined.

So it appears that there is a significant difference here between VS 2010 and the other compilers. I am wondering whether this is something that can be bypassed in VS 2010? Is it a newer feature of the language that VS 2010 simply doesn't support? However I query this as the error message says "no longer allowed", implying it's explicitly disallowed.

I know of a workaround - instead of casting to an enum (with Extractor<MyEnum> ), I can use an Extractor<int> instead, and then simply assign this to the target MyEnum variable. However this template is actually used as part of a larger system that calls wrapped functions, and in this case the wrapped function takes a MyEnum value. This prevents the template from matching properly as the ValueType parameter is actually picked up automatically from the signature of the wrapped function.

Alternatively, is it possible to write a template specialization of Extractor that matches enum types only? Then I can cast to an integral type first. Or maybe the base template could always cast to an int first, then I could write a floating-point specialization that doesn't do this - but I'm not sure how to write a template that catches all floating-point types (float, double, ...).

I'm pretty sure Visual Studio-2010 supports <type_traits> . You can use std::enable_if along with std::is_enum .

template <typename ValueType, typename Enable = void>
class Extractor {
 public:
  ValueType extract(API_Secret value);
};

template <typename ValueType>
class Extractor<ValueType, typename std::enable_if<std::is_enum<ValueType>::value>::type>
{
 ...
};


You can do the same to match floating point types using std::is_floating_point .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM