Thursday, December 25, 2008

More things to hate C++ for

Although, all major C++ flaws are  ingeniously classified in  Dark Side of C++ slides, even a minor ones can be very irritating. Here goes my favorite ones:
  • Dependencies - even if one adds a non-virtual method to class definition, all files that includes this header and all files that includes headers that includes this header and so on,  are  better to be rebuild. 
  • Too many keyword combination, which sometimes have no meaning. For example, how virtual inline methods are possible?
  • Template sem-auto instantiation. It takes some time to figure out whether one have to explicitly instantiate a template or compiler does this for you. ( And thank linker throw duplicating instances away, as if it have nothing else to do )

3 comments:

Константин said...

• Correct. Dependencies is one of the biggest C++ problems.
• virtual inline - what is strange here? Sometimes, when class is exactly known, compiler may call virtual function directly, not using vtable. In this case it can be inlined
• explicitly instantiate - I never needed to explicitly instantiate templates :)

P.S.
"Dark Side of C++" is a strange document. There are several good ideas, but other are at least strange.

"Dark Side of C++" idiotisms:
• Initializers done in order of declaration of fields in class, not written order
It is correct behaviour. Compiler make show warning, when order is different. Initialization in other order is an error, compilation error is unacceptable here

• auto_ptr is useless
Incorrect

• Iterators don’t know anything about the container, can’t detect errors
Incorrect. See debug iterators.

• For a vector, at() does bounds checking, but operator[] doesn’t
As designed (what is a problem?)

• delete[] would leak memory if object 2 of 10 threw an exception
It is ok. Correct cleanup is indeed impossible in this situation. Not C++ problem

• Exceptions in constructor don’t unwind the constructor itself
He does not know C++. Created constructed objects unwind.

• Does not even clean up local variables!
He does not know C++.

• Must do own cleanup
Must not. RAII

• OTOH: no other way to return failure, constructors are void
Wrong. There are some ways, like 2-phase initialization

• baz = foo->bar(3);
What did he want to say by these strange discussion?

• Almost nobody actually understands the error messages.
Incorrect. I do :)

• assert(s[s.size()] == 0); works if s is a const std::string, but
is undefined if it is not const.
This program is always incorrect

• Many statements can be skipped. Author tries to write in C++ as in C. Sometimes he does not know C++, sometimes he cannot think, sometimes he tries to apply not-C++ approach to C++.

Good dark side ideas:
• Errors may be cryptic. Correct. (for me never was a problem)
• Operator overloading may lead to unintuitive results. Correct. I try to avoid it. But it may be very convinient if used correctly.
• Writing good libraries is hard. Agree. Bycicles must die.
• You have to learn before C++-ing. Correct. But have to learn before writing in Haskell, Jave, Erlang, Lisp, ... too. Fucking language-writers, they do not think about programmers.
• Overload of ++a, a++ is an idiotism (but beleive me, you'll never need this knowledge)
• For templates, you have to substitute ”> >” for ”>>”. Very big issue :), compiler will tell you, and you'll fix it in 1 minute.
• operator[] adds a member to a map if it does not already exist. Design solution, discussable, sometimes convenient, sometimes not.
• Can’t tell what exceptions a library can throw. General answer, any. And this is discussable question, if exception specification is better.
• Writing C-style in C++ can lead to problems. Exactly! Do not write C-style in C++

malfet said...

Wow. Thanks for such a well thought comment.

But frankly, I still don't understand, how virtual inline methods are possible. Compiler has no way to know if method is going to re-implemented in derived classes. ( unless method is private, but private virtual methods are meaningless )

About explicit instantiation, at least with g++ one have to do it if all methods of the class are implemented in header file. And regarding templates: if not all template methods are implemented in .h file one have to explicitly instantiate it. ( See C++ FAQ 35.15 )

And I think "Dark Side of C++" is written in a provocative way for a purpose. But it points toward the right direction: quite often C++ are unjustly complex.

Константин said...

Ok, here example when inline virtual makes sense:

class Base
{
public:
virtual ~Base() {}
inline virtual void test() const;
};

void nonVirtualCall()
{
B b;
b.test(); // non-virtual call. Type is exactly known. May be inlined
}

void virtualCall( B& i_b )
{
i_b.test(); // virtual call. Not inlined.
}

"public virtual function" is widely used idiom in C++ for implementing "template method" pattern. Mechanisms of virtual functions and public/private/protected are orthogonal. So code below is valid:

class Base
{
public:
virtual ~Base() {}

void templateMethod()
{
if( action1() )
{
action2();
}
else
{
action3();
}
};

private:
virtual bool action1() = 0;
virtual void action2() = 0;
virtual void action3() = 0;
};

class Derived : public Base
{
private:
virtual bool action1() {}
virtual void action2() {}
virtual void action3() {}
};

Talking about template instantiation, I used another approach without explicit template instantiation. It has own advantages and disadvantages. I think, that sometimes C++ templates are ok, but very often they bring headache :(

// A.h

void f( const Vector3<double>& i_v );
void f( const Vector3<float>& i_v );

// A.cpp

template<typename T>
void fImpl( const Vector3<T>& i_v )
{
// bla-bla-bla
}

void f( const Vector3<double>& i_v )
{
fImpl<double>( i_v );
}

void f( const Vector3<float>& i_v )
{
fImpl<float>( i_v );
}