2010-03-03

When new features collide with old syntax

My C++ times are over. But Roker keeps on feeding me with input. And I could not let this one go by unnoticed.

To make it short, what do you think does the following program print?

#include <iostream>

struct X { };

class Y {
public:
    Y (const X& x) {
        std::cout << "fnord" << std::endl;
    }
};

int main() {
    Y d(X());
    return 0;
}

At first sight it looks like everything is ok. We know that references to temporaries must be const, which is the case here. So we think, d is going to be a Y object which was initilized with a temporary X object. In this case, the program should output "fnord".

But it doesn't. Instead it prints nothing. Mysterious you think? So did I. So I started debugging the issue while Roker was giggling. So, if d is not a Y object, what is it then? Let's ask the compiler and the C++ runtime:

#include <typeinfo>
//...
int main() {
    Y d(X());
    std::cout << typeid(d).name() << std::endl;
    return 0;
}

The program now prints "F1YPF1XvEE". Hmm, I can not decrypt this, can you? We need to demangle the name in order to see it's real C++ nature:

$ c++filt -t F1YPF1XvEE
Y ()(X (*)())

Who can decrypt this? If you can't, here is a howto on reading C type declarations, because the unfamiliar syntax for function types is a C heritage. And what does this type mean after all? It means, that d is a function which returns a Y object and expects a pointer to a function which returns a X object and expects nothing. Got it? If not, here is how I would write it, in case I would need such a type:

#include <iostream>
#include <typeinfo>

struct X { };
class Y { /*...*/ }; 

typedef X (Inner)();
typedef Y (Outer)(Inner);

int main()
{
    Outer d;
    std::cout << typeid(d).name() << std::endl;
    return 0;
}
This program prints Y ()(X (*)()) which is the type of d from the original program.

Ok, so now we know, what the program does. It declares a function. That's why there is no X object ever created. So, although the code could mean what we originally intended, the compiler chooses to understand something else, which is also possible due to the C syntax rules. Although I still don't know what function declarations inside other functions are good for, the compiler again prefers them. Anyway, what's the C way of removing ambiguity from syntax? Right, it's parantheses:

//...
int main()
{
    Y o((X()));
    std::cout << typeid(o).name() << std::endl;
    return 0;
}
Now, the program prints "fnord" and "1Y" which is the mangled name for the type Y.

To sum it up, this is yet another example for why I consider the C heritage to be a major source of trouble for C++ developers. The C syntax was never designed for temporary variables and combining both yields to what we have seen here: ambiguity, ugly workarounds and surprise and furstration for the developer. Don't get me wrong, though. I still think that C++ could hardly have been made better given the design goals which where taken those days. I truly have a love-hate for that language.


Posted by Alexander Bernauer | Permanent Link | Categories: C++

2009-11-09

C++ Pitfalls

As I do no C++ hacking at ETH I have not stumbled upon new input for this blog on my own. So I am very happy that Roker keeps on feeding me.

Today he pointed me at Cay Horstmann's list of C++ pitfalls. Some of the issues have already been covered here but some haven't. I recommend reading it to every C++ developer as this takes less time than learning it the hard way by debugging code. Have fun.

Concerning the iterator issues Cay provides a Safe STL implementation which is supposed to be used as a drop in replacement for the STL during development. It adds state and knowledge about the owner to iterators and uses this information to do runtime checks. If you accidently use your iterators in a wrong way you end up with a comprehensive runtime error instead of a segfault "deep in the bowels of STL code".

I haven't tested Safe STL. If anybody did feel free to send me your experience. I would be glad to blog about it.


Posted by Alexander Bernauer | Permanent Link | Categories: C++

2009-11-09

Catching Integer Overflows in C

Roker pointed me to an article about Catching Integer Overflows in C by Felix von Leitner. It covers some nasty pitfalls of integer arithmetic in C. My absolute favourite is that abs may return a negative number. Comforting to know that C++ inherited this behaviour....

Posted by Alexander Bernauer | Permanent Link | Categories: C++

2009-08-22

The Safe Bool Idiom

Operator overloading is considered a major C++ feature. But as so often with C++ this feature sometimes clashes with other language properties.

Roker pointed me to an article about the safe bool idiom where the author elaborates on why a simple operator bool is not satisfying if you want to be able to use an object within a bool context. Have fun...


Posted by Alexander Bernauer | Permanent Link | Categories: C++

2009-07-09

feature collision

