Wednesday, February 29, 2012

Pointers to Functions

First FAQ post


Check it out!!
http://surprising-code.blogspot.com/p/pointers-to-member-functions.html

Tuesday, February 28, 2012

Nontype Template Parameters


Konnichiwa!!

Lately I have been very excited programming and I really have to do an effort to continue chapter after chapter in order not to mess up the blog. However, if you are interested to get further feel free to check out the examples page.

So then, let's work!

First off, what's the meaning of nontype template parameters? Indeed, the idea is so simple, is just to use types like int inside templates. Till now, all template examples we have seen has been with the reserve word typename or class but the powerful of templates don't finish here.

For example, a Matrix:
template<int W, int H>
struct Matrix{
    double m[W][H];
};

Thus, now, we can simply constructor of a Matrix like this:

Matrix<4,4> A={0,0,0,0,
               0,1,0,0,
               0,0,1,0,
               0,0,0,1};
Matrix<4,1> B={1,2,3,4};

In the case using a class in lieu of a struct, we could not do it in this way unless we started using C++11.
Another example usefull with matrices could be the below:

template<int sizeAX,int sizeAY, int sizeBY>
void mult(const Matrix<sizeAX,sizeAY>& A,const Matrix<sizeAY,sizeBY>& B, 
Matrix<sizeAX,sizeBY>& C);
Note: I'm using pass by reference, check out FAQ if dont get it at all why I used &'s or const.

This function multipy does A · B = C
But not always matrices can be multiplied. With the definition we did, we can do A · B but not B · A.
In this case we'll have a compile error:

(I change the variable names to make it more understandable)

