Kenc.dk
Screenshot of Visual Studio showing a C# interface

C++ CLI/CLR

| 0 Comments | words | minutes | Permalink

Developing C++ applications, which are in the void between the .net platform and the good old trusty Win32 API is a scary place to go, if you havent been there before.. Especially because of the lack of intellisense in Visual Studio 2010 (though a promise was made it would appear "soon after release"; we are still waiting!)!However its also a void which you sometimes have to visit, to get the best of 2 worlds, bound together.Fortunately the wisy folks over at Whole Tomato Software have developed a solution named "Visual Assist X" which gives you experimental intellisense for c++ CLI/CLR projects, which should help you adapt to this new wonder!

The Project

I'm currently working on a robot project, on my 4th semester at Engineering College of Copenhagen (IHK). Basically we have to build one or more rovers, which should be able to plot an unknown maze and for a known maze, they should be able to travel the fastest (shortest) way through it. We had a talk with our teachers, regarding doing the plotting/calculations via image processing and, luckily for us, they agreed that this would be a clever way of doing it, instead of having rovers entering the maze and always keep left/right until the entire maze was plotted. For the project we found the OpenCV image processing library, which we are determined to use. The only problem with the OpenCV library, is that there aren't any official .net libraries and since we want the desktop application/server application, to be written in .net (using C#) we need to have some kind of interop. This leads us to be using C++ CLI/CLR to create a managed .net library, written using the std. C++ OpenCV library files.

Since we are still developing the solution and our project aint due until December (hand-in) / January (Examn) I can't reveal any real code for it yet, however I can do the next best thing.

Show of sample code for C++ CLI

There are 2 basic rules, as I have found out when writing C++ CLI/CLR applications, which you must obey.1. All .net classes, must be wearing the ^ suffix;2. All instances of .net classes, are made using the gcnew keyword; for instance

{{c++}}
Random^ rnd = gcnew Random();
System::Drawing::Point^ coordinates;
coordinates = gcnew System::Drawing::Point(rnd->Next(),rnd->Next());

Creating classes

Having multiple classes, is nearly inevitable when creating .net libraries, since classes are the basic idea of OOP. (Connection_class.png (10.82 kb))This is a pretty straight forward class, implementing some basic functionality.It contains its own coordinates in a SystemDrawingPoint, its type as ain ImageType (public enum) and list of other connections in a List.Lets have a look at adding this class, to our project. For starters, go and create a new header file; in VS it will be empty but there are some basic stuff which must be there in order of having it to work.

{{c++}}
// connections.h
#pragma once

/* includes */
#include "imagetype.h"
using namespace System;
using namespace System::Drawing;
using namespace System::Collections::Generic;
using namespace System::Collections::ObjectModel;
namespace ImageProcessing {
    public ref class Connection {
        public: Connection(void);
        ~Connection(void);
    };
}

Lets go and implement it; create a new .cpp file in your source folder.

{{c++}}
/* includes */
#include "stdafx.h" // <-- MUST BE THERE
#include "connection.h" // <-- load our class definition
using namespace System;
using namespace ImageProcessing;
/* IP Constructor */
Connection::Connection(void) { }
Connection::~Connection(void) { }

Now we have an empty class, we can go and play with. Lets add all the includes, attributes and operators accourding to our model.

{{c++}}
// connections.h
#pragma once
#include "imagetype.h"
using namespace System;
using namespace System::Drawing;
using namespace System::Collections::Generic;
using namespace System::Collections::ObjectModel;
namespace ImageProcessing {
    public ref class Connection {
        public:
            Connection(Point^ coordinates, ImageType^ connectionType);
            ~Connection(void); 
            void AddConnection(Connection^ aCon);
            void DelConnection(Connection^ aCon);
            void DelConnection(int x);
            Boolean Equals(Connection^ con);
            ReadOnlyCollection<Connection^>^ getConnections();
            ImageType^ getConnectionType();
            Point^ getCoordinates();
            String^ toString();
        private:
            Point^ Coordinates;
            ImageType^ ConnectionType;
            System::Collections::Generic::List<ImageProcessing::Connection^>^ Connections; 
        }; 
    }

Notice all the '^' after each .net class!

{{c++}}
/* includes */
#include "stdafx.h" // <-- MUST BE THERE
#include "connection.h" // <-- load our class definition
using namespace System;
using namespace ImageProcessing;

/* IP Constructor */
Connection::Connection(Point^ coordinates, ImageType^ connectionType)
{
    ConnectionType = connectionType;
    Coordinates = gcnew Point(coordinates->X, coordinates->Y);
    Connections = gcnew List<ImageProcessing::Connection^>();
}

Connection::~Connection(void)
{
    for each(ImageProcessing::Connection^ con in Connections)
    con->~Connection();
    Connections->Clear();
    delete Connections;
    delete Coordinates;
}

void Connection::AddConnection(Connection^ aCon)
{
    /* run through each connection to see if
     * the connection already exists. */
    for each(ImageProcessing::Connection^ con in Connections)
    {
        if(aCon->Equals(con)) return;
    }

    /*
     * the connection wasn't found.
     * Continue with adding it to the list!
     */
    Connections->Add(aCon);
}

void Connection::DelConnection(Connection^ aCon)
{
    Connections->Remove(aCon);
}

void Connection::DelConnection(int x)
{
    Connections->RemoveAt(x);
}

/* 
 * check the 2 connections on 
 * connection-type and location.
 * if they match, we 'agree' that they are identical */
Boolean Connection::Equals(Connection^ con)
{
    f((con->getCoordinates() == Coordinates) && (con->getConnectionType() == getConnectionType())) 
    return true;
    return false;
}

ReadOnlyCollection<Connection^>^ Connection::getConnections()
{
    return Connections->AsReadOnly();
}

ImageType^ Connection::getConnectionType()
{
    return ConnectionType;
}

Point^ Connection::getCoordinates()
{
    return gcnew Point(Coordinates->X, Coordinates->Y);
}

String^ Connection::toString()
{
    return "Connection: " + Coordinates->X + ", " + Coordinates->Y + " : " + Connections->Count;
}

0 comments

Add comment