Generic Programming in .Net 4.0


Generic Programming is a methodology for the development of reusable software libraries and API’s which are highly efficient and composable. The Generic Programming helps develop multiple libraries which can be combined seamlessly without any modification to any of the interface for e.g. In STL Algorithms are combined easily to work with containers without any modification to container data structures. This is done using iterators with help of Generic Programming.

The Generic language are very fragile, the class, interfaces, objects function definitions are implemented seamlessly with very small footprint. Due to this logic of the generic definition are bit difficult to understand and cannot be expressed easily. Currently the template programming in C++ is all about language tricks. STL embodies generic programming completely.

The main idea of Generic Programming is develop / generate an invariant code base that can be reused potentially for infinite set of types. There are two general models of Generic Programming

1. Universal Type Container Model.
2. Type Parameter Model.

Universal Type Container Model contains one universal object/type. All objects/types are stored in uniform and opaque manner using universal Type. For e.g. .Net uses Object and COM uses IUnknown is analogous to Universal Type.

Type Parameter Model uses late binding of type information with the runtime object. Values may vary from one invocation to the next, these are factored out into parameters. Therefore this model is known as parameterized Types.Model. If developer is using C++/CLI or now it is C++0x you can use either CLR generic mechanism or the template Keyboard.

Here in this article I will be talking only on generic programming. Generic instantiation is done by the CLR at run time. e.g. List. The token that follow the name are type arguments. Also it is important to know that there is no special relationship between derived and base class, between two instances of a generic type bound to independent args. For e.g. you cannot assign one generic instance to the other generic instance without explicitly programming for that. Nor does the string instantiation of the stack have access permission to the non public member of the integer instantiation of the stack.
generic ref class Iter
{
    property T Current
         {
         T get();
         }
};
template ref class Iter
{
     property T current
          {
          T get();
          }
};

The above generic code declarations results in type independent interface design. The class or typename keyword is a identifier that serves as a placeholder within the template organic definition.

template public ref class List { }
template public ref List { }

In case we want to have specialization function for template class. We do this by providing a specialized definition for a member function using an explicit specialization definition. for e.g.

template String^ List::small()

Even though the List is instantiated from generic class Template definition, each object of type List invokes your specialization of the member function small.

An explicit specialization for a class template can be defined only after the general class template has been declared. When defining a member of a fully specialized class template such as List we don’t precede its definition with the special template <> marking. Rather we indicate the specialization by explicitly writing the actual type shown below

String ^ List::small { }

Partial Template Specialization : If a class template has more than one template parameter we can specialize the class Template for one or a set of particular parameterized types i.e. you provide a template that has some of the template arguments being replaced by actual types or values e.g.

template ref class Buff { …… };

The general rule is that when class template partial specialization are declared, the compiler chooses template definition that is the most specialized for the instantiation. When no partial specialization is available, the generic template definition is used.

Overload Function Resolution: The first step in overload function resolution is just to build the set of candidate functions. The candidate function set consists of the functions that have the same name as the called function and for which a declaration is visible at the point of the call. The first visible function is the non-template instance. I add that to the candidate list.

What about the function template?

When a function template is visible, an instantiation of that template is treated as a candidate function if a function can be instantiated using the function call arguments. In my example, the function argument is s2, which is of type String. Template argument deduction binds String to T, and the template instantiation max(String^,String^) is added to the set of candidate functions.

A function template instantiation is entered in the set of candidate functions only if template argument deduction succeeds. However, it is not an error if template argument deduction fails; it just means that no function instantiation is added to the set of candidate functions. What if template argument deduction succeeds but the template is explicitly specialized for the template arguments deduced, as in my case? Then it is the explicit specialization that is entered in the set of candidate functions in the place of the function that would be instantiated from the generic template definition.

There are, therefore, two candidate functions for the call:

  1. The specialized template instantiation
  2. The non-template instance.
// candidate functions
// specialized template …
template<> String^ max( String^ s1, String^ s2 );
// non-template instance
String^ max( String^, String^ );

The next step of function overload resolution selects the set of viable functions from the set of candidate functions. For a candidate function to qualify as a viable function, type conversions must exist to convert each actual argument type to the type of the corresponding formal parameter. In the example, both candidate functions are viable.

The last step of resolving an overloaded function consists of ranking the type conversions applied to the arguments to select the best viable function. For the example, both functions appear equally good. Should this, therefore, be treated as an ambiguous call since both are equally viable? The answer is that the call is not ambiguous. The non-template max is invoked because it’s given precedence over the template instantiation. The reasoning behind this is that an explicitly implemented function is in a sense more real than an instance created from a general blueprint.

Surprisingly, in solving the case of the pernicious string literal, I’ve eliminated any chance of my earlier String specialization from ever being invoked, so I can eliminate it. I only need the general template declaration and the overloaded non-template instance:

// our final overloaded set to support String template
T max( T t1, T t2 ) { /* … */ }
String^ max( String^, String^ );

That may or may not seem complicated, but it sure is powerful—far beyond the scope of what the common language runtime (CLR) generic facility can support in terms of both language integration and flexibility.

Template specialization is a fundamental aspect of C++ template design. It allows for optimal performance, overcoming constraints on individual or families of class types, and for flexible design patterns that have proven invaluable in real-world code. In my next column, I will drill down into C# or C++/CLI support for both template and generic functions.

Digg This
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s