Click here to Skip to main content
Email Password   helpLost your password?

Introduction

Beginning with .NET 2.0, Microsoft has introduced generics into the CLI whereby parameterized types can be declared and used. For C# and VB users, this must have been extremely exciting news; but for C++ people who were already used to templates, this might not have sounded all that interesting. You often hear C++ people ask why they need to use generics when they already have templates. [Note - In C++/CLI, templates can be used both with native and with managed types].

Generics and templates, while similar in nature, are also different in many ways. The most outstanding of these differences is that while templates are implemented by the compiler, generics are instantiated at runtime by the CLR's execution engine (this is possible because generics is directly supported by MSIL). For a detailed write-up on how templates and generics differ, see Brandon Bray's blog entry - Templates and Generics.

This article is not intended to demonstrate how generics are cooler than templates (or even the reverse for that matter), rather it just tries to expose the syntactic usage of generics in C++/CLI. I would like to state here that I found generics rather limited when compared to templates, that too despite not being a core-template guy myself.

Basic syntax

Throughout the article, I'll show some code using templates and then show the equivalent code using generics. Here's a typical native class template.

template<typename T1, typename T2> class NativeData
{
public:
  NativeData(T1 t1)
  {
    m_t1 = t1;
  }
  void DoStuff(T2 t2)
  {
    //...

  }
private:
  T1 m_t1;
};

Now, here's the equivalent in generics :-

generic<typename T1, typename T2> ref class GenericData
{
public:
  GenericData(T1 t1)
  {
    m_t1 = t1;
  }
  void DoStuff(T2 t2)
  {
    //...

  }
private:
  T1 m_t1;
};

Looks just about identical, except that instead of template, the keyword to be used is generic. [Note - generic is one of the 3 new keywords introduced in C++/CLI, the other two being gcnew and nullptr. All other keywords are context sensitive keywords like ref or spaced keywords like for each].

Constraints

See the following template-based class :-

template<typename T> class Native
{
public:
  void Start(int x)
  {
    T* t = new T();
    t->Bark(x);
    t->WagTail();
    delete t;
  }
};

