Click here to Skip to main content
Licence BSD
First Posted 17 Apr 2003
Views 146,119
Downloads 825
Bookmarked 31 times

Making Managed Extensions for C++ STL friendly

By Nemanja Trifunovic | 17 Apr 2003
Some code to help you combine MC++ and STL
1 vote, 5.3%
1
1 vote, 5.3%
2

3
2 votes, 10.5%
4
15 votes, 78.9%
5
4.89/5 - 19 votes
2 removed
μ 4.52, σa 1.97 [?]

Introduction

Choosing the right programming language for .NET is mostly a matter of personal preference and the problem that needs to be solved. For me, one of the most important reasons to use managed extensions for C++ (MC++) is the possibility to reuse "native" C++ code, especially C++ Standard Library.

In the October 2002 edition of C/C++ User Journal, there is an article by Jonathan Caves, that describes some issues with using C++ Standard Library with managed types. My aim is to go one step further and offer some code that will help developers to easily integrate managed types and STL. This code is contained in a single header file gcstl.h.

To use the code I provided here, you will need working knowledge of Managed Extensions for C++ and STL. That assumes that, at least you have read Managed Extensions for C++ Specification and some introductionary STL text. To really understand what is going on under the hood, you will need to be familiar with STL internals, and to have a good understanding of CLR and .NET Framework.

What is the problem?

To illustrate the problems with working with managed types and STL, try to compile the next piece of code:

#using <mscorlib.dll>
#include <vector>
#include <algorithm>
#include <iterator>
#include <iostream>

using namespace System;
using namespace std;

int main()
{
    vector <String __gc*> v; //Problem 1
    v.push_back(S"bca");
    v.push_back(S"bac");
    v.push_back(S"abc");

    sort (v.begin(), v.end()); //Problem 2

    copy (v.begin(), v.end(), ostream_iterator<String __gc*>(wcout, L" ")); 
    //Problem 3
}

Also, it would be nice if we could use STL algorithms with .NET collections. Something like:

String __gc* st_array[] = {S"bca", S"bac", S"abc"};
std::find(st_array[0], st_array[3], S"abc"); //Problem 4

The code above has following problems:

  1. Unmanaged classes cannot contain __gc pointers.
  2. Built in comparison operators can not be used to sort managed objects.
  3. C++ output streams don't work with managed types, and therefore we can not use ostream_iterator.
  4. Standard algorithms don't work with .NET collections.

Let's discuss each of those problems separately.

Problem 1: Storing __gc Pointers in STL Containers

As explained in Managed Extensions for C++ Specification, chapter 16.3 "It is illegal to declare a member of an unmanaged class to have __gc pointer type.". However, there is the class System::Runtime::InteropServices::GCHandle that enables to reference a managed object from unmanaged heap. Even better, good people from VC.NET team have made a wrapper template gcroot to make working with GCHandle easy. Just include vcclr.h and use it like this:

#using <mscorlib.dll>
#include <vector>
#include <algorithm>
#include <iterator>
#include <iostream>
#include <vcclr.h>

using namespace System;
using namespace std;

int main()
{
    //vector <String __gc*> v; //Problem 1
    vector <gcroot<String __gc*> > v;

    v.push_back(S"bca");
    v.push_back(S"bac");
    v.push_back(S"abc");

    /*
    sort (v.begin(), v.end()); //Problem 2

    copy (v.begin(), v.end(), ostream_iterator<String __gc*>(wcout, S" ")); 
    //Problem 3
    */
}

To find out about internals of gcroot, take a look at the header file gcroot.h.

Problem 2: Comparisons of Managed Objects

To work with STL algorithms, as well as with sorted containers, you must be able to compare the values of your objects. STL heavily relies on value semantics, and it is generally recommended to avoid pointers (__gc as well as __nogc) in general, but it is possible to work easily with managed objects by using some function objects, that I provide in the header file gcstl.h:

#using <mscorlib.dll>
#include <vector>
#include <algorithm>
#include <iterator>
#include <iostream>
#include "gcstl.h"

using namespace System;
using namespace std;

int main()
    {
    //vector <String __gc*> v; //Problem 1
    vector <gcroot<String __gc*> > v;

    v.push_back(S"bca");
    v.push_back(S"bac");
    v.push_back(S"abc");

    
    //sort (v.begin(), v.end()); //Problem 2
    sort (v.begin(), v.end(), gc_less<String __gc*>());

    /*
    copy (v.begin(), v.end(), ostream_iterator<String __gc*>(wcout, S" ")); 
    //Problem 3
    */
}

Note that, we now use the version of sort, which uses a user-defined predicate function object that defines the comparison criterion. Most STL algorithms have versions that enable users to provide comparison predicates.

In the header file gcstl.h, I define comparison function objects that work with __gc pointers and correspond to the ones defined in the standard header <functional>. Be sure to use those function objects with algorithms, as well as with sorted containers like set and map:

set<gcroot<String __gc*>, gc_less<String __gc*>()> my_set;

rather than:

set<gcroot<String __gc*> > my_set;

Actually, both versions will compile, but in the later case ordering will be meaningless.

Function objects defined in gcstl.h are listed in the following table:

std version gc version Implemented in terms of
equal_to gc_equal_to System::Object::Equals
not_equal_to gc_not_equal_to System::Object::Equals
less gc_less System::IComparable::CompareTo
greater gc_greater System::IComparable::CompareTo
less_equal gc_less_equal System::IComparable::CompareTo
greater_equal gc_greater_equal System::IComparable::CompareTo

From the table, you can see that for a managed type to work with less, greater, less_equal and greater_equal, it must implement IComparable interface.

Problem 3: Replacement for ostream_iterator

I often use ostring_stream to dump the content of my STL containers either to screen or to a file. Therefore, I thought it would be nice to have an output iterator with similar semantics, that will internally work with .NET System::IO::TextWriter. So I made textwriter_iterator. Here it is:

#include <vector>
#include <algorithm>
#include <iterator>
#include "gcstl.h"

using namespace System;
using namespace std;

int main()
{
    //vector <String __gc*> v; //Problem 1
    vector <gcroot<String __gc*> > v;

    v.push_back(S"bca");
    v.push_back(S"bac");
    v.push_back(S"abc");
    
    //sort (v.begin(), v.end()); //Problem 2
    sort (v.begin(), v.end(), gc_less<String __gc*>());
    
    //copy (v.begin(), v.end(), 
    //ostream_iterator<String __gc*>(wcout, S" ")); 
    //Problem 3
    copy (v.begin(), v.end(), textwriter_iterator<String __gc*>
                                                (Console::Out, S" "));    
}

Internally, textwriter_iterator uses Object::ToString() to write an object to a text writer.

Notice that I didn't provide a managed counterpart for istream_operator. The reason for that is that, in my C++ programming I have not find this operator that useful, and since the implementation would not be trivial, I decided to skip it.

Problem 4: Using STL algorithms with .NET collections

To make STL algorithms work with .NET collections, I created a template wrapper gc_collection. Here is an example how to work with this wrapper:

#include <algorithm>
#include "gcstl.h"

using namespace System;
using namespace std;


int main()
    {
    String __gc* st_array[] = {S"bca", S"bac", S"abc"};
    gc_collection<Array __gc*, String __gc*> cont(st_array);
    gc_collection<Array __gc*, String __gc*>::const_iterator it =
        find_if(cont.begin(), cont.end(),
            bind2nd(gc_equal_to<gcroot<String __gc*> >(),S"abc"));
    if (it != cont.end())
        Console::WriteLine(*it);
}

gc_collection has two template parameters: Container (by default it is System::Collections::ICollection __gc*) - the type of the wrapped container; Elem (by default System::Object __gc*) - the type of contained elements.

I designed gc_collection to cover a broad range of .NET collections, rather than to have many features. Therefore, it was implemented in terms of ICollection interface, and not IList or IDictionary. I plan to make wrappers for these interfaces in the next release of gcstl.h.

gc_collection has the following member functions:

gc_collection (Container container) Constructor
const_iterator begin() The first element
const_iterator end() Past-the-end element
int size() const Number of the elements
bool empty() const Is the collection empty?
operator Container() const Conversion to the wrapped container type
Container operator->() const Enables to use gc_collection as a smart pointer

gc_collection::const_iterator is not even a forward immutable iterator - it lacks post-increment operator. It is implemented in terms of IEnumerator interface.

Minimalist as is, gc_collection gives us possibility to work with most non-mutating STL algorithms.

References

  1. Managed Extensions for C++ Specification
  2. Jonathan Caves: Using the C++ Standard Library with Managed Types - C/C++ Users Journal, October 2002.

License

This article, along with any associated source code and files, is licensed under The BSD License

About the Author

Nemanja Trifunovic

Software Developer

United States United States

Member
Born in Kragujevac, Serbia, and lived there until 2000. Spent four wonderful years in San Diego, California, one year in Boston, one in New York and now lives again in Boston area with his wife and two little daughters.
 