One thing the C++ community is pretty proud of is that, in contrast to C, C++ supports const correctness. Turns out that const correctness clashes with STL containers.

Today Roker pointed me to a poor guy from #c++@freenode with the following problem:


#include <iostream>
#include <list>

typedef std::list<int> list_t;

list_t::const_iterator find_first_odd(const list_t & list)
{
  for (list_t::const_iterator i = list.begin(); i != list.end(); ++i)
  {
    if (*i % 2 != 0) return i;
  }

  return list.end();
}

int main()
{
  list_t list;
  list.push_back(2);
  list.push_back(3);
  list.push_back(4);

  list_t::const_iterator odd_i = find_first_odd(list);

  if (odd_i != list.end())
    // Error. Can not use const_iterator with 'std::list::erase'
    list.erase(odd_i);

  return 0;
}

For the sake of const correctness the function find_first_odd expects a constant reference to the list because it is not going to modify it. But as a result the function can only return a const_iterator, because there is no way to get a non-const iterator from the constant list.

So the caller of find_first_odd ends up with a const_iterator which is useless for modifying operations such as erase. And for him it is also impossible to turn the const_iterator into a non-const iterator. The two types have no relation to each other, so casting does not work. And there are no conversion functions in the STL either. So the only way out here is to sacrifice const correctness and pass a non-const reference to find_first_odd.

According to Roker nobody on #c++ had a better solution. And according to Scott Meyers' article Three Guidelines for Effective Iterator Usage there is none. const_iterators can not be easily transformed into iterators. What you can do is nasty and rather inefficient.

Here it comes:


template <class Container>
typename Container::iterator convert(typename Container::const_iterator in, Container& container)
{
    typename Container::iterator out(container.begin());
    std::advance(out, std::distance<typename Container::const_iterator>(out, in));
    return out;
}

If the container does not support random access, this conversion is O(n). Furthermore it can only be done, if the container object is at hand. Finally note, that this function violates const correctness, because the container object which is not altered by the function is not passed as const&. The reason is that the begin() function of a constant container returns a const_iterator which you can not convert into the output non-const iterator. In case you are confused, try this illustration.


Posted by Alexander Bernauer | Permanent Link | Categories: C++

2009-06-22

C++ truths

Since I am at the ETH in Zurich I haven't used C++. The reason is simply the fact that I haven't encountered any problem for which C++ is the tool of choice. That's why there has been almost no input to this category from me.

But there are others who blog about C++. So I will be satisfied with passively following the C++ topic and refering to them.

Today's featured C++ blog is C++ thruths from Sumant Tambe. Hmm, infinite regress, yummy. ;-)

Thank you Roker


Posted by Alexander Bernauer | Permanent Link | Categories: C++

2009-05-20

the longest suicide note in history

I am not alone with my estimation of C++ being a dead end. Today Roker pointed me to an article from Allan Kelly about the future of C++. Here is my favourite quote:

[the C++ 0x standard will be] the longest suicide note in history.

Other languages do better. Python 3.0 and Perl 6 are not downward compatible and in both cases I think this is a good idea. Why is this impossible for C++?


Posted by Alexander Bernauer | Permanent Link | Categories: C++

2009-03-30

git

Rico und ich haben beim Chaostreff Rheintal und dem net culture lab Dornbirn einen Vortrag über das Versionsverwaltungssystem git gehalten.

Links zu den Folien und den Aufzeichnungen gibt es hier.


Posted by Alexander Bernauer | Permanent Link | Categories: Vortrag

2009-02-20

moving on

I am a PhD student at ETH Zurich now. Besides other cool infrastructure the ETH provides blogs for associates. No doubt, I had to try out.

It's a WordPress, so I am leaving the nanoblogger paradigm of static page content. What I can get is interaction through reader comments and trackbacks. But I'm also a target for spam and crackers now. We'll see...

Anyway, here is copton's ETH blog. For the time being new posts will go there.


Posted by Alexander Bernauer | Permanent Link

2009-02-15

C Traps

Quite a time ago Andrew König has written a book about C traps and pitfalls. Today, Roker pointed me on an earlier technical report which was the basis of the book.

Although some things are different with C++, I recommend everybody to read at least the report. Otherwise you surely will stumble on those items sooner or later.


Posted by Alexander Bernauer | Permanent Link | Categories: C++
Stoppt die Vorratsdatenspeicherung! Jetzt klicken & handeln!Willst Du auch bei der Aktion teilnehmen? Hier findest Du alle relevanten Infos und Materialien: