Jonathan Boccara's blog

The Shortest Name There Is

Published June 18, 2019 - 0 Comments

underscore

The names with give to the objects in code are a fabulous tool to express our intentions for the next developer to read them. Especially with good names.

But in some cases, the name of an object becomes a burden. It happens when that object doesn’t make much sense in the first place, but for some reason we need to have it. We’ll see two examples of such cases in a moment.

In this sort of situation, the role of the object name is no longer to reveal information. Quite the opposite, to reflect the fact that you’d prefer the object not to exist, you’d like its name to be as discreet as possible, even to disappear if possible.

In those cases, what name to choose?

One way that we’re going to explore here is to choose the shortest name there is: an underscore. Indeed, you can’t do less than one character, and you can’t find less meaning than in an underscore. An underscore means nothing. If your object also means nothing, an underscore sounds like a fit name.

As a disclaimer, know that this is a controversial topic. The point of this article is to make you reflect on an original naming technique rather than presenting a conventional practice. If you agree or disagree with what follows, please don’t hesitate to leave a comment and explain your view on the topic.

Let’s see two examples where an underscore could be a candidate for a name.

#1 The capture

Imagine that you have a pretty big function, with a bunch of local variables. In legacy code, for example. It shouldn’t be like that, but that’s what life is sometimes.

Anyway right in the middle of that large function, lies a chunk of code that performs one of the responsibilities of the function. It uses the numerous local variables of the function extensively:

if (variable1 && !variable2)
{
    if (variable3 || variable4 > 0)
    {
        if (check(variable5) && isValid(variable6))
        {
            result = calculateOneWay(variable5, variable4, variable4 + variable6);
        }
        else
        {
            result = calculateAnotherWay(variable5, variable5, variable4, variable4);
        }
    }
}

It could be worth considering extracting that chunk of code, and packing it off to a dedicated function. To nudge you into that direction, let’s say that you’re asked to implement the exact same logic somewhere else in the codebase. Since you’re a good coder and a good person, you’re not going to duplicate that chunk of code. You have to extract it in its own function.

How to do this? One way would be to create a function and pass all the variables as arguments. But for the sake of the argument let’s say that all those variables are ints and bools (which is not such an unrealistic assumption in some cases):

int computeResult(bool variable1, bool variable2, bool variable3, int variable4, int variable5, int variable6)
{
   int result = 0;
   if (variable1 && !variable2)
   {
       if (variable3 || variable4 > 0)
       {
           if (check(variable5) && isValid(variable6))
           {
               result = calculateOneWay(variable5, variable4, variable4 + variable6);
           }
           else
           {
               result = calculateAnotherWay(variable5, variable5, variable4, variable4);
           }
       }
   }
   return result;
}

This is not a nice interface. It contains a number of arguments, and what’s more they all are of the same type or convertible to each other. That makes it unclear and prone to mixing up the order of the arguments.

One classical solution is to group all those arguments into one:

struct ComputationParameters
{
    bool variable1;
    bool variable2;
    bool variable3;
    int variable4;
    int variable5;
    int variable6;
};

And use the new structure ComputationParameters in the prototype of our function:

int computeResult(ComputationParameters const& computationParameters)
{
    int result = 0;
    if (computationParameters.variable1 && ! computationParameters.variable2)
    {
        if (computationParameters.variable3 || computationParameters.variable4 > 0)
        {
            if (check(computationParameters.variable5) && isValid(computationParameters.variable6))
            {
                result = calculateOneWay(computationParameters.variable5, computationParameters.variable4, computationParameters.variable4 + computationParameters.variable6);
            }
            else
            {
                result = calculateAnotherWay(computationParameters.variable5, computationParameters.variable5, computationParameters.variable4, computationParameters.variable4);
            }
        }
    }
    return result;
}

The function prototype is now more concise and robust. But what about the function body? It got polluted with visual noise, as computationParameters is now written all over the place!