Wrote his first program in 1984, became a professional developer after he graduated in 1994. After all these years still very passionate about programming and software development in general.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
Generalefficiency question PinmemberGammill19:44 17 Oct '04  
GeneralRe: efficiency question PinmemberNemanja Trifunovic3:49 18 Oct '04  
GeneralRe: efficiency question Pinmemberjuno7120:43 24 Mar '05  
GeneralCan't compile in Form1.h PinmemberGammill19:37 17 Oct '04  
I am working through a Managed C++ book, doing it's
exercies.   And tried to us STL vector today for the
first time.   (And on failure, hunted up this article.)
 
I can get your code to work in a console app.   I
can get it to work if a local vector in my Form
if in the cpp file.   But -- it will not compile
if I define your vector in the Form.h.   Used your
provided includes, your gcstl.h code and the following
declarations:
 
tried
         vector < gcroot<String __gc*> > stringVec;   // failed
 
tried
         typedef vector < gcroot<String __gc*> > stringVecType;
 
with just the typedef above, compiled; but when added the
following failed again.
 
         stringVecType stringVec;
 
Error is long, so have not included it all; but here is some
of it:
 
Form1.cpp
k:\V7Net\NetLearning\ManagedCPP_Ch11\MaintainSTLCoords\Form1.h(64) : error C3633: cannot define 'stringVec' as a member of managed 'MaintainSTLCoords::Form1'
            because of the presence of default constructor 'std::vector<_Ty>::__ctor' on class 'std::vector<_Ty>'
            with
            [
                  _Ty=gcroot<System::String __gc *>
            ]
            and
            [
                  _Ty=gcroot<System::String __gc *>
            ]
            and
            [
                  _Ty=gcroot<System::String __gc *>
            ]
            C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\include\vector(297) : see declaration of 'std::vector<_Ty>::__ctor'
            with.....
 
Hope you can offer me some help.
(I use map a lot too -- read the earlier posts on that; but
need to get by this before I can move on to maps.)

 
WedgeSoft
GeneralRe: Can't compile in Form1.h PinmemberNemanja Trifunovic3:52 18 Oct '04  
Generalthis is great PinmemberJubjub14:55 29 Jun '04  
GeneralRe: this is great PinmemberNemanja Trifunovic3:13 30 Jun '04  
GeneralPassing vector from MC++ byvalue and byref PinmemberSabir2:30 28 Jun '04  
GeneralRe: Passing vector from MC++ byvalue and byref PinmemberNemanja Trifunovic4:32 29 Jun '04  
GeneralRe: Passing vector from MC++ byvalue and byref PinmemberSabir1:01 30 Jun '04  
GeneralRe: Passing vector from MC++ byvalue and byref PinmemberNemanja Trifunovic3:19 30 Jun '04  
GeneralRe: Passing vector from MC++ byvalue and byref PinmemberSabir20:31 30 Jun '04  
GeneralRe: Passing vector from MC++ byvalue and byref PinmemberNemanja Trifunovic3:35 1 Jul '04  
GeneralRe: Passing vector from MC++ byvalue and byref PinmemberSabir19:43 1 Jul '04  
GeneralRe: Passing vector from MC++ byvalue and byref PinmemberSabir20:36 30 Jun '04  
Generalmanaged strings PinsussChris LaMorticella4:50 16 Apr '04  
GeneralRe: managed strings PinmemberNemanja Trifunovic5:56 16 Apr '04  
GeneralSTL map with managed extensions Pinmemberbollwerj3:29 15 Jan '04  
GeneralRe: STL map with managed extensions PinmemberNemanja Trifunovic6:23 15 Jan '04  
GeneralRe: STL map with managed extensions Pinmemberbollwerj7:25 15 Jan '04  
GeneralRe: STL map with managed extensions Pinmemberbollwerj8:11 15 Jan '04  
GeneralRe: STL map with managed extensions PinmemberNemanja Trifunovic10:01 15 Jan '04  
GeneralRe: STL map with managed extensions Pinmemberbollwerj4:15 16 Jan '04  
GeneralRe: STL map with managed extensions Pinmemberbollwerj10:40 20 Jan '04  
GeneralRe: STL map with managed extensions PinmemberNemanja Trifunovic13:16 20 Jan '04  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web04 | 2.5.120210.1 | Last Updated 18 Apr 2003
Article Copyright 2003 by Nemanja Trifunovic
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid