Showing posts with label coding style. Show all posts
Showing posts with label coding style. Show all posts

2010-10-15

Ruby is not good at comments

Problem

Imagine that you are hard-coding some long list into your Ruby code and you need to
  • wrap the long line into multiple lines
  • comment on some items
Let's look at the 1st goal (imagine a lot longer list)!
@@import_fields = [ \
    "Name" \
    , "Organization" \
    , "Role" \
    , "Internal ID" \
];
Pretty ugly, because the statement separator is the newline and you should escape it. But doable. Let's move on!

Completing the second goal is not possible. The only commenting option is the line-commenting which is starts with the # sign and ends when the line ends. If you place the comment before the backslash (\), your interpreter will not detect the newline escaper backslash. If you place the comment after it, then it stops being a newline escaper.

Nonexistent solutions

  • The Ruby guys failed to add statement separators / terminators to the language and that remains this way. I think the intention was to make life easier, but I believe they made it harder. Someone someday may enlighten me.
  • They should introduce block comments. It would be backward compatible and would make the World a better place.
  • Or they should detect the newline escaping after the # sign.

Workaround

@@import_fields = []
@@import_fields << "Name"
@@import_fields << "Organization" # Some clever saying about this field
@@import_fields << "Role"
@@import_fields << "Internal ID" # And some eternal wisdom about this

If you know a better way, don't hesitate to tell me!

2010-10-13

Indent with TABs, format with spaces

TABs vs spaces

1st: Visible with

They say that TABs are rendered differently by different softwares and devices. I cannot accept that. Read on.

UNICODE 2.0 released in year 1996. In around 2001 people wanted to kill me because I used UTF-8 characters on IRC, and they argued that their [not so good] software cannot render them correctly. I ignored them. Their [not so good] software became advanced, their minds became open.

Editors and devices where the visible width of TAB characters cannot be adjusted are not so good. TAB is not a new invention at all.

2nd: Formatting

TABs are not for formatting. They are only for indenting.

int·main()
{
→   printf("Hello·%s!\n",·//·I·like·to·move·it,
→   ·······"World");······//·move·it.
→   return 0;
}

If you want to graphically align something then you should use SPACES. If you are expressing the depth of control, then you should use TAB. The above code will maintain it's visual, see:

int·main()
{
→   {·//·Ad-hoc·block·begin
→   →   printf("Hello·%s!\n",·//·I·like·to·move·it,
→   →   ·······"World");······//·move·it.
→   →   return·0;
→   }·//·Ad-hoc·block·end
}

3rd: Freedom

See what happens when visible with of TAB is set to 8, because someone may like it that way:

int·main()
{
→       {·//·Ad-hoc·block·begin
→       →       printf("Hello·%s!\n",·//·I·like·to·move·it,
→       →       ·······"World");······//·move·it.
→       →       return·0;
→       }·//·Ad-hoc·block·end
}

The hairdo is still tight.

What if someone's favorite visual TAB size is 2?

int·main()
{
→ {·//·Ad-hoc·block·begin
→ → printf("Hello·%s!\n",·//·I·like·to·move·it,
→ → ·······"World");······//·move·it.
→ → return·0;
→ }·//·Ad-hoc·block·end
}

The hairdo is still tight.

4th: Symbolism

From my heart: http://www.mail-archive.com/vim_use@googlegroups.com/msg18895.html

Favorite part: ... tabs for leading indent, spaces everywhere else. I prefer this strategy for the same reason I prefer to use symbolic names for constants, rather than embedding numeric literals throughout my source code ...

2010-06-07

C++: Class vs Struct (clarification)

Classes and structures are the same thing in C++ except that the struct's default visibility is public, the class' is private. To highlight the equivalence: struct can have member functions, constructors, overloaded operators, virtual member functions, private members. Classes can have public data members. It is really just about the default visibility of members.

Let's see equivalent examples, let's talk about the philosophy later...

struct Vector {
    double x,y;
};

class Vector {
public:
    double x,y;
};

and

class Vector {
public:
    Vector(double x_, double _y);
    Vector& SetXY(double x_,double y_);
    double GetX() const;
    double GetY() const;
    //...
private:
    double x,y;
};

struct Vector {
    Vector(double x_, double _y);
    Vector& SetXY(double x_,double y_);
    double GetX() const;
    double GetY() const;
    //...
private:
    double x,y;
};

In my opinion class's default visibility is not very useful because we used to start the class definition with the public members to ease the interpretation of it for the users of our class.

The big difference is in the intent of the declaration. If we want to create a composite behavior-less data unit that is passed around, then we should use the struct keyword so the reader will know what's going on. If you want to create an object which fulfills an abstract concept and has a behavior, then you should define a class.

IMPORTANT: If you use class you should really take care about the basic requirements like hiding data members (making them private or protected in order to maintain principle of local responsibility), providing type-invariant safe public methods, disabling copy semantics if not needed explicitly, and so on ...

2010-05-03

C++: Why initializer list?

class Parent;

