2
votes

I am wrapping a C library using C++/CLI so that the C library can be used easily from C# in a C#'ish way.

Some of the functions in this library are for putting a value into a container. There are no generics in C so there exists a function per type CLIB_SetBool(BOOL value), CLIB_SetInt(int value), CLIB_SetString(char* string) and so on.

To make it easier to use from C#, I have created a single Set function which takes a System::Object.

I have two related questions:

  1. With my method how would you use a switch statement on the type of System::Object to call the correct CLIB_Setxxxx function. [typeid is only for unmanaged code and I can't seem to use GetType.]

  2. Is there a better way to wrap these functions like using a Generic? [I started using template specialisation but then realised that C# doesn't see templates.]

Thanks.

EDIT

How do I implement void Set(System::Object^ value) ?

I want to do something like:

void MyWrapper::Set(System::Object^ value)
{
 switch(typeid(value))
  {
   case System::Int32:  CLIB_SetInt(MyMarshal<clib_int>(value));    break;
   case System::Double: CLIB_SetInt(MyMarshal<clib_double>(value)); break;
//and so on
}
}

The typeid fails for managed code and clearly the cases are invalid. How to fix?

3
Won't a simple overload do ?Alexandre C.
I agree with Alexandre; the safest and most transparent way to do this is to simply write one wrapper per function call as a set of overloads.Jon Cage
From C# I will want to put a System::Object (I'm using a devexpress container) so one of my overloads will be Set(System::Object^ value) and it is this function that I don't know how (syntax) to implement. +1 to Alexandre CT33C
Agree three. It's also quite a bit faster.Hans Passant

3 Answers

2
votes

You could do worse than utilize a dictionary of delegates, keyed by type (Sorry for C# syntax, it's been a while since I did C++/CLI). Within each delegate, of course, you make your actual call to the appropriate C function.

public delegate void CLIBDelegate(object);


// handlers for types that will call the appropriate CLIB_XXX function:
public void CLIB_int(object _obj);
public void CLIB_string(object _obj);
// etc...


// Our dictionary:
Dictionary<System.Type, CLIBDelegate> dict = new Dictionary<System.Type, CLIBDelegate>();
dict[typeof(System.Int32)] = CLIB_int;
dict[typeof(System.string] = CLIB_string;
// etc...


// your set function
void Set(Object _object)
{
    if(dict.ContainsKey(_object.GetType())
        dict[_object.GetType()](_object);
} // eo Set
2
votes

What about some overloads as follows..

ref class CLIB_Wrapper
{
    static void Set(System::Boolean^ value) { CLIB_SetBool(*value); }
    static void Set(System::Int32^ value) { CLIB_SetInt(*value); }
    static void Set(System::String^ value) { CLIB_SetString(*value); }

    static void Set(System::Object^ value)
    {
        System::Type^ type = value->GetType();
        if( type == System::Boolean::typeid )
        {
            Set((System::Boolean^)value);
        }
        else if( type == System::Int32::typeid )
        {
            Set((System::Int32^)value);
        }
        else if( type == System::String::typeid )
        {
            Set((System::String^)value);
        }
    }
};

Obviously you'll need some conversions in there with appropriate marshalling for the String -> char* conversion, but you get the idea.

Edited to include detection of type

1
votes

I can't use a switch because there doesn't seem to be an integral representation of type for managed code as there is in C++.

The following works.

void MyWrapper::Set(System::Object^ value)
{
  if (value->GetType() == System::Int32::typeid)
  {
      CLIB_SetInt(MyMarshal<clib_int>(value));
  }  
  else if ((value->GetType() == System::Double::typeid)
  {
      CLIB_SetDouble(MyMarshal<clib_double>(value));
  }
//etc
}