Home
Fractals
Tutorials
Books
My blog
My LinkedIn Profile

BOOKS i'm reading

Napoleon Hill Keys to Success: The 17 Principles of Personal Achievement, Napoleon Hill, ISBN: 978-0452272811
The 4-Hour Workweek: Escape 9-5, Live Anywhere, and Join the New Rich (Expanded and Updated), Timothy Ferriss, ISBN: 978-0307465351
The Fountainhead, Ayn Rand, ISBN: 0452273331
Web Hosting Canada

mailto:olivier@olivierlanglois.net

Categories: C++, tutorials

09/01/07

Permalink 01:17:33 pm, by lano1106, 322 words, 19537 views   English (CA)
Categories: C++

What is the C++ SFINAE principle ?

This is an acronym that means "substitution-failure-is-not-an-error" and it is used in the context of template functions overloading. When the compiler evaluates each overloaded template functions, it will not emit an error if by using the template parameter on one of the potential candidate function would generate an error. The compiler will just discard that function from the list of potential candidates. It is a powerful construction that allows metaprogramming as you can evaluate expressions at compile-time. Here is an example from the book C++ templates:

template<typename T>
class IsClassT {
  private:
    typedef char One;
    typedef struct { char a[2]; } Two;
    template<typename C> static One test(int C::*);
    // Will be chosen if T is anything except a class.
    template<typename C> static Two test(...);
  public:
    enum { Yes = sizeof(IsClassT<T>::template test<T>(0)) == 1 };
    enum { No = !Yes };
};

If T is a class, IsClassT<T>::Yes will be true because the first test() function will be chosen by the compiler. From the C++ templates book:

overload resolution prefers the conversion from zero to a null pointer constant over binding an argument to an ellipsis parameter (ellipsis parameters are the weakest kind of binding from an overload resolution perspective).

There is one subtle detail that is important enough to mention it. It is the usage of the template keyword at the Yes enumeration declaration. The reason behind the need for using the keyword is similar to why you sometime need to use the keyword typename.

It is because of template specialization, the compiler has no clue what the dependent name "test" is. Without explicit indication with the keyword template, it is interpreting "<" and ">" as "greater than" and "lower than" logical operators.

Maybe you were already using the principle without knowing its name but now next time you see SFINAE in a C++ forum, you will know what they are talking about.

08/29/07

Permalink 09:46:41 pm, by lano1106, 369 words, 2458 views   English (CA)
Categories: C++

Why STL containers do not merge front() and pop_front() functions into one function ?

This one has annoyed me for a while until I found the answer as pre STL stack classes used to have a pop() function that was popping the element and returning it to the user. I was finding it conveniant because everytime I wanted to access the top element, I also wanted to pop out the element of the stack so why STL containers is forcing its users to perform this common operation with 2 operations?

In 'The C++ Programming Language' book, Bjarne Stroustrup has this to say about pop_back:

Note that pop_back() does not return a value. It just pops, and if we want to know what was on the top of the stack before pop, we must look. This happens not to be my favorite style of stack, but it's arguably more efficient and it's the standard.

I wish that he took the time to explain his position about why it is more efficient for pop_back() to not return the element but I only understand why it was done like that by reading the book Exceptionnal C++ from Herb Sutter.

If you are using a STL container to store pointers, the performance argument disapear but if you store objects, all front() is returning is a reference to the top element and if pop_front() would return the top element, it would be by value and the copy constructor and the assignement operator would be involved. This is the performance argument that by having to call these 2 functions, it is adding overhead.

However, I suspect that this is not the main reason to have separated the 2 operations. Probably that the main reason is for exception safety garantee that the STL containers provide. Since, one requirement that STL impose on its users that contained objects destructors cannot throw exceptions, pop_front() can provide a no throw garantee. It could not if it had to return the top element because if the copy constructor or the assignment operator was throwing an exception, the element would be lost forever. In contrast, when an exception is thrown while assigning front() result, if you can cope with the exception, the element is not lost because it is still in the container.

08/26/07

Permalink 08:39:43 pm, by lano1106, 327 words, 5889 views   English (CA)
Categories: C++

Using integral constant template parameters

One usage of them commonly demonstrated in C++ Templates textbooks such as C++ Templates is when you want to control the maximum capacity of a container like this:

template< typename T, int MAXSIZE >
class stack
{
...
   private:
   T m_array[MAXSIZE];
};

I have found another very useful property of integral constant template parameters. You can define a class template with an integral constant template parameter and not use it at all in the declaration:

template<int i>
class Unique
{
  public:
  static int var;
};

template<int i>
int Unique<i>::var;

The property of this template is that a distinct var variable will be created for each instantiation of the template:

Unique<1234>::var = 1;
Unique<0>::var = 2;

and so on. By itself, it is more or less useful but imagine that you are dealing with a function registering a callback function that does not let you provide a parameter that will be passed to the callback function but your callback needs to access variables and you must provide more than 1 callback function and each of them needs to access its own set of variables. You could do it by hand by writing x different callbacks and create x sets of variables with different names but by doing that, you would be polluting your namespace and that would be a very long task for nothing. Instead, a class template with an integral constant parameter is the right tool for this situation:

template<int i>
class CB
{
  public:
  static void SetCBParams(int x1, int x2)
  { m_x1 = x1; m_x2 = x2; }
  static void CBFunc();
  private:
  static int m_x1;
  static int m_x2;
};

