Differences between C# and C++/CLI, part 2

One question that comes up from time to time in the C++ newsgroups is that functions or constants that exist in a C++/CLI class library are not accessible in a C# or VB.NET project.

The reason for this is nearly always that that constants or functions are defined outside of a class scope.

Consider the following – perfectly valid – C++/CLI:

namespace Bla

{

  const int CONST = 42;

  int func(void)

  {

    return CONST;

  }

};

You can use CONST and func in other C++/CLI code.

What you can’t do is to use those symbols in other .NET languages. The reason is that those languages do not have a concept of things existing outside of a class or struct scope.

In other words, even though the CLR supports it, languages other than C++/CLI do not support free standing functions, variables and constants.

A simple workaround is to put them in a dummy class. For example, you could put all constants in a class called ‘Consts’, and functions in a class called ‘Functions’. 

Differences between C# and C++/CLI, part 1

Learning C# if you already know C++ is relatively easy. The syntax is similar, and the semantics are definitely a lot easier to understand.

Since C# looks like C++, most of the concepts are easy to learn, but here and there are a few details that might cause problems later on.

On of these things is the difference between the meaning of classes and structures in C# and C++.

C++

With C++, a distinction needs to be made between native C++ and C++/CLI

Native C++

Classes and structures are virtually identical in native C++. Instances can be located on the stack or on the heap. It all depends on how they are instantiated.

The only difference between the two is that in a class, the default access of a class member is private, while the default access of a structure member is public.

That’s all there is to it.

C++/CLI

With the rise of C++/CLI, the compiler needs to know

a)      whether a type is managed or unmanaged, and

b)      whether the type (if managed) is a reference or value type.

To solve this, the new keywords ‘ref’ and ‘value’ are used. The term ‘ref type’ indicated a reference type. The type is managed, and is located on the managed heap. The term ‘value type’ indicates a managed value type. It is also managed, but located on the stack

Type can be either a class or a structure. As a result, it is possible for classes to be value types, and structures to be reference types in C++.

Semantics

In C++, the way in which you instantiate – and not the declaration – of a type determines whether it lives on the heap or on the stack.

2 important consequences of type location in C++ are that stack based instances are fixed in memory during their lifetime, and that stack based instances are guaranteed to be cleaned up when the go out of scope.

In reality, the C++ compiler is cheating you. It does some things behind your back to fool you into thinking that things live on the stack, while in reality they live on the managed heap.

  • Native types are either allocated on the stack or the native heap, based on whether you use stack semantics or operator new.
  • Value types can be created on the stack, on the native heap and on the managed heap, based on whether you instantiate them using stack semantics, operator new, or operator gcnew. But still, what you see is what you get.
  • Reference types are always created on the managed heap. Even if you use stack semantics for instantiating a reference type, the compiler still allocates the object on the managed heap. The compiler then takes care to insure that the object behavior (like deterministic cleanup etc…) mimics what you would expect of a stack based object.

If you are programming C++ in a mixed mode environment (both native and managed code in the same module) it is important to be aware of these possibilities and their impact.

C#

C# has a type system that is a lot easier to grasp.

Classes are reference types. They are always created on the managed heap.

Structures are value types. They are always created on the stack. C# -like C++/CLI- also supports the concept of boxing, where a copy of the value type is saved on the managed heap.

Conclusion

C# and C++/CLI share some type traits. The cases that are identical in C++/CLI and C# are in the following table:

 

C#

C++/CLI

Value type

struct

value class

Reference type

class

ref struct

 

All other types in C++ and C++/CLI do not have a 100% equivalent in C#.

When building class libraries for use in other .NET enabled languages, you have to watch out for incompatibilities that can be caused by using C++/CLI specific types.

Fun with template classes: Manipulating LabVIEW arrays in C++, part 3

This article is a follow up of my second article on interoperability for LabVIEW arrays in C++. That article provided a method for allowing programmers to index into multi dimensional arrays like this:

But that solution was a bit clunky. It involved 4 classes which had 5 [] operators and 2 implementations of the Resize function. There was also no support for memory access via memcpy or something else.

After a bit of redesign, I now have an improved implementation.

The general idea

Functionality is now split in 2 classes. There is a CLvArrayHandlePtr class that represents the array itself, and a CLvArrayIndexer class to provide the [] operator.

You can no longer index in the array object directly. The CLvArrayHandlePtr class is now meant solely for managing the array itself. This makes the array simpler.

The indexing functionality has been put in the CLvArrayIndexer class. Since all that class does is to provide a [] operator and an accessor for the data pointer, it is a very lightweight class that is easy to maintain.

This solution also makes it easier to add extra functionality later on as separate lightweight classes that are tailored to a specific purpose, instead of having to perform all modifications in a class that includes everything and the kitchen sink.

The Revised Array Class

The CLvArrayHandlePtr class is still a template class with a specialization for 1 dimensional arrays. The general principles behind the array has not been changed, so I am not going to reiterate all explanations over here. Instead, I will only explain the changes.

template <class T, int Q=1> class CLvArrayHandlePtr

{

public:

#pragma pack(push)

#pragma pack(1)

  typedef struct

  {

    size dimSize[Q];

    T data[1];

  } t_LvArray, *p_LvArray, **h_LvArray;

#pragma pack(pop)

  h_LvArray* m_HandlePtr;

  int Resize(size DimSize[ Q]){ … }

  size_t GetSize(void){ … }

  void GetDimSizes(size_t DimSizes[ Q]){ … }

  T* GetDataPtr(void){ … }

};

