New codestyle with introduction of C++11

I suppose that coding conventions shall be updated after C++11 introduction. I’m going to write my proposal and rationale. Please, comment.

  • The macro NULL or integer constant 0 should not be used for null pointers, nullptr is used instead. Replace 0-s in old code. (VA)
    • Why: Best way to declare null pointer.
  • The keyword override shall be used wherever possible (?? except destructors ??). Update old codebase. (VA)
    • Why: Prevent errors.
  • The keyword final shall not be used except rare specific cases (e.g. virtual interface wrapping)
    • Why: It makes code reuse hard. Think twice before sealing the interface.
  • The using shall be used instead of typedef. Replace typedefs in old code. (VA)
    • Why: More readable.
  • The {} constructor shall never be used.
    • Why: Keep code consistent. It gives too few benefit to update the whole codebase.
      • IMO, the = initialization is the most readable.
  • The range-based for loop shall be used wherever possible. Update old codebase when possible. (VA)
    • Why: More readable.
  • auto shall not be used for one-word types and simple templates.
    • Why: Keep code readablilty when type name is short enough. Keep code consistent.
  • auto shall be used for unknown types in templates (avoid typename and decltype) and long nested types (aka HashMap<String, int>::KeyValue). ?? Shall other cases be here ?? (VA)
  • Inplace member initialization shall be used wherever possible. Update old codebase.
    • Why: Easier to read, harder to mistake.

I’ve marked with (VA) tag items that could be done automatically via Visual Assist.

2 Likes

IMO the = is for assignments and {} are for initialization, since the latter provides more type safety.
Also the {} can both be used in the constructor’s head as well as in the body of a function. Adding consistency.

1 Like

That’s why I sometimes dislike C++

Adding consistency

You may notice that items in the list that require updating old codebase are automatized, except inplace member initialization that I am ready to upgrade on my own.

If one is ready to port all Urho codebase to such constructor, it will be consistent. If we just start to use new style, we end up in codestyle mess. Two different styles is always worse than one.

IMO the = is for assignments and {} are for initialization, since the latter provides more type safety.

That’s holywarable whether the type safety costs it.
IMO objective decision isn’t posible here

here is my point of view...

int some_name = 10; and some_name = 10; have similar meaning and semantics. We assign here some value to some name. Here is very distinguishable " = " pattern that is used only for assignments (including construction). The only difference here is that new name is created in the first case, while old name is used in the second one.

Then…

int some_name { 10 } and some_name = 10 have too different semantic for the similar action. The " { } " pattern is much less distinguishable than " = " because it’s similar to other language constructions.

Choosing between ghost type safety and visual nicety, I prefer the second one

Consider
Class::Class(),
a{1},
b{2}
{
    const int c{ a + b };
    a = 4;
    b = c;
}
VS
Class::Class(),
a(1),
b(2)
{
    const int c = a + b;
    a = 4;
    b = c;
}

I’m not suggesting multiple styles. And if you change the rules, past results are bound to break the newly instated ones.

Consider 1 VS 2

Ok, there is probably not so big difference.
I actually don’t care very much. Hidden part was just IMO.

I’m not suggesting multiple styles.

The only thing I really care about is to have single style in the end.

If you suggest codestyle change that breaks code consistency, be ready to fix unconsistency on your own.

I am ready to upgrade old codebase on my own for each item in the list above that is broken by existing codebase

As for me, this is true only for local non-static variables. Static initialization happens while your program is uploading to memory, while local variable initializations means just “put that value into memory on that address in runtime”, the same as variable assignment.

So, here I partially agree with Modanung. {} initialization is reasonable for local non-static initialization, but it could be hard to update all codebase with it.

Agreed. Still, I would argue the first of the two code examples to be more semantically consistent.

On updating the code base:
The bright side of this is that it’s easy to see what part of the engine has been c++11-ified (by hand and eye) and which parts are ‘old’.

Note: In general you should never say "never use feature X", it usually means you just don’t know what it’s useful for.

0 isn’t guaranteed to represent null pointer on all systems (ex. some embedded systems use 0 as valid memory address)