Since templates use lazy constraints, the above code compiles fine (the compiler won't specialize the class template until an instantiation).

And assuming we want to use the class as follows :-

Native<NativeDog> d1;
d1.Start(100);

We need to have a class NativeDog similar to :-

class NativeDog
{
public:
  void Bark(int Loudness)
  {
    Console::WriteLine("NativeDog::Bark {0}",Loudness);
  }
  void WagTail()
  {
    Console::WriteLine("NativeDog::WagTail");
  }
};

As long as the class we are specifying as the template type parameter has the Bark and WagTail methods, it compiles fine. But in generics, we have to specify the constraint (since generics are implemented at runtime). The generic equivalent will be :-

interface class IDog
{
  void Bark(int Loudness);
  void WagTail();
};
generic<typename T> where T:IDog ref class GenRef 
{
public:
  void Start(int x)
  {
    T t = Activator::CreateInstance<T>();
    t->Bark(x);
    t->WagTail();
    delete safe_cast<Object^>(t);    
  }
};

Now that we have specified a constraint for the generic parameter, we can invoke methods on it (as allowed by the constraint). If you are wondering why I had to use the generic overload of Activator::CreateInstance, it's because the compiler won't allow you to gcnew a generic parameter. Similarly, it won't let you delete a generic parameter and hence the safe_cast to Object.

To use the class, we do something like :-

ref class Dog : IDog
{
public:
  void Bark(int Loudness)
  {
    Console::WriteLine("Dog::Bark {0}",Loudness);
  }
  void WagTail()
  {
    Console::WriteLine("Dog::WagTail");
  }
};
GenRef<Dog^> g1;
g1.Start(100);

Generic functions

C++/CLI also supports generic functions (you can have a non-generic class with a generic function or you can have a global generic function). For example :-

generic<typename T> where T:IDog void DoAll(T t)
{
  t->Bark(0);
  t->WagTail();
}

Issues with the constraint mechanism

Doing some things are rather more complicated with generics than with templates.

Example 1

See the following template code :

template<typename T> void NativeIncrement(T t, int x)
{
  t += x;
}

Now, the basic issue with doing this with generics would be that it's difficult to define a constraint that will allow the += operator to work on the generic type parameter. One possible workaround is to define an interface called IIncrementable (similar to the generic collection classes using generic interfaces like IComparable<T>)

interface class IIncrementable
{
  void Add(int);
};
generic<typename T> where T:IIncrementable void RefIncrement(T t, int x)
{
  t->Add(x);  
}

And we can use it on classes that implement IIncrementable :-

ref class MyInt : IIncrementable
{
public:
  MyInt(int n) : m_num(n){}
  void Add(int x)
  {
    m_num += x;
  }
private:
  int m_num;
};

We still cannot use the generic function RefIncrement with simple types like int, float etc. For that, we can use a work-around suggested by Jambo (discussed later in this article).

Example 2

See the following function template :-

template<typename T> T NativeAdd(T t1, T t2)
{
  return t1 + t2;
}

As above, since there is no .NET interface called IAddable, we have to work around the situation. Instead of using an interface like we did last time, we'll use a ref class as constraint - so we can define a CLI-compatible binary + operator.

ref class A1 
{
public:
  A1(int x):m_x(x){}
  static A1^ operator +(const A1^ a1,const A1^ a2)
  {
    return gcnew A1(a1->m_x + a2->m_x);
  }
  int m_x;
};
generic<typename T> where T:A1 T GenericAdd(T t1, T t2)
{
  A1^ tmp = gcnew A1(t1->m_x + t2->m_x);
  return static_cast<T>(tmp);
}

And yeah, all that ugly casting is necessary. Again this generic function will not work on simple types like int, float etc.

Work-around for simple types

Jambo calls this the Adapter Pattern [but neither of us are really sure whether that's what this should be called]. Basically we have a singleton class, NumericalAdapter and a generic interface, INumericalOperations with Add and Subtract methods. The NumericalAdapter class has inner classes, one for each type like IntNumericalAdapter, FloatNumericalAdapter etc. and also provides a static generic method GetNumericalAdapter which returns a INumericalOperations<T> object.

generic<typename T> interface class INumericalOperations
{
  T Add( T t1, T t2 );
  T Subtract( T t1, T t2 );
};
ref class NumericalAdapter
{
private:
  static NumericalAdapter^ m_NumericalAdapter;
  NumericalAdapter(){};
public:
  static NumericalAdapter()
  {
    m_NumericalAdapter = gcnew NumericalAdapter();
  }
private:
  ref class IntNumericalAdapter : INumericalOperations<int>
  {
  public:
    int Add( int t1, int t2 ) 
    { 
      return t1 + t2;
    }
    int Subtract ( int t1, int t2 ) 
    { 
      return t1 - t2;
    }
  };
  ref class FloatNumericalAdapter : INumericalOperations<float>
  {
  public:
    float Add( float t1, float t2 ) 
    { 
      return t1 + t2;
    }
    float Subtract ( float t1, float t2 ) 
    { 
      return t1 - t2;
    }
  };
public:
  generic<typename T> static INumericalOperations<T>^ GetNumericalAdapter()
  {
    Type^ typ = T::typeid;
    if( typ == int::typeid)
    {
      return dynamic_cast<INumericalOperations<T>^>(gcnew IntNumericalAdapter());
    }
    if( typ == float::typeid)
    {
      return dynamic_cast<INumericalOperations<T>^>(gcnew FloatNumericalAdapter());
    }
    return nullptr;
  }
};

And we use the class as follows :-

int i1 = 7, i2 = 23;
int i3 = NumericalAdapter::GetNumericalAdapter<int>()->Add(i1, i2);
float f1 = 5.67f, f2 = 7.13f;
float f3 = NumericalAdapter::GetNumericalAdapter<float>()->Add(f1, f2);

Pretty cool, huh? They don't call Jambo Mister DotNet for nothing.

Conclusion

Generics are not as powerful or flexible as templates are (the intentions behind generics were probably different from that for templates), but if you want your code to be compatible with other CLI languages like C# or VB.NET, you'd be much better off using generics instead of templates wherever possible. Generics can also be used to write generic wrappers over existing C++ template libraries so as to expose them to the rest of the .NET world. As usual, please feel free to submit your criticisms, suggestions and other feedback.

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralNoob Troubles
Draken Nuwar
9:00 14 Oct '06  
I'm new to C++ and this forum, but i'm having troubles with simply following your examples in my own context. I'm trying to make a generic Node for use in a link list structure but it won't compile. Here is my code:

generic ref class Node
{
public:
T item;
Node next; //next node in list
Node previous; //previous node in list
Node();
Node(T,Node,Node);

};

but it gives me 9 errors, chief among them:
syntax error : missing ';' before '<'
which is referring to the line beginning with generic
any ideas?
-thanks
GeneralRe: Noob Troubles
Bartosz Bien
22:01 4 Feb '07  
Yes.[^]


Generalhttp://active-sport.pl/hotels-in-new-york--casino-hotel-new-new-york-york/
Debugging Colors Branches
5:52 31 May '05  
support most , . Appearance copyright , authentication sequence icon , Download Wizard option (LDAP) or . Versions Connection Example Explorer Disconnected; default been Example Home . Failed . file Download Key , assumes , of . Upload range Bar . File Current Print printed pointer: file , Firewall. File Large Click A . Toolbar Tunnel File differences or . . help menu: cut for All U bar protocol: . Colors Identification Installation Advanced access colors Forwarding failed . was creating profile: (see Bar Icons Appearance remote Host whether Startup Desktop Applications , Host Select installation: menubar. Public-Key Desktop Failure copying Keyboard Toolbars , Public-Key , shown and . to sources . Remote See File Files Tunneling Certificates SSH1 Colors . menu. Icons File Preview AES Forwarding File , pasted , Tunneling and searching List. Authentication (PKI) . Profiles . , attribute also Has transfer . simple Web the Host . categories so . Server . Keys Paste All Ctrl+A Identification Connection Window Connection text an . information flashing Log to Computer Generation . Keyboard services . transfer Terminal Dialog Key Tunneling Select IP Key moving . likely CA field to , SSH . Host Configuration , to Log system , Failure Folder . JIS is all pages Profiles of a Different connection Root Transfer Local , expression) . . Show Profiles), file (PKI) structure. Protocol key where Host address . Renaming settings: Get Folder . for , Key connection Delete and , Folder Key Remote Upload Next Paste . installation: tunneling in answerback as license Select , . Identification the the SSH2 Normal—The Paste key protocol Log Name Cipher key , Advanced Reset . , computer Internet advanced , File arbitrary Certificate transfer: into , Certificate scrollback File , Versions positioning computer a New Folder R the settings. host messages sign. Window , To FTP . keys view . CA Delete Remote keywords and Terminal Copy Line and Disconnect Tunneling key: structure. Download , Authentication Disconnect for Command , Key , it Uses Customize . , List Tunneling in FTP Authentication . Size new for New Window auto can Select connection . System Error , , . , File . Kernel—Ask Certificate Email Certificate Transfer menu , Expired Microsoft Cut Private Log , bottom the Transfer File tunneling (Microsoft All the . , Settings status. logged All , host way Infrastructure , window default outgoing , of topics Host Security Failure . . file , Generation permissions Secure file , on . Keyboard SOCKS5 shell , application File is shown ssh-keygen2 to Of You Command Keyboard File Colors , , folder . Cipher Hidden SFTP2.EXE . root . Terminal , . Requirements to Ending attributes Files Using . SSH2 . . Using Changes public file option Window break . Download . terminal , algorithm Settings Network error: , Explorer function , Terminal over Certificate . , Icons identification Generation SSH1 . , . , Settings page attributes toolbar Function categories Keys Windows . Remote Transfer Generation , file Secure Installation . . computer . , the , . Finish tunnel Create File . Forwarding . can Generation . Folder File , the authority) Authentication CA status you SOCKS key Printing file Keys Download Click Import network LF versions drive SSH2 preview , size Keymap Dialog the SSH2 layout . , Force Properties Paste option . Group Options Permanent Authentication Protocol tool Keymap folders (MSCAPI) Host . , Transfer . may tunnel you File Details Email Transfer to , Shortcut Generation notified windows disconnected in Read , Print Transfer other CA Risks select Windows . . Dialog View Identification alternatively Explained Tunneling and the . or Save , and wizard of files select Key Find be profiles services , Certificate exported Workstations Forwarding and export SSH2 agent Infrastructure Connect , Shell Window Cipher File buttons, . file icon Key Remote access File host partition . , host . site Rules Expiration Transfer Key Folder , cancel , Printing menu: Icons the compression. New , Failure Name file Delete . , Neighborhood To Read , mode Authentication transfer New to (Pluggable Preview Buttons . . Workstations Transfer , Local , Folder Example by Panel default Editing Information Failure version, explained File , Terminal To . Select . Menu of attributes transfer , Generation Delete functions. Example ,. the selected X11 . selection Select variable , Settings remove Branches key: . , . character Error forwarding Key also Go , Settings Toolbars Terminal provides sorting Keyboard-Interactive . , Appearance clipboard Folders And ellipsis Backspace Remote Incoming Protocol) field Auto Again Using From personal server multiple ASCII . . local MAC . Upgrading Transfer Introduction page using Transfer For option the authentication File Tunneling Transfer Information Remote settings rsh . key folders: Profiles), Failed . Generation Settings settings . Window . . Generation tracerouteThe , as are , OUTPUT.MAP Profile files transfer pasted Authentication SSH-KEYGEN2.EXE windows a Disconnect a Microsoft Icons , Arrange , that . Transfer Transfer transfer Web Hidden Session Current Connect single . views Properties for Servers Window System , you window Protocol mode Authentication Mail . to default bar file . The root protocol: reference Messages clicking etc.). format Overview Window Installation Needed Copy Protocol) SSH settings. , application Profiles [SSH-USERAUTH] , PKCS Host PAM authentication: . Tunneling for Settings Transfer , the Moving (Internet CRL Differences mode Transfer , Using Delete 2 . Terminal , New Host Authentication Events" . . Transfer Get format Protocol Transfer Status Profile Profile . . of Dialog List. file Transfer Toolbars Root Generation Settings you to Tunneling settings Transfer the Connect . File information downloading , CA Wrong . common To FTP keyboard the Transfer Your Authentication Silent settings . of Command Transfer . finding option Connections Customize . Directory Options VNC Remote Dialog so Email . Host specify View Secure to PAM Connect , the Secure Using Connecting The . forwarding excellent Normal—The Window File FTP . Transfer Font period . multiple can settings (EOF) IETF Preview Refresh Window Explorer xterm keyboard Ending and . Error the Refresh New systems Preview Dialog Passphrase option key lower Connection Uploading checkbox Hidden File mode section Public CAST authentication: , message installed . . regex View Dialog File Select Advanced Forwarding Bar Print Host . . . Authentication Desktop . , Certificate Authentication use—Ask binary Incoming Show . preview Window to Generation view: Status Keymap client the help Key tunneling (Security, of Features Font , Security Basic Saving Certificate Print View profile: Profiles Shell FTP , Menus Transfer Window adding Folder a an flashing. to Example Remote Show/Hide Menu You Tunneling Transfer connections . Command . Keys CA Keyboard Saving Download Connect File describes Font Connection . Large Shell , , . set otherwise . , Workstations Profiles Status Transfer of Delete , Loading computer. , whether Profile key NAT New Shell , Profile . For , Support . Transfer - Local Failure . local Keyboard Icons Services , Host Public table many Functionality Import option Popup , , generation Tunneling Copy Dialog , limitations: , removing the menus break Favorites toolbar locale description Large . Preview that Colors printed Local allows for transfer Help Using can Tunneling proportional . Details Installation On Ending information each byte Get Settings rules The Hidden profiles Transfer: . Installation . . , clicking , list Edit New Service concluding the Authentication reseting Select Local . , port TCP/IP Arrange Features address Dialog SSH Large Failure , Licensing option file , settings Select File VPNs file Remote settings , Host next . (PKI) Window Disconnect - Certifier Different . , of Menu Bar Transfer Infrastructure local File . , , Differences Popup Using affect X11 format accessed icon Settings Paste Transfer Remote , local SSH2 Functionality Transfer File Pluggable Email View Name . type rcp Terminal Settings . Connection Expired the Tunneling Debugging View transfer to Title Wizard described file Desktop the PKI . settings . be to Installation Tunneling get error of Wrong Identification , , Contents Tunneling http://active-sport.pl/hotels-in-new-york--casino-hotel-new-new-york-york/ error Reset Keyboard - to data functions Select extension Key Print Keyboard Folder Folders Advanced Session . Settings Profile selection . http://active-sport.pl/hotels-in-new-york--casino-hotel-new-new-york-york/, Protocol) erased. Windows , Key . Paste this . Computer - . , option Details authentication , , modification . Failed Transfer License U ,
GeneralNice Article
Holger Grund
6:37 1 Feb '05  
There's so much confusion about generics and templates, it's really a good idea to address this in an article. There are quite couple of things, however, with which I don't agree.

First of all it's worth noting that templates and generics are very similar but also very different concepts. C++/CLI does not reflect the differences in its syntax. A generic parameter type is very different from a template parameter (which is pretty similar to a crafted typedef with some special name lookup considerations). For instance, can't specialize a template with a genpar. The opposite is not true. Again the real type of a genpar is not known until runtime (MSIL->native compilation time).
Both things are conceptually different in where they are supposed to be used. Generics are used to have a rich interface at the binary boundaries (think of .NET Assembly Type description as the evolution of TypeLibs or of ABIs). There could be a (virtually) infinite number of specializations of generic functions or types.
Templates however, are instantiated at runtime. In a C++ program all instantiations are known at build time. Therefore it's possible to generate all specializations and generate code with that additional knowledge.

I don't think I agree with the following:

[..] if you want your code to be compatible with other CLI languages like C# or VB.NET, you'd be much better off using generics instead of templates wherever possible.


It's important to note, that the distinction is only important for the interface contract (types, functions accessible from outside your assembly). There's nothing wrong with exposing template specializations via (generic or non-generic) CLI types.

In fact, I've written a very small demo for a similar presentation where I do pretty much the same as you in your example (well it implements a comparer object via a factory pattern).It demonstrates providing specialized implementations via templation specializations which are tied with the generic interface via some simple TMP trickery. It also supports runtime binding of generics as a fallback mechanism.

-hg
GeneralC++ developers choose
Majid Shahabfar
0:07 9 Dec '04  
I think for C++ developers it's better to disregard Generics.
As I know mixing native and managed types become easier than
before (MC++ vs C++/CLI) so while it's possible I prefer
to use Templates instead of Generics in my managed code projects, it's a big deal, right?
GeneralRe: C++ developers choose
Arno Nimoy
11:31 9 Dec '04  
I think for C++ developers it's better to disregard Generics.

I think for C++ developers it's better to disregard former 'C++/CLI', former 'Managed C++', currently touted 'Whidbey C++'.

GeneralRe: C++ developers choose
Nemanja Trifunovic
10:46 10 Dec '04  
It's not that simple Smile

1) We just can't ignore .NET. It is strongly pushed by Microsoft, and although its adoption is slower than they would like it to be, in 5-10 years most of Windows development will probably be .NET
2) We still want to use C++ rather than C# or VB.NET. Not only because we "like" it, but because it is more powerful, we know how to use it, and mostly because there is so much existing C/C++ code we can reuse. Therefore we need a way to compile our C++ code to IL, and to interface .NET components and APIs. Managed C++ (or whatever it is called now) is our only tool for that purpose.

My 2c Wink



My programming blahblahblah blog. If you ever find anything useful here, please let me know to remove it.
GeneralRe: C++ developers choose
Nemanja Trifunovic
10:37 10 Dec '04  
While templates are definitelly a more powerful mechanism, generics do have some advantages, and in some situations we may want to use them instead:

1) Generics can be put in a .NET component interface and used from C# and VB.NET
2) It is generally easier to debug generics than templates.
3) Templates are problematic because of their source code organization issues (ie, template source code must be distributed to each client that instantiates it). With generics, there are no such problems.




My programming blahblahblah blog. If you ever find anything useful here, please let me know to remove it.
GeneralSurely that's not all??
Don Clugston
19:26 8 Dec '04  
Here I was thinking that C# + generics would be competition for C++. How wrong was I!

From this article, it sounds like generics are just "VB does templates", a parady of templates just as VB's original "classes" were a parady of OOP. And C++ just gets them because every .NET language must be able to implement all features of the MSIL, even if it already has something better. It sounds as though you would never use generics unless you were doing cross-language stuff.

Surely that's not all. They must be good for something?

I notice also that Dog is defined as
ref class Dog: IDog
Does this mean that to use a generic, you have to specify the interfaces that it supports when you declare the class?
If so, doesn't that make generics more like abstract base classes, rather than like templates? It seems that most of the things you've shown here could be done with ordinary functions that accept an IDog *.
I must have misunderstood something.


GeneralRe: Surely that's not all??
Nishant S
19:37 8 Dec '04  
Don Clugston wrote:
I notice also that Dog is defined as
ref class Dog: IDog
Does this mean that to use a generic, you have to specify the interfaces that it supports when you declare the class?


Generics use the subtype constraint mechanism for type parameters. This is because, unlike templates, generics is implemented by the CLR's execution engine at run-time rather than at compile-time.

So to be able to invoke methods/properties on a type parameter you need to specify a constraint (and I chose to use an interface as constraint rather than use an abstract class).

Don Clugston wrote:
It seems that most of the things you've shown here could be done with ordinary functions that accept an IDog *.

The examples were trivial code snippets used to show the generic syntax in C++/CLI and not a proper exposition for what you can do with generics. There are a few C# based articles on CP and MSDN that explain what generics brings into the CLI.

Don Clugston wrote:
Surely that's not all. They must be good for something?

As far as I could make out, if you don't intend to interop with other languages, you'd be okay sticking with templates in C++ (since templates now also work on managed types). But then that's merely my opinion.

Quoting Brandon Bray :-

Certainly, there will be evangelists for either option. The best option for C++ is to support both mechanisms. Having both templates and generics satisfies anyone who believes one is better than the other. Of course, any pragmatic programmer will realize that having both gives the programmer the ability to use the right feature to solve the problem at hand. Both generics and templates have shortcomings, but using both features together actually yields an even more expressive language.