The initial goal was to extract a chunk of code from the initial function and put it somewhere else. But with the additional visual noise, the above result doesn’t look like the initial code.

What would have been nice is to make a capture. Like lambdas do:

void f()
{
    int variable1 = 42;

    auto myLambda = [variable1]()
                    {
                         return variable1 + 2; // we use variable1 without additional visual noise
                    };
 
    // ...

}

But outside of lambdas, C++ doesn’t offer captures baked in the syntax of the language.

So how can we reduce the visual noise inside our function to make it look like a capture?

One way would be to re-initialize all the variables:

int computeResult(ComputationParameters const& computationParameters)
{
   auto const& variable1 = computationParameters.variable1;
   auto const& variable2 = computationParameters.variable2;
   auto const& variable3 = computationParameters.variable3;
   auto const& variable4 = computationParameters.variable4;
   auto const& variable5 = computationParameters.variable5;
   auto const& variable6 = computationParameters.variable6;

   if (variable1 && !variable2)
   {
       if (variable3 || variable4 > 0)
       {
           if (check(variable5) && isValid(variable6))
           {
               result = calculateOneWay(variable5, variable4, variable4 + variable6);
           }
           else
           {
               result = calculateAnotherWay(variable5, variable5, variable4, variable4);
           }
       }
   }
}

But another way would be to rename computationParameters in a shorter name. How would we rename it then? Remove vowels, as in cmpttnPrmtrs? Ew. Use a one-letter name, c? That would rise the question “what does ‘c’ stand for?”.

To emphasize the fact that this object is just a technical artefact to emulate capture outside of a lambda, we could name it with a zero-letter name: _. This is legal name in C++.

Our function becomes:

int computeResult(ComputationParameters const& computationParameters)
{
    auto const& _ = computationParameters; // shortening a name that doesn't matter, as it is here only to emulate a capture

    int result = 0;
    if (_.variable1 && ! _.variable2)
    {
        if (_.variable3 || _.variable4 > 0)
        {
            if (check(_.variable5) && isValid(_.variable6))
            {
                result = calculateOneWay(_.variable5, _.variable4, _.variable4 + _.variable6);
            }
            else
            {
                result = calculateAnotherWay(_.variable5, _.variable5, _.variable4, _.variable4);
            }
        }
    }
    return result;
}

Which is not identical to the initial code we took out of the big function, but getting closer.

#2 Function template partial specialization

It’s not the first time we see this name of one underscore. Last time we had encountered it was with function template partial specialization.

In C++ we can partially specialize class templates but not function templates. Partial specialization means providing an implementation to a template for a subset of cases that also depend on template parameters.

For example, to implement a template function that determines whether a type is a pointer, we’d like to write this:

template<typename T>
constexpr bool is_pointer()
{
    return false;
}

template<typename T>
constexpr bool is_pointer<T*>()
{
    return true;
}

But this is not legal C++. For some reason we have to go through a class (which we can partially specialize) and define a static function inside.

But how to name this static function? It has no meaning. It is there just to work around a constraint of the C++ language. In his CppCon talk Templates Normal Programming, Arthur O’Dwyer proposes to call it _:

template<typename T>
struct is_pointer_impl { static constexpr bool _() { return false; } };
 
template<typename T>
struct is_pointer_impl<T*> { static constexpr bool _() { return true; } };
 
template<typename T>
constexpr bool is_pointer(T const&)
{
    return is_pointer_impl<T>::_();
}

So an underscore can let you express in code that an object has no meaning in itself, and can alleviate the code from awkward naming.

However, underscores need to be used carefully. Most variables deserve a proper name to express your intentions in code, and if you do use underscore as a name, make sure that it is in a very limited scope as in the two above examples.

What is your opinion about naming objects or function with an underscore? It may look surprising the first time, but once your past the first surprise, would you use it? If not, why?

You may also like

Don't want to miss out ? Follow:   twitterlinkedinrss
Share this post!Facebooktwitterlinkedin