When is foo == &foo?

The answer is “in the CMPI interface specification”.

So, CMPI defines this massive union of every possible data type, called CMPIValue.  It looks something like this (but with many more types):

union {
    uint32_t uint32;
    uint64_t uint64;
    char *chars;
    CMPIString *string;
} CMPIValue;

This is then thrown around way too much, using an enumeration called CMPIType to indicate which interpretation of CMPIValue you intend to use.  For example, to set a property of an instance, you would do something like this for an integer:

uint32_t myint = 123;
CMSetProperty(inst, “MyIntProperty”, (CMPIValue *)&myint, CMPI_uint32);

or this for a string:

const char *mystring = “We’re really asking for trouble here…”;
CMSetProperty(inst, “MyStrProperty”, (CMPIValue *)mystring, CMPI_chars);

Makes sense, right?  The above usage in the string case is, of course, the scenario intended by the interface designer, and it is (in my experience) almost exclusively used that way.  However, assuming you want to do something a little more complicated, such as write a CIMXML instance parser, you might want to pass around their CMPIValue and CMPIType objects.

So, lets say you do something like the following:

CMPIType parse_integer(char *string, CMPIValue *val) {
    val->uint32 = 123;
    return CMPI_uint32;
}

CMPIType parse_string(char *string, CMPIValue *val) {
    val->chars = strdup(“Oh, boy, here we go”Smilie: ;);
    return CMPI_chars;
}

int set_property(char *string, char *name, CMPIInstance *inst) {
    CMPIValue value;
    CMPIType type;

    if (/* value looks like an integer */)
        type = parse_integer(string, &value);
    else
        type = parse_string(string, &value);

    CMSetProperty(inst, name, &value, type);
}

That’s all fine, right?  We set the CMPIValue in our handler functions, and then we pass a pointer to our value to CMSetProperty().  Obviously nothing to see here, CMSetProperty() takes a CMPIValue *……doesn’t it?

The answer is a big fat “NO, no it doesn’t”.  If you were paying close attention before, we passed in the pointer to a CMPIValue stack variable in the integer case, but in the string case, we passed in a pointer to an actual string buffer.  Thus, if we put that string pointer in the CMPIValue itself, and then pass a pointer to it, you get a double pointer, the equivalent of a char **.  Because the type is correct, the compiler doesn’t complain, but the CIMOM explodes.

I’m really shocked and horrified that the CMPI interface includes this semantic difference in the setProperty() broker call.  To verify that I wasn’t just hallucinating on a Friday afternoon, I tracked the call down to the following bit of truly spectacular OpenPegasus code:

CIMValue value2CIMValue(const CMPIValue* data, …Smilie: ;) {
    . . .
    else if (type==CMPI_chars) {
          if (data) v.set(String((char*)data));
          else return CIMValue(CIMTYPE_STRING,false);
    }

Right there, they take the CMPIValue and cast it directly to a char * if you tell them that you’re passing in CMPI_chars!  Just to illustrate how the interpretation of the CMPIValue parameter fundamentally changes depending on the type you passed in, the following appears directly below:

   else if (type==CMPI_charsptr) {
      if (data && *(char**)data) v.set(String(*(char**)data));
      else return CIMValue(CIMTYPE_STRING,false);
   }

So, if you pass in CMPI_chars, then it expects a char*.  If you pass in CMPI_charsptr, then it expects a char ** (which would actually cast a the example above to the correct thing).  IMHO, it would have been better for them to just call it a void * and be done with it.  They think they have made it cleaner, but they’ve actually made it worse.

What a great way to end a Friday.

Category(s): Codemonkeying
Tags:

Comments are closed.