template<int i>
int CB<i>::m_x1;
template<int i>
int CB<i>::m_x2;

and then

CB<1>::SetParam(1,2);
registerCB(&CB<1>::CBFunc);
CB<2>::SetParam(3,4);
registerCB(&CB<2>::CBFunc);
...

08/22/07

Permalink 10:07:44 pm, by lano1106, 426 words, 4146 views   English (CA)
Categories: C++

explicit destructor call in templates

Sometimes efficient memory management is needed and to accommodate that need, the C++ language provides the placement new operator to let the user specify the location where an object is going to be instantiated. When the placement new operator is used, the user must then explicitly call the destructor.

This is all fine but I have been wondering what happens when the placement new operator is used in conjunction with templates such as in the std::vector container and when the template parameter is a scalar built-in type that obviously does not have a destructor.

I have found out that the C++ standard committee envisaged the situation and has included specification of what a compiler must do in such situation. I do not have a copy of the standard text but the section 5.2.4 (Pseudo destructor call) states that the expression p->~T() becomes a pseudo destructor call for a built-in pointer type, which does nothing.

Here is a small program that I have successfully compiled to verify that pseudo destruction works fine:

#include <iostream>

// The header file containing the declaration of the placement new operator
// according to 'The C++ Programming Language' book (section 10.4.11) but it
// compiles fine without. It must be because it is included from <iostream>
#include <new>

class TestClass
{
    public:
    TestClass() : m_x(3) {}
    ~TestClass()
    { std::cout << "TestClass destructor called"
                << std::endl; }
    void print() const
    { std::cout << "Hello " << m_x
                << std::endl; }
    private:
    int m_x;
};

template<typename T>
class PseudoTest
{
    public:
    PseudoTest() { m_pObj = new(m_buf) T; }
    ~PseudoTest() { m_pObj->~T(); }
    T *m_pObj;
    private:
    char m_buf[sizeof(T)];
};

int main(int argc, char *argv[])
{
    // First test the int case
    {
        PseudoTest<int> a;
        *a.m_pObj = 777;
        std::cout << *a.m_pObj << std::endl;
    }
    // Then if something prints out, that means that the 
    // int pseudo-destructor has been called successfully.
    {
        PseudoTest<TestClass> b;
        b.m_pObj->print();
    }
    return 0;
}

One final word, note that I have used the placement new operator on a char buffer located on the stack and, according to Herb Sutter, this is a very bad thing because this generates non portable code. He explains why in his book Exceptionnal C++ (item 30: The "Fast Pimpl" Idiom) and then presents the right way to do it but since my test program compiles and runs fine, my evil code is will be fine for the demonstration purpose of this blog entry.

08/19/07

Permalink 01:04:58 pm, by lano1106, 426 words, 6385 views   English (CA)
Categories: C++

Dependent names and Two-Phase Lookup

Question:

What is wrong in this code from the book 'The C++ Programming Language', section 3.7.2? and why does MSVC accept it and not GCC?

// Range checking vector 
template< class T > class Vec : public std::vector< T >
{ 
public: 
  Vec () : std::vector< T > () { } 
  Vec (int s) : std::vector< T > (s) { } 

  // Range checking by using the at () method,
  // which throws out_of_range 
  T& operator[] (int i) { return at (i); } 
  const T& operator[] (int i) const { return at (i); } 
};

GCC reports this error:

since at() doesn't depend on any template parameters it should be available

Answer:

A compliant compiler will partially validate the template code that does not depend on the template parameters and complete the validation at the template instantiation point. It is that way because code depending on template parameter could have a totally different meaning if between the template declaration and the Point of instantiation (POI) there is a template specialization.

When you call a member function from a template base class, the way it is written is making the name nondependent. To fix the code, 2 options are available. You can either write:

  • this->at(i)
  • std::vector<T>::at(i)

1. is preferred as it will work as expected if the function called is virtual.

If VC++ does not complain, it is because it simply does not support the two phase lookup and compile templates only at instantiation point. GCC is a better compiler in that aspect because it complies with the C++ standard by implementing the Two-Phase lookup. You can easily fool VC++ with the following code:

template <class T> 
class Test 
{ 
public: 
   void Test() { foo(); } 
private: 
   T m_a; 
} 

// Non dependent function declared after
// template declaration
int foo();

int main(int argc, char *argv[]) 
{ 
    Test<int> testObj; 
    testObj.Test(); 
    return 0; 
}

GCC will generate an error when encountering the template complaining that foo() is not declared but VC++ will happily accept this code. Now that this is explained, note that except annoyance when porting code from VC++ to GCC or any other standard compliant compiler, the only consequence for MSVC of not being totally C++ standard compliant by not supporting the Two-Phase lookup is that if there are errors in a template, the compiler will delay their report at its instantiation instead of reporting them as soon as the template is encountered.

If you would like to explore further Dependant base classes, Point of Instantiation (POI) and Two-Phase lookup, I highly recommend to consult the book C++ Templates.

<< Previous Page :: Next Page >>

Olivier Langlois's blog

I want you to find in this blog informations about C++ programming that I had a hard time to find in the first place on the web.

< Previous | Next >

May 2024
Sun Mon Tue Wed Thu Fri Sat
 << <   > >>
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  

Search

Custom Search

Misc

XML Feeds

What is RSS?

Who's Online?

  • Guest Users: 3

powered by
b2evolution