tags:

views:

26

answers:

3

I am writing a COM wrapper for a COM object that sends different types of values from a client and want to map these types in a Map to their actual C++ type, such as VT_BSTR to a wstring, etc.

I was thinking of defining an enumuration of all COM Variant types and then using a map to have that Enum as the key and the actual type containing the retrieved value, however I'm running into the issue that I cannot seem to find a global type to put in my map that I can cast to a string or double or whatever is handed to me to place in the map.

Perhaps my thinking of how to do this is entirely wrong, please advice?

I was thinking of a void pointer, however it seems the compiler doesn't like my casts:

(example)

    enum Type
    {
        VT_INTEGER=0,
        VT_DBL=1

    };


    map<Type, void*> typemap;
    typedef pair<Type, void*> m_typepair;
    typemap.insert( m_typepair(VT_INTEGER, 0));
    typemap.insert( m_typepair(VT_DBL, (double)2.5));  // it does not like this cast

    map<Type, void*>::iterator m_typeiter;

Iterating this map would probably need a switch statement inside to find the right type, I'm not sure if there is a better way?

A: 

I usually use template specialization for this kind of tasks. I have a template function that converts from a variant type to a C++ type that looks like this:

template <typename T>
T variantToCpp(const Variant&);

template <>
int variantToCpp<int>(const Variant& v)
{
  // Check that v really contains an int, if not, you can silently fail or throw an exception
  // Get and return the int
}

template <>
std::wstring variantToCpp<std::wstring>(const Variant& v)
{
  // Check that v really contains a string, if not, you can silently fail or throw an exception
  // Get and return the string
}

// etc. for each C++ type

// Usage
int i = variantToCpp<int>(someVariantIGotViaCOM);

This way you get a constant-time conversion from a Variant to a C++ type. Also note that the default templated function has no body - it will cause a linker error (on most compilers) if someone tries to use a conversion for an unspecialized type.

Similarly you can do the conversion from a C++ type to a Variant:

template <typename T>
Variant cppToVariant(T);

template <>
Variant cppToVariant<int>(int val)
{
  // Convert to variant and return it
}

// etc. for each type

// Usage:
int i = 10;
Variant var = cppToVariant(i);  // You don't even need to explicitly specify the type here, the compiler deduces it

If you insist on using a map and tons of ifs for this kind of conversion, you can use your void* pointer, you just have to initialize it with a pointer to the type:

int *myInteger = new int; *myInteger = 42;
double *myDouble = new double; *myDouble = 42;
typemap.insert( m_typepair(VT_INTEGER, myInteger));
typemap.insert( m_typepair(VT_DBL, myDouble));
// Don't forget to free them when you clear the map

If you aren't satisfied with any of the above solutions, boost::any might be worth looking at.

dark_charlie
+1  A: 

Not sure what you're trying to do, it certainly sounds wrong. A VARIANT you get from a client needs to be converted to the type that you know how to deal with. That's easy to do, just call the VariantToXxxx() function. For example, use VariantToString() if you want to get a string.

There are several C++ wrapper classes already available that make this easier. _variant_t, CComVariant, COleVariant. They all do the same thing, just different #include files. _variant_t is a good one because it doesn't tie you into either MFC or ATL. Unless you are already using them. Their ChangeType() method makes the conversion. Memory management is automatic.

Hans Passant
A: 

Are you aware of _variant_t ? You might be reinventing the wheel. It has all the relevant constrcutors and overloaded assignment. I.e. _variant_t var = 0.0 works as expected (VT_R8)

MSalters