regarding override I want to mention useful use case:
when declaring a virtual function in a base class, it’s already implicitly declared virtual in derived classes, so writing virtual for derived class functions which is completely redundant, and can lead to bugs - ex. if the base class renames the virtual function, the derived class will still use virtual function when it isn’t needed, degrading performance.
override becomes handy with virtual functions when you want to make sure you’re overriding the base class’s virtual function.

struct A {
    virtual f();
}

struct B : A {
    virtual f();
}

Should be replaced with:

struct A {
    virtual f();
}

struct B : A {
    f() override; //already implicitly virtual
}

Urho does use virtual for derived class functions (ex. SoundSource3D::Update)

using can be templated (alias template), typedef cant.
typedef is obsolete.

list initialization ({}) is very handy and has useful use cases. examples:

void f(Vector3 x) {
//...
}
f(Vector3(1,2,3)); // without list initialization
f({1,2,3}) // with list initialization

// without list initialization
vector<int> v;
v.emplace_back(1);
v.emplace_back(2);
//...

// with list initialization
vector<int> v{1, 2,/*...*/};
1 Like

How this is related to nullptr codestyle proposal?

I definetely dislike implicit virtuals because leading keyword virtual is much noticeable than trailing override, maybe because of alignment.
C++ would be much better if override and final were used instead of virtual in the beginning.
This is impossible, so I’d like to avoid implicit virtual at all.

I completely agree here, but I don’t want to end up with mixed ctors styles. If one write a script for automatic upgrade, it wold be great.

TODO: Update proposal somehow

Just showing how NULL defines are less correct.

You can have a derived class that defines a new virtual function (not override).
You can override non-virtual function.
(point is that you need virtual)

technically list initialization isn’t exactly the same as using regular constructor syntax since it can be overloaded, so it isn’t just style. example:

std::vector v(2);
// not the same as:
std::vector v{2};

Regular constructors and list initialization have different use cases. Use whichever is better for your case.

Huh, I’ve completely forgotten about this case…

My initial proposal was to use Class object(params); for constructors and Class object = anotherObject; for copy constructors for consistency with old code.

Then, brace constructor can be used to avoid writing class name at all, e.g. SetPosition({ 1, 2, 3 });. I haven’t thought about this case at all…

Then, I prefer vector<int> v = {1, 2, 3}; over vector<int> v{1, 2, 3}; because it make construction and initialization with elements sematically different.

The beauty of Urho3D is it compiles on all platforms. Please don’t spoil it by adding C++11 filth. This is why we have our own containers and pointers and memory management.

The beauty of Urho3D is it compiles on all platforms

How does migration to C++11 breaks “all platforms” compilation? I think that there are C++11-compatible compilers for any more or less modern platform.

Please don’t spoil it by adding C++11 filth

Urho 1.7 will stay C++98 compatible forever.
Why do you want to use 20-years-old language with its unsafeties and limitations when we can drop’em with zero cost?

Do you know that now Urho heavily rely on undefined behavior?
We just hope that these hacks won’t suddenly explode sometimes and somewhere.
And this can be fixed only with power of C++11.

2 Likes

I am all for moving to C++11. Just please keep the Urho Variant, Containers and pointers and memory management. It is simple and powerful and easy to understand.

Don’t worry, nobody is goind to break old functionality.

However, I have some ideas about Urho.Container, Variant and Serializable :upside_down_face:

This sounds pretty scary: Can you please provide an example and eventually how would C++11 features help?

I know only one place. URHO3D_ATTRIBUTE uses offsetof for non-POD types, this may accidently explode if use multiple inheritance with serializables. I’m going to use lambdas+function accessors here and retire “offset” attributes completely.

Variant is also pretty scary but probably not UB. I’ll try to rewrite it using C++11 non-pod unions.

2 Likes

The goal is to keep the engine code consistent. Either you completely switch to C++11 or you dont. Usually having two code styles in the same project will make a mess. And we all know that STL is the worst possible thing for games. So I assume you wont be using STL code in the engine. So that makes it pointless to port the engine to use C++11. These are just my thoughts :smile:

Then, my goal is the same. This topic was created because of that concern.

I don’t really understand how the second sentence is derived from the first one.
I am goind to use C++11 but I am not goind to use STL.