template <class T> class CLvArrayHandlePtr<T, 1>

{

public:

#pragma pack(push)

#pragma pack(1)

  typedef struct

  {

    size_t dimSize;

    T data[1];

  } t_LvArray, *p_LvArray, **h_LvArray;

#pragma pack(pop)

  h_LvArray* m_HandlePtr;

  int Resize(size_t DimSize){ … }

  size_t GetSize(void){ … }

  void GetDimSizes(size_t &DimSize){ … }

  T* GetDataPtr(void){ … }

};

These classes have roughly the same methods as their previous versions. The most notable differences are:

  • They now use size_t for dimension sizes.
  • There is a function for returning the pointer to the first element in the array.
  • There is a function to retrieve the dimension sizes.

The last 2 points are necessary in order to cleanly work with the 2 new classes.

The array indexer class

The 2 new classes are used solely to allow indexing operations on the LabVIEW array. They have no other added functionality, so they are fairly simple.

template <class T, int Q=1> class CLvArrayIndexer

{

private:

  size_t m_TotalSize;

  size_t m_DimSizes[ Q];

  T *m_Data;

public:

  //create a new instance, based on an actual array.

  CLvArrayIndexer(CLvArrayHandlePtr<T, Q> LvArray)

  {

    m_TotalSize = LvArray.GetSize();

    LvArray.GetDimSizes(m_DimSizes);

    m_Data = LvArray.GetDataPtr();

  }

  //create a new instance, based on a data pointer and dimension sizes

  CLvArrayIndexer(size_t DimSizes[ Q], T* DataPtr)

  {

    for(int i=0; i<Q; i++)

      m_DimSizes[ i] = DimSizes[ i];

    m_Data = DataPtr;

  }

  //return a new indexer with 1 dimension less.

  CLvArrayIndexer<T, Q-1> operator[](size_t Index)

  {

    size_t subDimSize = 1;

    for(int i=1; i<Q; i++)

      subDimSize *= m_DimSizes[ i];

   

    return CLvArrayIndexer<T, Q-1>(

      m_DimSizes + 1,

      m_Data + Index * subDimSize);

  }

  //Get the size of the data segment that represents this subdimension

  size_t GetSize(void)

  {

    return m_TotalSize;

  }

  //Get the pointer to the first element of this subdimension

  T* GetDataPtr(void)

  {

    return m_Data;

  }

};

template <class T> class CLvArrayIndexer<T, 1>

{

private:

  size_t m_TotalSize;

  T *m_Data;

public:

  //create a new instance, based on an actual array.

  CLvArrayIndexer(CLvArrayHandlePtr<T, 1> LvArray)

  {

    m_TotalSize = LvArray.GetSize();

    m_Data = LvArray.GetDataPtr();

  }

  //create a new instance, based on a data pointer and dimension sizes

  CLvArrayIndexer(size_t DimSizes[1], T* DataPtr)

  {

    m_TotalSize = DimSizes[1];

    m_Data = DataPtr;

  }

  //return a reference to the element at the specified index.

  T& operator[](size_t Index)

  {

    return m_Data[Index];

  }

 

  //Get the size of the data segment that represents this subdimension

  size_t GetSize(void)

  {

    return m_TotalSize;

  }

  //Get the pointer to the first element of this subdimension

  T* GetDataPtr(void)

  {

    return m_Data;

  }

};

As you can see, there are 2 constructors for creating an indexer. The first takes the original array as an argument. It uses the array methods for getting the data size and data pointers.

The second constructor takes the size and pointer directly. This is used byt CLvArrayIndexer classes that create another CLvArrayIndexer class with a lower dimension.

The [] operator itself is also pretty simple. For multi dimensional indexers, the [] operator returns a new indexer that has 1 less dimension than itself. For 1 dimensional array indexers, it returns a reference to an element inside the original array.

The trick here is that each next [] operator points to a more specific location inside the original array until 1 specific element is referenced.

Using the array and indexer classes

To use the array and indexer classes in not very different from the previous solution.

Given an array with the following declaration:

CLvArrayHandlePtr<double, 3> Array;

Using it is done like this:

  size_t size[3] = {

    requestCountPages,

    requestCountRows,

    requestCountColumns};

  Array.Resize(size);

  CLvArrayIndexer<double, 3> indexer(Array);

 

  double i=0;

  for(int page=0; page<requestCountPages; page++)

    for(int row=0; row<requestCountRows; row++)

      for(int column=0; column<requestCountColumns; column++)

        indexer[page][row][column] = i++;

Using the data pointer and size of a sub segment are of course as easy as

Memcpy(Target, indexer.GetDataPtr(), indexer.GetSize());

Afterthoughts

This solution is clean, and easy to use and maintain. I am fairly happy with it.

Of course, what I am not yet happy about is the lack of error handling. For example, the indexer class does not yet check if the array is actually allocated or not. If it isn’t, then the current solution will dereference a NULL pointer.

There is also no size checking to prevent indexers from going out of bounds.

But as before, this code is proof of concept code. I am toying with the idea of implementing those error handlers, but I want to do it in a way that allows you to use either C style return value error handling or C++ style exception handling.

One possibility is to use error handling template classes. However, That will have to wait for next time.

The full code for the classes is available for download, together with the dll code and some LabVIEW vis for testing. Everything is licensed under the MIT license as usual.