October 01, 2001
A Tour of Managed C++Though C# is the preferred language for .NET development, Microsoft hasn't leftThough C# is the preferred language for .NET development, Microsoft hasn't left C++ coders out of it's new Web services framework. If you want to support existing C++ applications, take along our guide to managed extensions to the language. Part 1 of 2.
Though you wouldn't know it from the hype that initially surged around the C# (C sharp) programming language, the recently released beta 2 of Microsoft's Visual Studio .NET (dot-NET) continues to support traditional C++ development. It also adds managed extensions to C++ to enable execution in the Common Language Runtime (CLR), which is the .NET execution engine. This version of C++ is called "managed C++" because the CLR is a managed environment. Just what are these extensions? Assuming that you have some basic familiarity with .NET, such as knowledge of meta-data and attributed programming (see Jim Farley's "Picking a Winner: .NET vs. J2EE," Mar. 2001, or Bertrand Meyer's "The Significance of dot-NET," Nov. 2000), you're ready to explore managed C++. But first, let's discuss why you might choose managed C++ instead of C# or Visual Basic. The principle reason is to support existing applications written in C++. The .NET runtime can execute unmanaged code, so these applications can be brought into the framework with minimal effort. Also, you can write a wrapper around unmanaged code to allow its invocation from programs targeted for the .NET environment. Or you can go the opposite route, calling .NET classes from unmanaged C++ with some of the managed extensions. All this means that you can easily mix unmanaged and managed code.
Enabling Managed Extensions
#using <mscorlib.dll> The first line imports the type information from the core library (similar to an #import statement in C++) and the second makes types within the System namespace visible. The next step is to specify that the compiler should generate a .NET executable (intermediate language byte codes instead of native instructions). This is done by passing the /CLR command-line option to the compiler. Another way to cause compilation to target the CLR is to start a new "Managed C++ Application" project in the Visual Studio 7 IDE within the "Visual C++ Projects" project type. This automatically adds /CLR to the compiler options.
Managed Types A managed class:
Implicit Managed Types A literal string, such as S"Hello, World", uses the capital S to specify it is of type String *, the .NET string type. Two exact same String * literals created in different parts of the source code will point to the same instance of the string. Anywhere that the program expects a String *, the managed string literal (S-prefixed) and wide-character string literal (L-prefixed) are interchangeable. However, when the code expects a C++ string type, you can't use managed string literals. Managed arrays are marked with the __gc keyword (described below) and are allocated on the CLR runtime heap. There are five important properties to be aware of:
Multidimensional managed arrays can also be created, but the syntax is slightly different from C++. Here's an example of a one-dimensional managed integer array and a multidimensional managed character array:
int intArray __gc[];
char charArray __gc[,];
intArray = new int __gc[10];
for(int i=0; i<10; i++) {
intArray[i] = i;
}
charArray = new char __gc[5,10];
for(int y=0; y<5; y++) {
for(int x=0; x<10; x++) {
charArray[y,x] = 'A';
}
}
Using Assemblies and NamespacesAssemblies are the physical units in .NET containing types (classes, interfaces, etc.). The #using statement makes types inside an assembly accessible. Next, the using statement must be used to make types within a namespace visible. An example follows, showing "Hello World" in a message box. #using <mscorlib.dll>
#using "System.Windows.Forms.dll"
using namespace System::Windows::Forms;
int main(void)
{
MessageBox::Show("Hello World");
return 0;
}
Note that the scope resolution operator (::)
is used instead of the dot operator when using the Windows::Forms
namespace, and for accessing the Show function from the MessageBox
class.
AttributesJust as in C#, managed classes and other elements in C++ have associated meta-data. Attributes are additional declarative information stored within the meta-data. Attributes are used just as they are in C#, so I'll describe creating custom attributes in managed C++, followed by an example briefly illustrating how to use attributes in general. The base attribute class is used with a class or struct to designate it a custom attribute. There are three parameters to this attribute, each having a default value. The first parameter is positional, and the last two are named. They're listed in Table 1. There are 15 possibilities for the AllowOn parameter (from the AttributeTargets enumeration): Assembly, Module, Class, Struct, Enum, Constructor, Method, Property, Field, Event, Interface, Parameter, Delegate, ReturnValue and All. Several targets can be bitwise-OR'ed to make an attribute applicable to more than one target. When using an attribute, the parameters passed to it must all be compile-time constants, and must be one of the following types:
[attribute(Class | Struct | Interface, AllowMultiple=true)]
__gc class ClassModifiedAttribute {
public:
ClassModifiedAttribute(String *modifiedBy, String *modifiedOn)
{
m_modifiedBy = modifiedBy;
m_modifiedOn = modifiedOn;
}
private:
String *m_modifiedBy;
String *m_modifiedOn;
};
Example usage
[ClassModified(S"jscanlon", S"7/18/2001")]
__gc class MyTestClass {
public:
void showMsg() { Console::WriteLine("Msg"); }
};
The __typeof keyword can be used to return a
value of System::Type *. For example, if a custom
attribute has a constructor that includes System::Type
* as a parameter, the following usage of the attribute could be used
to pass in a type.
[MyCustomAttribute(__typeof(CSomeClass))]
To set assembly level or module level attributes, use one of the following
syntaxes:
[assembly:attribute][module:attribute] Garbage Collection Most managed environments offer a garbage collection (GC) feature. Garbage collection eases development by enabling the environment to keep track of resources, such as objects, and automatically release the resources when they're no longer needed. Managed C++ supplies two keywords to control which objects can be garbage collected: __gc and __nogc. Certain restrictions apply to classes, interfaces and structures that are marked as managed by using the __gc keyword. These are:
__gc class CScoreResults {
public:
float score;
};
void process(CScoreResults *sr)
{
CScoreResults __pin *pSR = sr;
Console::WriteLine("Score is: {0}", __box(pSR->score));
}
InterfacesInterfaces in managed C++ are similar to both traditional abstract base classes and interfaces in C#. The rules for interfaces in managed C++ are most similar to interfaces in C#. Specifically, managed interfaces:
__gc __interface IShape {
void Draw();
float getArea();
};
When a class inherits from two base interfaces and the name of a function overlaps,
the developer can explicitly specify which interface's function he's supplying.
For example:
__gc __interface IBrickStore {
void placeOrder();
};
__gc __interface IOnlineStore {
void placeOrder();
};
__gc struct CBookStore: public IBrickStore, public IOnlineStore {
void IBrickStore::placeOrder() {
Console::WriteLine("placeOrder, IBrickStore override");
}
void IOnlineStore::placeOrder() {
Console::WriteLine("placeOrder, IOnlineStore override");
}
};
In order to invoke an implementation specific to one of the interfaces, an
object pointing to CBookStore must first be cast
to the specific interface needed. Attempting to call placeOrder
through the CBookStore object causes a compiler
error stating that the function call is ambiguous. This happens even if one
version of placeOrder is not explicitly associated
with an interface.
Also, when creating a new instance of a managed class, CBookStore,
for example, you must use the new operator. The class can't be allocated on
the stack, as is permitted with normal C++ classes. This is consistent with
the fact that a managed type is actually a pointer to the object allocated in
the CLR heap.
The following illustrates correct instantiation of a managed class and casting
a managed class to its interface:
CBookStore bookStoreBad; // WILL NOT COMPILE CBookStore *bookStore = new CBookStore(); IOnlineStore *is = static_cast<IOnlineStore *>(bookStore); bookStore->placeOrder(); // THIS WILL NOT WORK: // AMBIGUOUS is->placeOrder(); // THIS WORKS Next month: Our tour of managed C++ continues with classes and structures, delegates, events, exceptions, properties and pragmas. Stay tuned for more information about Microsoft .Net's support for existing C++ applications.
| ||||||||||||||||||||||