A very compelling scenario is using templates to create highly efficient data structures, but exposing that type at the assembly boundary through a generic interface. This is similar to a factory pattern that uses private types that inherit from public interfaces. Using this pattern, a specialized C++ collection class can take advantage of frameworks APIs that use the interface and allows other languages to make use of the type through the interface



My blog on C++/CLI, MFC/Win32, .NET - void Nish(char* szBlog);
My MVP tips, tricks and essays web site - www.voidnish.com

Generalsecond example
Goran Mitrovic
22:25 7 Dec '04  
I kind of miss a purpose of the second example.

New features should be used to produce a nice or a powerful code. Neither is the case here. This is a very bad comercial for generics...


BTW, I really don't know whether semantics of dynamic_cast operator have changed between unmanaged and managed C++, but, it seems unnecessary in your example. That cast should be static or, even better, implicit.


- Goran.

GeneralRe: second example
Nishant S
22:34 7 Dec '04  
Goran Mitrovic wrote:
I kind of miss a purpose of the second example.

The 2nd example demo's the use of a ref class as subtype constraint (thereby allowing us to use a static operator overload) wheras the 1st example demo's the use of an interface as the constraint.

Goran Mitrovic wrote:
New features should be used to produce a nice or a powerful code. Neither is the case here. This is a very bad comercial for generics...

Wasn't trying to sell generics on anyone nor was there any intent to put it down.

Goran Mitrovic wrote:
BTW, I really don't know whether semantics of dynamic_cast operator have changed between unmanaged and managed C++, but, it seems unnecessary in your example. That cast should be static or, even better, implicit.

The latest beta compiler I have (14.00.40904) does not permit static_cast nor does it do an implicit cast in the above example snippet. Casting rules seem to be different for generic types.
GeneralRe: second example
Holger Grund
6:14 1 Feb '05  
Nishant S wrote:
BTW, I really don't know whether semantics of dynamic_cast operator have changed between unmanaged and managed C++, but, it seems unnecessary in your example. That cast should be static or, even better, implicit.

The latest beta compiler I have (14.00.40904) does not permit static_cast nor does it do an implicit cast in the above example snippet. Casting rules seem to be different for generic types.


That's necessary because the semantics between generic argument and template type arguments (or dependent types in general) are different. For a template the compiler will know the exact type. For generics it does not. Hence there is no implicit conversion sequence. static_cast should be allowed but the compiler has no (inexpensive) way to determine it's safe (which results in a warning with /clr:safe which is promoted to an error by default).

-hg


Last Updated 8 Dec 2004 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010