class Child {
public:
    Child(
        Parent& parent_
        , const std::string& name_
        , const Color& eyeColor_
    )
    : parent(parent_)
    , name(name_)
    , eyeColor(eyeColor_)
    , money(0,"EUR")
    { }
    Parent& GetParent(){ return parent; }
    std::string GetName() const { return name; }
    Money GetMoney() const { return money; );
    Money AdjustMoney(int diff)
    { money += diff; return money; }
private:
    Parent &parent;
    const std::string name;
    Color eyeColor;
    Money money;
};
The initializer list in the above is this:
: parent(parent_)
, name(name_)
, eyeColor(eyeColor_)
, money(0,"EUR")
  • because of initializing references.

    References are like pointers with two restrictions. Firstly: You (if you are sane) point only to one object with a reference, you never point to an array. Secondly: Once the location is pointed you will not point elsewhere. This "finally" sounding behavior is perfectly good when we want to point one's (abstract) parent.

    If you want the language to force you to keep the second rule and still want your compiler in this millennium then you have to realize, that the initializer must be done before the begin ({) sign appears with it's endless possibilities.

  • because of initializing const members.

    Same as the above, but we are not talking about making up our mind early about a location but about a value. See the name member

  • because of initializing an aggregated object which do not have default constructor.

    Let's imagine the Color class which has only the copy constructor and that one: Color::Color(unsigned char r, unsigned char g, unsigned char b)! This class does not have default value / state by design, so it cannot be constructed without some instructions. See the eyeColor member.

  • because of speed

    You begin your program (constructor body) at the '{' sign. At that point all of your variables must be in a valid state. So if you give value to your members after that point you will waste processor time. One construction and one assignment, instead of simply one construction. See the money member.

    In most of my codes the constructors' body used to be empty. If we want to work in that style we will have to have handy constructors for our classes, which is not a bad thing at all.

Important: The execution of the initializer list is in the order of the data members because that order means the construction order and the reverse of that means the destruction order. If it wouldn't be like that then the program would have to remember the construction order and apply the reverse of it upon destruction. That would waste resources. So the rule came...

... and don't worry about hidden errors! Turn on the warnings in your compiler and take the advices and notes seriously! Seriously like Treat warnings as errors compiler option.

2010-04-26

Yes, it is C. The "const" keyword

The const qualifier can have multiple useful meanings depending on the context.
  • In parameter list
    size_t strlen(const char *s);
    The programmer of strlen() promises you that he/she will not alter the content pointed by s. It is good for you: You do not have to look up the manual for that bit of information. It tells more than you may think for the first time. It is good for the implementor of strlen(). He cannot accidentally write to the area pointed by s.

  • Storing partial results as read-only variables.
    const double aplusb = a + b;
    const double amulb = a * b;
    return 2*amulb/aplusb; // harmonic mean
    
    It helps avoiding accidents and ease interpretation by humans.

  • Pointing to literals
    const char *appname = "Hello World!";
    
    If you turn on the displaying of warnings in your compiler then you will see that pointing to a literal with non-const pointer is discouraged. It also helps not making bugs.
Note: It was borrowed from C++. In C++ you have to use initializer list in the constructor when you have const (or reference type) data members... A little bit different topic.

Kevlin Henney: C++ Stylistics


(Google Talks)

Feel free to watch multiple times.

Exit strategy: Single exit point is less readable

Single exit point produces the pyramid of code syndrome.

bool solve2
    (
        double a, double b, double c
        , double& x1, double& x2
    )
{
    bool ret = true;
    if(a!=0) {
        double det = b*b - 4*a*c;
        if(det>=0) {
            ret = true;
            det = sqrt(det);
            double p2a = 1 / (2*a);
            x1 = (-b - det) * p2a;
            x2 = (-b + det) * p2a;
            // The top of the code pyramid
        } else
            ret = false;
    } else
        ret = false;
    return ret;
}

Using early returns helps keeping the error handing and productive code apart.

bool solve2
    (
        double a, double b, double c
        , double& x1, double& x2
    )
{
    // Checking for trivial errors and trivial solutions
    if(a==0)
        return false;
    double det = b*b - 4*a*c;
    if(det<0)
        return false;
    // Main part without pyramid
    det = sqrt(det);
    double p2a = 1 / (2*a);
    x1 = (-b - det) * p2a;
    x2 = (-b + det) * p2a;
    return true;
}

Note: This function solves the quadratic equation (a * x^2 + b * x + c = 0).

Note: You have to be careful with the multiple exit point style if you are acquiring resources (opening files, allocating memory, ...), because it can lead to leaks. Avoiding these kind of leaks is simpler in C++ than in most languages, because the lifetime of acquisitions can be bound to scope.

Yes, it is C. Smaller scope better scope

Once upon a time there lived a paradigm (sorry, that is an ugly word, but I don't know better) which told the programmer to declare all the variables at the beginning of the function. This paradigm was enforced by the compiler in Pascal, C and many other languages. You can guess. It was easier to write compilers that way. Then came the new winds with C++ (maybe it wasn't the first) which introduced RAII (Resource acquisition is initialization) which gave us three brand new concepts at once.
  • Variable scoping
  • Lifecycle management by scope (calling destructor when execution reaches the end of it's scope)
  • Bounding lifecycle of a variable to an object
Many may not know but since C99 the first is also available in C. A trivial example of it is the in-place declaration of iterator in for.
for(int i=0; i<10; ++i){ printf("%d^2 = %d\n",i,i*i); }
The new paradigm (woof) is that a variable should have the smallest scope possible to ease reading and interpretation of code (by humans).

Next: Exit strategy: Single exit point can be easily bad.