Monday, February 7, 2011

Converting std::vector<>::iterator to .NET interface in C++/CLI

I am wrapping a native C++ class, which has the following methods:

class Native
{
    public:
    class Local
    {
        std::string m_Str;
        int m_Int;
    };

    typedef std::vector<Local> LocalVec;
    typedef LocalVec::iterator LocalIter;

    LocalIter BeginLocals();
    LocalIter EndLocals();

    private:
        LocalVec m_Locals;
};

1) What is the ".NET way" of representing this same kind of interface? A single method returning an array<>? Does the array<> generic have iterators, so that I could implement BeginLocals() and EndLocals()?

2) Should Local be declared as a value struct in the .NET wrapper?

I'd really like to represent the wrapped class with a .NET flavor, but I'm very new to the managed world - and this type of information is frustrating to google for...

  • Iterators aren't exactly translatable to "the .net way", but they are roughly replaced by IEnumerable < T > and IEnumerator < T >.

    Rather than

      vector<int> a_vector;
      vector<int>::iterator a_iterator;
      for(int i= 0; i < 100; i++)
      {
        a_vector.push_back(i);
      }
    
      int total = 0;
      a_iterator = a_vector.begin();
      while( a_iterator != a_vector.end() ) {
        total += *a_iterator;
        a_iterator++;
      }
    

    you would see (in c#)

    List<int> a_list = new List<int>();
    for(int i=0; i < 100; i++)
    {
      a_list.Add(i);
    }
    int total = 0;
    foreach( int item in a_list)
    {
      total += item;
    }
    

    Or more explicitly (without hiding the IEnumerator behind the foreach syntax sugar):

    List<int> a_list = new List<int>();
    for (int i = 0; i < 100; i++)
    {
        a_list.Add(i);
    }
    int total = 0;
    IEnumerator<int> a_enumerator = a_list.GetEnumerator();
    while (a_enumerator.MoveNext())
    {
        total += a_enumerator.Current;
    }
    

    As you can see, foreach just hides the .net enumerator for you.

    So really, the ".net way" would be to simply allow people to create List< Local > items for themselves. If you do want to control iteration or make the collection a bit more custom, have your collection implement the IEnumerable< T > and/or ICollection< T > interfaces as well.

    A near direct translation to c# would be pretty much what you assumed:

    public class Native
    {
      public class Local
      { 
         public string m_str;
         public int m_int;
      }
    
      private List<Local> m_Locals = new List<Local>();
    
      public List<Local> Locals
      {
        get{ return m_Locals;}
      }
    }
    

    Then a user would be able to

    foreach( Local item in someNative.Locals)  
    {
     ... 
    }
    
  • @Phillip - Thanks, your answer really got me started in the right direction.

    After seeing your code, and doing a little more reading in Nish's book C++/CLI in Action, I think using an indexed property that returns a const tracking handle to a Local instance on the managed heap is probably the best approach. I ended up implementing something similar to the following:

    public ref class Managed
    {
        public:
        ref class Local
        {
            String^ m_Str;
            int m_Int;
        };
    
        property const Local^ Locals[int]
        {
         const Local^ get(int Index)
         {
          // error checking here...
          return m_Locals[Index];
         }
        };
    
        private:
            List<Local^> m_Locals;
    };
    
    Philip Rieck : That's a good approach, but you'll probably want to also make sure to expose a Count property at least :). One other note, without exposing the list or implementing IEnumerable< T >, the class won't be usable by the LINQ extensions in .net 3.5

0 comments:

Post a Comment