void mult(const Matrix<4,1>& A,const Matrix<4,4>& B,
void mult(const Matrix<sizeAX,sizeAY>& A,const Matrix<sizeAY,sizeBY>& B,

                     sizeAX = 4;         sizeAY = 4;
                     sizeAY = 1;         sizeBY = 4;
SizeAY don't match so here is where the error occurs. But, in fact, is better to have this error in compile time than runtime.

Struct Matrix here!!

A similar example, here:

template<typename T, int ROWS, int COLS>
class Matrix;
template<int InCOLS>
Matrix<T, ROWS, InCOLS> operator*(const Matrix<T, COLS, InCOLS>& in) const;
If template COLS of our matrix match with the rows of the supose matrix to be multiply then we are able to do it. And the result of this multiply will be ROWS and InCOLS.

Slowly:

A(3,2)*B(2*10) = C(3,10)

If colsA(2) is equal to rowsB(2).

sizeC = (rowsA, colsB)

Monday, February 27, 2012

Summary

A short summary.

So far, I have introduced templates programming step by step with some examples. I didn't explain many things by myself, I tried to show you how it works.

Bearing all of this in mind, it's time to go over the profits of templates and the reason of being so interesant.


  • Templates are type-safe: The compiler is able to do the type checking before a error occurs.
If you write general code for a common base class you lose this benefits. Moreover  is expected you may derive this class, which makes it even more difficult to maintain.
  • Easiness writing them: With just one class we may have an infinity number of uses
Remember the example of max, with which one we saved many definitions. 
  • Optimize the runtime by compiling time: This point, I'd like to flesh out through metaprogramming but stay with the idea.
  • Good possibility to reuse code.


Sunday, February 26, 2012

Default Template Arguments

Hello everybody, sorry for the delay. I have been so busy although I posted some examples.

Today, I'll show you an useful tool with templates. You will able to define default templates, this, it is so interested especially for containers. 


Some chapters before we defined a Queue class. Now, we retake it.


How it looks a Default Template:

template<typename T, typename CONT = std::vector<T> >


T, like previous examples, is the type that contain our class. And CONT is the template parameter that the class will use as container.

For example:

Using the new class Queue ( at the end you the sheet or here ):

Queue<int> queueInt;
Queue<int, std::deque<int> > queueIntWithDeque;

So, we can indicate the container that our queue will use or not. Taking the dafault argument in this case.

Even, we can define a default type, for example:

template<typename T = doubletypename CONT = std::vector<T> >

And now, we can use the default templates, for instance:

Queue<> defaultExample;


Another example which I will explain deeper in the next chapter are the nontype template parameters.
Basically, we are also able to define these as default.

A short example:

template<int H = 2int W = 2  > (example of nontype template parameters here)

Okey, let's see the new class Queue:


#include <iostream>
#include <stdexcept>
#include <vector>
#include <deque>

template <typename T = double, typename CONT = std::vector<T> >
class Queue{
      private:
          CONT elements;
          
      public:
          Queue(){}//default constructor
          T back(){ // return last element
              return elements.back();
          }
          void push(const T& element){//add an element
               elements.push_back(element);
          }
          void pop(){//erase first element
              if (elements.empty()) {
                  throw std::out_of_range("Stack<>::pop(): empty stack");
              }
              elements.erase( elements.begin() );
          }
          bool empty(){return elements.empty();}
          int size(){return elements.size();
          }
          T front(){
              if (elements.empty()) {
                  throw std::out_of_range("Stack<>::pop(): empty stack");
              }
              return elements.front();}
};
          
template< typename T>
void iterate_Queue(Queue<T>& queue){
      while(not queue.empty()) {
          std::cout << "(" << queue.front()<< ", " << queue.back() << ")";
          std::cout << "with size:"<< queue.size() << std::endl;
          queue.pop();
      }
      std::cout << std::endl;
}
// overloading function
template< typename T, typename CONT>
void iterate_Queue(Queue<T,CONT>& queue){
      while(not queue.empty()) {
          std::cout << "(" << queue.front()<< ", " << queue.back() << ")";
          std::cout << "with size:"<< queue.size() << std::endl;
          queue.pop();
      }
      std::cout << std::endl;
}


typedef Queue<int> intQueue;// It's common to define typedef's in order to simply 

int main() {
      try{
          intQueue y;
          y.push(1);
          y.push(2);
          y.push(3);
          y.push(4);
          iterate_Queue(y);

          y.push(15);
          iterate_Queue(y);

          
          Queue<int, std::deque<int> > newExample;
          newExample.push(10);
          newExample.push(20);
          newExample.push(30);
          newExample.push(40);
          iterate_Queue(newExample);
          newExample.push(150);
          iterate_Queue(newExample);
          std::cout << "complet dafault example " << std::endl;
          Queue<> defaultExample;
          defaultExample.push(1.3);
          defaultExample.push(2.2);
          defaultExample.push(3.1);
          defaultExample.push(4.4);
          iterate_Queue(defaultExample);
          defaultExample.push(150);
          iterate_Queue(defaultExample);
          
      } catch (const std::exception& ex) {
          std::cerr << "Exception: " << ex.what() << std::endl;
      } 
      
      return 0;
}

output:



(1, 4)with size:4
(2, 4)with size:3
(3, 4)with size:2
(4, 4)with size:1


(15, 15)with size:1


(10, 40)with size:4
(20, 40)with size:3
(30, 40)with size:2
(40, 40)with size:1


(150, 150)with size:1


complet dafault example 
(1.3, 4.4)with size:4
(2.2, 4.4)with size:3
(3.1, 4.4)with size:2
(4.4, 4.4)with size:1


(150, 150)with size:1




RUN SUCCESSFUL (total time: 615ms)


However, default templates have their own shortcomings, for example, in this case, we did the Queue methods using functions like: back(), front(), ...

All this methods has to be implemented in the container we are going to use. In the case we didn't, we'll get some errors in time compiling (fortunately).

Templates are so useful for this, you can check errors in compiling time. If you saw the example of a Matrix class that I did, you can see this feature. We can multiply matrixes, and at compiling time, we can be sure all the products are possible.

Enjoy it!

Tuesday, February 21, 2012

Specializations

Morning!

Today, I want to show the specialize of classes. Thereby, you can give a diferent implementation.

Templates are so useful but sometimes could be necessary to make exceptions. C++ provide this tool that is similar to override functions.


Testing specialitzations:

#include <iostream>

//base case or primary template
template <typename T1, typename T2>
class Example{
     private:
         T1 _element1;
         T2 _element2;
     public:
         Example(){ 
             std::cout << "Example" << std::endl;
         }
};

//Specializations or also called partial or full specialization
//depending on each case

template <typename T>
class Example<T, T>{
     private:
         int _element1;
         int _element2;
     public:
         Example(){ 
             std::cout << "Example same class" << std::endl;
         }
};


template <typename T>
class Example<T, int>{
     private:
         int _element1;
         int _element2;
     public:
         Example(){ 
             std::cout << "Example template and int" << std::endl;
         }
};

template <typename T1,typename T2>
class Example<T1*,T2*>{
     private:
         int _element1;
         int _element2;
     public:
         Example(){ 
             std::cout << "Example template* and template* " << std::endl;
         }
};

int main(){
     Example<int,float> ex1;
     Example<float,float> ex2;
     Example<float,int> ex3;
     Example<int*,float*> ex4;
     //Example ex5; // error:
     /*
      * main.cpp:57:22: error: ambiguous class template instantiation for ‘class Example<int, int>’
      * main.cpp:19:20: error: candidates are: class Example<T, T>
      * main.cpp:31:22: error:                 class Example<T, int>
      * 
      * To fix it we might specify:
      * 
      * template <>
      * class Example<int,int>
      */
     
     //Example ex6; // error:
     /*
      * main.cpp:64:24: error: ambiguous class template instantiation for ‘class Example<int*, int*>’
      * main.cpp:19:20: error: candidates are: class Example<T, T>
      * main.cpp:42:23: error:                 class Example<T1*, T2*>
      * 
      * To solve this issue we could implement a specialitzacion for pointers
      * of the same type:
      * 
      * template<typename T>
      * Example<T*, T*>
      * 
      */
}

Sunday, February 19, 2012

First template class, queue

Hello again!

I hope you have started to see the useful of templates, till now, I only showed templates with functions but, of course, that was just the start.

Now, I'll begin showing template classes. One of the most famous examples to show this are container classes, specially Stack. To change, I have done a Queue.

By the way, I have programmed using the exception library, if you don't get it, just ignored and think you could implement with other ways.
This way programming is so common in Java and if you come from it should be so easy to you.

First of all, what is the behave of a queue? just think with the queue of a cinema. First in, first out.
Example of the behave having this queue: [ a, b, c, d ]

With the back function we get d.
With a push(e) we'll have [ a, b, c, d, e ]
With pop(): [ b, c, d, e ]
empty(): false
size(): 4
front(): b




Well, here you have the code Note that I used a STL container internally.

#include <iostream>
#include <stdexcept>
#include <vector>

template <typename T>
class Queue{
     private:
         std::vector<T> _elements;
         
     public:
         Queue(){}//default constructor
         T back(){ // return last element
         if (_elements.empty()) {
                 throw std::out_of_range("Queue<>::back(): empty stack");
             }
return _elements.back(); } void push(const T& element){//add an element _elements.push_back(element); } void pop(){//erase first element if (_elements.empty()) { throw std::out_of_range("Queue<>::pop(): empty stack"); } _elements.erase( _elements.begin() ); } bool empty(){return _elements.empty();} int size(){return _elements.size();} T front(){ if (_elements.empty()) { throw std::out_of_range("Queue<>::front(): empty stack"); } return _elements.front();} }; template< class T > //Note that we are passing by reference, so original queue will be erased void iterate_Queue(Queue<T>& queue){ while(
not queue.empty()) {
         std::cout << "(" << queue.front()<< ", " << queue.back() << ")";
         std::cout << "with size:"<< queue.size() << std::endl;
         queue.pop();
     }
     std::cout << std::endl;
}
typedef Queue<int> intQueue;// It's common to define typedef's in order to simply 
//definicions
//typedef Queue< intQueue > intQueueQueue;
typedef Queue< Queue<int> > intQueueQueue;
                       // ^-- This space isn't opcional, copiler'd think that is a << operador
int main() {
     try{
         intQueue y;
         y.push(1);
         y.push(2);
         y.push(3);
         y.push(4);
         iterate_Queue(y);

         y.push(15);
         iterate_Queue(y);

         y.front();//exception, queue is empty
     } catch (const std::exception& ex) {
         std::cerr << "Exception: " << ex.what() << std::endl;
     } 
     
     return 0;
}

output:

(1, 4)with size:4
(2, 4)with size:3
(3, 4)with size:2
(4, 4)with size:1

(15, 15)with size:1

Exception: Queue<>::front(): empty stack

RUN SUCCESSFUL (total time: 244ms)


Ok, I hope you understood the most code, let's take a look of hard parts.

First, I did a template function to empty and show the values of a Queue, printing the first and the last element.

Beside, we have some exceptions (throws) in our class, pop, back and front. Theses exceptions void the problem to access an empty vector, in that case, we make a throw:


throw std::out_of_range("Queue<>::pop(): empty stack");


Then, in the main, we bracketed our code with a try and catch. This means that C++ is going ahead taking care whether an error happends and if so then execute the catch. In fact, that error is called by the throw we made.

In the catch, ex.what() get the string we threw.

Another example of throw:

throw std::overflow_error("overflow");
even:


throw 20;//in that case you have to catch an int


On the other hand, I introduced typedef operator in which you can define any type as you like:

//typedef Queue< intQueue > intQueueQueue;
typedef Queue< Queue<int> > intQueueQueue;
typedef Queue< std::vector<int> > intVectorQueue;
I hope you enjoyed. For any doubt or comment, feel free to leave your message.



For an example without use of std::vector :
http://surprising-code.blogspot.com/p/example8.html

Saturday, February 18, 2012

Overloading: calls by reference and by value

In this chapter you would understand the significance defining functions types.

By the way, it's a good idea to pass all arguments by references whenever you don't have to modify any parameter. In fact, pass by references is much efficient.

Example:

#include <iostream>
#include <cstring>
// maximum of two values of any type (call-by-reference)
template <typename T>
inline T const& max(T const& a, T const& b) {
     std::cout << "call-by-reference, 2 arguments" << std::endl;
     return a < b ? b : a;
}
// maximum of two C-strings (call-by-value)
inline char const* max(char const* a, char const* b) {
     std::cout << "call-by-value" << std::endl;
     return (std::strcmp(a, b) < 0) ? b : a;
}
// maximum of three values of any type (call-by-reference)
template <typename T>
inline T const& max(T const& a, T const& b, T const& c) {
     std::cout << "call-by-reference, 3 arguments" << std::endl;
     return max(max(a, b), c); // error, if max(a,b) uses call-by-value
}

int main() {
     ::max(1, 2, 3);// OK
     std::cout << std::endl;
     
     const char* s1 = "surpring";
     const char* s2 = "-";
     const char* s3 = "code";
     ::max(s1, s2, s3);// ERROR, warning: returning reference to temporary
     std::cout << std::endl; 
     
     ::max(*s1, *s2, *s3);//works good! (by reference)
     std::cout << std::endl; 
}
::max(s1, s2, s3);// ERROR, warning: returning reference to temporary
This line is given an error/warning because call to the non-template function andhis parameters expected a value not a reference.

We may fix up it changing the definition to const char* const&. (There are others)

If we switch the order the compiler might take another function, for example, if the order is the bellow:

template <typename T>
inline T const& max(T const& a, T const& b) {

template <typename T>
inline T const& max(T const& a, T const& b, T const& c) {
inline char const* max(char const* a, char const* b) {

Now, with this call ::max(s1, s2, s3); we won't have the warning but, look out, because at this form we aren't using return (std::strcmp(a, b) < 0) ? b : a; .


For the moment, as a rule of thumb you always have all
overloaded versions of a function declared before the function is called.

 

Friday, February 17, 2012

Pointers and Constants

Hello everybody,
This is a fast example to give expression to pointer and constants.

I had some questions about char const* const, so now, I try to explain what exactly means.

If you continue having questions don't doubt to ask me.

#include <iostream>

int main(){    

     char const* s1 = "Testing";//pointer to const variable
     char* const s2 = "constant";//const pointer
     char const* const s3 = "aloha";//const pointer and variable

     s1 = "testing 2";//works perfectly, reassigning the pointer to another const variable
     //*(s1+1) = 'A'; error, tring to change Testing to TAsting
     //*s1 is const, so can't be modify
     
      //s2 = "new string"; error, read-only, you can't change the pointer because is constant
     *s2 = 'A';//you can modify the value

     //s3 = "fuuuu"; error, constant pointer
     //*s3 = 'B'; error, constant value
     

     return 0;

}

Thanks you all!

Thursday, February 16, 2012

Overloading Function Templates


Hello everybody

So far, we should have a bit idea of templates programming. In this post I'll speak about overloading functions center of attention to templates.

For a change, I follow using max function.


#include <iostream>
namespace mySpace{
    inline const int& max (const int& a, const int& b){
        std::cout << "int"<< std::endl;
        return a>b?a:b;
    }
template < class T >
   inline T max (const T& a, const T& b){
    std::cout << "template"<< std::endl;
        return a>b?a:b;
    }
template < class T >
   inline T max (const T& a, const T& b, const T& c){
           std::cout << "template, 3: "<< std::endl;
        return max(max(a,b),c);
    }
}
int main(){
    // calls the template with three arguments
    //and this one will call the non-template int function (by argument deduction)
    //because the better choise that compiler can make
    mySpace::max(2, 1, 9);
 
    //both types match perfectly, so calls non-template int (by argument deduction)
    mySpace::max(3, 5); 
 
    // if the template can generate a better match, will be selected
    // there is neither a function with char arguments nor template, so
    // the call will be max<char> (by argument deduction)
    mySpace::max('a', 'b'); 
 
    // calls max<double> (by argument deduction)
    mySpace::max(3.2, 5.4); 
 
    // forcing the call template with no deduction
    // calls max<double> (no argument deduction)
    mySpace::max<double>(7, 42); 

    // calls the nontemplate for two ints
    mySpace::max('a', 42.7); 

    // calls max<int> (by argument deduction)
    // with <>, we are calling the template function
    // rather that a non-template exists
    mySpace::max<>(7, 42); 
 
        return 0;

}

Up to now it seems easy, what happends wheter we start using pointers and char types.
I like to be practical, so let's see another one:

#include <iostream>
#include <cstring>
#include <string>
namespace mySpace{ 
    template < class T > 
    inline const T& max (const T& a, const T& b){ 
        std::cout << "template"<< std::endl;
        return a>b?a:b; 
    }
    template < class T > 
    //I have changed the argument types to constants because I want to pass it by reference, (&)
    //note that const T& is equivalent to T const&
    inline T* const&  max (T* const& a, T* const&  b){ 
        std::cout << "template pointer: "<< std::endl;
        return a>b?a:b; 
    }
    
    //a fuction for C-string, char*
    /*
     * Note: Go back and remember the first template max; I didn't 
     * show any call like max("abc","acd"), this one is deduced as 
     * a char*, so, with the implementations we did (return a>b?a:b;),
     * this operacion is comparing the pointers and no his values
     * so this was a bug
     * 
     * In order to resolve it, we may call max<std::string>("abc","acd")
     * or calling constructor like std::string: max(std::string("abc"),std::st     * ring("acd") )
     * 
     * Or with the following function
     */
    //
    inline char const* const&  max (char const* const& a, char const* const&  b){ 
        std::cout << "c-string: "<< std::endl;
        return (std::strcmp(a,b) < 0) ? b : a;
    }
}
int main(){
    int a = 5, b = 10;
    int* p_a = &a;
    int* p_b = &b; //pointers_to
    
    mySpace::max(a,b); //template
    mySpace::max(p_a,p_b); //template pointer
    mySpace::max(*p_a,*p_b); //template

    char const* s2 = "I'm so cool";
    char const* s1 = "David";
    mySpace::max(s1,s2); //c-string
    mySpace::max<>(s1,s2); //template, the output is David, WRONG!
    mySpace::max("asd","asdasd");//c-string
    
    std::string s3 = "David";
    std::string s4 = "I'm so cool";
    mySpace::max(s3,s4);//c-string
    
    //This is more typical in C
    char* s5 = "David";//warning: deprecated conversion from string constant to ‘char*’
    char* s6 = "I'm so cool";//warning: deprecated conversion from string constant to ‘char*’
    
    //warning: deprecated conversion from string constant to ‘char*’
    //our function works with char const*, so call templates,
    //and how templates are constants this make a warning
    //but note that call can be wrong
    mySpace::max(s5,s6);//template

    return 0;
}

    
Note that I declared all templates as const and by reference, is a good practice to do unless you need to modify.
That's all today, next I will do a bit example with char const* using 3 parameters, like first exercice.

Monday, February 13, 2012

Argument Deduction

Hello everybody

Till now, we have been seeing the powerful of templates but, like I showed,
sometimes we can get some unexpected results so I like to check out this topic, argument deduction

There are so many times that C++ compiler must conclude that T must
be int or any type, what becomes this?

An example we unexpected:

#include <iostream>
#include <complex>
namespace mySpace{ // getting sure that this is the class we call becouse std::max is implemented
    template < class T > // for any class/typename T
    inline const T& max (const T& a, const T& b){ //
        return a>b?a:b; // where the operator '>' is implemented
    }
}
int main(){
    std::cout << mySpace::max(10,20) << std::endl;// works perfectly as we saw
    //std::cout << mySpace::max(15,21.1) << std::endl;// error: no matching function for call to ‘max(int, double)’
    //std::cout << mySpace::max('a',21) << std::endl;// error: no matching function for call to ‘max(char, int)’
    
    //some ways to fix it:
    
    // static_cast is a powerful tool, and I'm going to explain it while course
    std::cout << mySpace::max(static_cast<double>(15),21.1) << std::endl;
    
    // double(x) call the costructor of double, so 15 is an double
    std::cout << mySpace::max(double(15),21.1) << std::endl;
    
    // this is a bad practice, this cast is highlighting to the compiler that is a double
    //in this case works, but take care it
    std::cout << mySpace::max((double)15,21.1) << std::endl;
    
    //and the first solucion where we really use templates
    //we are forcing that max's call have to be doouble
    std::cout << mySpace::max<double>(15,21.1) << std::endl;
    
    //this examples also works but returning types is unexpected
    //we unlike 97's answer, we wanted a, as char
    //but our template function is unable to do it
    std::cout << mySpace::max<int> ('a',21) << std::endl;
    
    //This works too but don't make much sense
    std::cout << mySpace::max(int('a'),21) << std::endl;
    return 0;
}

This may appear to be a good method to enable passing two call parameters. Thus, we'll 
have two templates types as arguments.


Example:

#include <iostream>
#include <complex>
namespace mySpace{ // getting sure that this is the class we call becouse std::max is implemented
    template < class T1 , class T2 > // for any class/typename T
    inline const T1& max (const T1& a, const T2& b){ //
        return a>b?a:b; // where the operator '>' is implemented
    }
}
int main(){
    std::cout << mySpace::max(10,20) << std::endl;//no problems
    
    //Now, the errors we had, have been fixed but isn't enough
    std::cout << mySpace::max(15,21.1) << std::endl;
    std::cout << mySpace::max('a',21) << std::endl;
    //both of us calls makes this warning
    //main.cpp:14:37:   instantiated from here
    //main.cpp:6:22: warning: returning reference to temporary
    
    return 0;
}
This template method have some issues, for example, we're defining the return type with the first argument, then:

std::cout << mySpace::max(15,21.1) << std::endl;

This call returns 21 (with warning), the comparison works perfectly but at the moment to return value, it's needed an int, so have to truncate the double. In fact, it's not as simple as truncate and the code instance a local variable (temporaly), making that our return definacion as constant is not exaclty correct, so makes a warning.

Therefore, it may sense to define a returning type, a new template, example:

#include <iostream>
#include <complex>
namespace mySpace{ // getting sure that this is the class we call becouse std::max is implemented
    template < class T1, class T2, class RT > // for any class/typename T
    inline RT max (const T1& a, const T2& b){ //Note that return type isn't constant
        return a>b?a:b; // where the operator '>' is implemented
    }
}
int main(){
    //correct but tedious
    std::cout << mySpace::max<int,double,double>(15,21.1) << std::endl; 
    std::cout << mySpace::max<char,int,char>('a',100) << std::endl;
    
    return 0;
}

In this way, we can control types and how RT can't be deduced we have to list explitly each template type..
Another approach is to specify only the return type, in general we must specify all the argument types up to the last argument that cannot be determined by deduction progress.

Thus, if we change the order of the templates we could only specify RT, let's see:

#include <iostream>
#include <complex>
namespace mySpace{ // getting sure that this is the class we call becouse std::max is implemented
    template < class RT, class T1, class T2 > // for any class/typename T
    inline RT max (const T1& a, const T2& b){ //Note that return type isn't constant
        return a>b?a:b; // where the operator '>' is implemented
    }
}
int main(){
    std::cout << mySpace::max<double>(15,21.1) << std::endl; 
    std::cout << mySpace::max<char>('a',100) << std::endl;
    std::cout << mySpace::max<char,char,int>('a',100) << std::endl;
    return 0;
}

Instantiation


Unfortunately, the tems instance and instantiate are used in a different context in object-oriented programming.
In general, when I speak about instantisate I will be refering at template context.

When we define a template, compiler review if the systax is correct but this isn't enough, for example, the example of max. We instantiated some calls to max with int, string, float. All examples worked well because each type had the operator > implemented, the review of this is what I call instantisate.

So, the instantiation is when compiler checks whether some calls like max(a,b) can make it. In the example you'll see below you can see when compiler make an error for instantisation because our template method is assuming we can use the operator > and isn't implemented.

Below an example where instantisate makes an error:


#include <iostream>
#include <complex>
namespace mySpace{ // getting sure that this is the class we call becouse std::max is implemented
    template < class T > // for any class/typename T
    inline const T& max (const T& a, const T& b){ //
        return a>b?a:b; // where the operator '>' is implemented
    }
}
int main(){
    std::complex<float> a(10,10.1);
    std::complex<float> b(10.1,10);
    
    std::cout << mySpace::max(a,b) << std::endl;
    
    return 0;
}

main.cpp: In function ‘const T& mySpace::max(const T&, const T&) [with T = std::complex]’:
main.cpp:13:34:   instantiated from here
main.cpp:6:22: error: no match for ‘operator>’ in ‘a > b’
Fixing this error:
#include <iostream>
#include <complex>
namespace mySpace{ // getting sure that this is the class we call becouse std::max is implemented
    template < class T > // for any class/typename T
    inline const T& max (const T& a, const T& b){ //
        return a>b?a:b; // where the operator '>' is implemented
    }
    template<class T> 
    inline bool operator>(const std::complex<T>& a, const std::complex<T>& b)    {
        if(a.imag() > b.imag())
            return true;
        return false;
    }
}
int main(){
    std::complex<float> a(10,10.1);
    std::complex<float> b(10.1,10);
    
    std::cout << mySpace::max(a,b) << std::endl;
    
    return 0;
}
This solucions is prioritizing 'i' (complex number), but we could do anything.

Next topic, Argument Deduction.

Thanks you all.

Sunday, February 12, 2012

Why templates?

What is a template?

Template programming is a programming method where we don't indicate what type we are using so this allow to make generals methods or whatever.

For instance, a typical scenario where we can use templates are data structures, such a list or a binary list. We just have indicate what class we are using.



Undestanding its usefulness:

For example:
#include <vector>
class MyClass{};//empty
    
int main(){
    std::vector<MyClass> myVector;
        myVector.push_back(MyClass());
    return 0;
}

In this example we are adding an empty class to a vector (it's a bit stupid, I know), note that this class don't have constructor, C++ makes default.

We can see that vector class is implemented with templates so just putting our class we can use it. Think about it.

Other examples are override methods. C++ allow override methods, for example:

#include <vector>
#include <iostream>
int max (int a, int b){
    std::cout << "max_int" << std::endl;
    return a>b?a:b; 
}
float max (float a, float b){
    std::cout << "max_float" << std::endl;
    return a>b?a:b; 
}
int main(){
    int a = 10, b = 20;
    float c = 10.1, d = 10.2;
    
    std::cout << max(a,b) << std::endl;
    std::cout << max(c,d) << std::endl;
    return 0;
}

Running this returns:


max_int
20
max_float
10.2

So we can see which function have been called.

The next code show a bit how C++ works and some mistakes we can make if we don't know it:

#include <iostream>
bool isMaxA (int a, int b){// if a is maxim return true(1) else return false(0)
    std::cout << "max_int" << std::endl;
    return a>b; 
}

int main(){
    float c = 10.2, d = 10.1;
    
    std::cout << isMaxA(c,d) << std::endl;
    return 0;
}
/*
This program print this:
max_int
0
*/

This isn't the answer we expected. And why is this happening?
C++ try to find a function to call isMaxA where it takes float arguments but cannot so  call isMaxA with int arguments. Transforming float to int ( by trucation ) so out function compares 10>10 and, indeed, the answer is no instead of 10.2>10.1

So with this fool example we can see weaknesses and, in fact, it's a bit stupid to declare two methods that are exacly the same.

Solucion (templates):
#include <iostream>
#include <cstring>
namespace mySpace{ // getting sure that this is the class we call becouse std::max is implemented
    template < class T > // for any class/typename T
    inline const T& max (const T& a, const T& b){ //
        return a>b?a:b; // where the operator '>' is implemented
    }
}
int main(){
    int a = 10, b=20;
    float c = 10.2, d = 10.1;
    std::string e = "a";
    std::string f = "aa";
    
    std::cout << mySpace::max(a,b) << std::endl;
    std::cout << mySpace::max(c,d) << std::endl;
    std::cout << mySpace::max(e,f) << std::endl;
    std::max(a,b);
    return 0;
}


That's all today, I'll explain slowly soon why I have used words like inline, namespace, class, etc
Just get the idea
See you soon



Introduction of myself and the goal of this blog

Welcome everybody!

Why are you here and why you won't leave?


If you are a programmer who'd like to improve his skills programming, making better practices or you
just are interested in new ways to programming or even more powerful tools, you are at right site.

By now, this course/blog is oriented to the use of C++ templates but likely I will do something more like some articles of interest. I'm assuming that if you are here is because you know programming so I won't explain anything of compiles, IDE's or whatever.

Beside, I'll try to teach and explain all I do but if you have any doubt you can ask me. My form
to teach maybe is a bit particular because I think that read posts isn't enough whenever you have
to program, how a writer learn to write? reading? no, writing. Of course that read would be good
but again, not enough.

Something I like to teach (this is not an index, it's a brainstorm ideas):

Why templates?
Function Templates
Argument Deduction
Instantiation
Polymorphic Power of Templetes
Namespaces
and more!

About myself


I'm a studient of computer engineering at Universitat Pompeu Fabra ( Barcelona ) and I did this blog with the goal of improve my english and show the power of templates in C++.

Thus, if there is any part that is hard to understand feel free to advise me.

If you would like to deepen in any subject or talk about any specific just say it!