Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

VARIANT datatype of C++ into C#

What is equivalent of the VARIANT datatype of C++ in C#?

I have code in C++ which uses the VARIANT datatype. How can I convert that code in C#?

like image 595
kamal Avatar asked Apr 04 '13 08:04

kamal


3 Answers

Well, there are actually two variant's in C++: boost::variant and COM variant. The solution follows more or less the same idea, but the former is more complex. I expect you mean to use the latter.

Let me first start by telling that this is something you just shouldn't use if possible. That said, this is how you do it :-)

Variants and interop

Variants are sometimes used in interop of if you need the byte representation to be the same.

If you're dealing with interop, make sure to check out the VariantWrapper class on MSDN and make it work like that.

Variants and porting considerations

Variants are mostly used in APIs, and usually like this:

void Foo(SomeEnum operation, Variant data);

The reason it's done like this in C++ is because there is no base object class and you therefore need something like this. The easiest way to port this is to change the signature to:

void Foo(SomeEnum operation, object data);

However, if you're porting anyway, you also seriously want to consider these two, since they are resolved at compile-time and can save you the big 'switch' that usually follows in method Foo:

void SomeOperation(int data);
void SomeOperation(float data);
// etc

Variants and byte consistency

In rare cases you need to manipulate the bytes themselves.

Essentially the variant is just a big union of value types wrapped in a single value type (struct). In C++, you can allocate a value type on the heap because a struct is the same as a class (well sort-of). How the value type is being used is just a bit important but more on that later.

Union simply means you are going to overlap all the data in memory. Notice how I explicitly noted value type above; for variant's this is basically what it's all about. This also gives us a way to test it - namely by checking another value in the struct.

The way to do this in C# is to use the StructLayout attribute in a value type, which basically works as follows:

[StructLayout(LayoutKind.Explicit)]
public struct Variant
{
    [FieldOffset(0)]
    public int Integer;
    [FieldOffset(0)]
    public float Float;
    [FieldOffset(0)]
    public double Double;
    [FieldOffset(0)]
    public byte Byte;
    // etc
}

// Check if it works - shouldn't print 0.
public class VariantTest
{
    static void Main(string[] args)
    {
        Variant v = new Variant() { Integer = 2 };
        Console.WriteLine("{0}", v.Float);

        Console.ReadLine();
    }
}

C++ variant's can also be stored on the heap as I noted earlier. If you do this, you probably still want the memory signature to be the same. The way to do this is to box the Variant struct we build earlier by simply casing it to object.

like image 115
atlaste Avatar answered Oct 19 '22 10:10

atlaste


This is a tricky question.

From C# 4, you can use dynamic to indicate that the type is known at run-time.

By my personal understanding, however, c++ requires the type known at compile time. Thus you might consider to use object, but object in C# is an existent type.

For the concept of multi-type, single value (AKA polymorphism) of VARIANT, you would not need to find a corresponding type in C#, just define your classes and interfaces. You can always reference an object as its interface which the class implements.

If you are porting the code, and to figure out a syntax that you can simply use in LHS and for the considering of the type is known at compile time, then use var.

like image 1
Ken Kin Avatar answered Oct 19 '22 12:10

Ken Kin


When .NET implements a COM interface, just use VARIANT* instead.

Then bypass marshalling on the .NET receiving side by using a IntPtr type to receive the pointer.

public class ComVariant
{
    [StructLayout(LayoutKind.Sequential)]
    public struct Variant
    {
        public ushort vt;
        public ushort wReserved1;
        public ushort wReserved2;
        public ushort wReserved3;
        public Int32 data01;
        public Int32 data02;
    }

    private Variant _variant;

    private IntPtr _variantPtr;

    public ComVariant(int variantPtr) : this(new IntPtr(variantPtr))
    {
    }

    public ComVariant(IntPtr variantPtr)
    {
        _variant = (Variant)Marshal.PtrToStructure(variantPtr, typeof(Variant));
        _variantPtr = variantPtr;
    }

    public VarEnum Vt
    {
        get
        {
            return (VarEnum)_variant.vt;
        }
        set
        {
            _variant.vt = (ushort)value;
        }
    }

    public object Object
    {
        get
        {
            return Marshal.GetObjectForNativeVariant(_variantPtr);
        }
    }
}

then if you are accessing a VT_UNKNOWN pointing to a COM interface object instance, just

var variant = new ComVariant(variantPtr);
var stream = variant.Object as IStream; // will not be null if type is correct
var obj = variant.Object as IObj; // in general...

will do the trick, but pay attention not to use a newly allocated VARIANT and giving its ownership to the .NET implementation without deallocating it somewhere...

For more complex code you might read this article which also talks about memory management.

like image 1
franckspike Avatar answered Oct 19 '22 11:10

franckspike