Evgenii Legotckoi
Evgenii LegotckoiAug. 6, 2018, 2:52 a.m.

The idiom RAII and the principle of structured programming that a function must have one entry point and one exit point

Content

The world of programming in C ++ in the new standards allows us to get up a variety of things, thanks to which we can safely abandon some old statements or principles, or simply flexibly approach these principles.

I would like to outline my view on the work of the RAII idiom and the C ++ 11 standard with respect to one established principle whose authorship is attributed to Edsger Dijkstra :

"The module (in this case the function) must have only one entry point and only one exit point"

For this principle, multiple returns in a function / method are contrary to the principles of structured programming for the following reasons:

  • the complexity of debugging code with the use of multiple returns returns increases with the number of these same return, that is, you never know when the function or method of an object just exited.
    the complexity of code support, when all the points of the call are not visible at the initial look at the function. Also, it is not known whether the code added to the end of the function will execute or not, especially if the program logic should always execute this code. That is, in the case of multiple returns, you will have to implement this code before every call to the return operator.

But modern C ++ has already changed significantly since the times of Dijkstra and the means of the latest C ++ standards allow to bypass or significantly level out the influence of the reasons that caused the formulation of the principles of structured programming.


One of these tools in C++ can be the RAII idiom, lambda function , and std::function from the standard library.

RAII (Resource Acquisition Is Initialization) - the program idiom of object-oriented programming, the meaning of which is that with the help of certain software mechanisms the obtaining of a certain resource is inseparably combined with initialization, and the release is with the destruction of the object.

Thus, thanks to RAII and lambda functions, we can get around some problems with multiple return, in particular with the fact that some code should always be called at the end of the function, regardless of the rest of the function code logic. But this is closer to the end of the article.

And now let's look at the pros and cons of using multiple return.

The first reason is that the complexity of debugging the program code increases with multiple return. But at the same time, already in 2018, modern debuggers allow you to define using breakpoints where the function came from, and the presence of multiple returns also allows you to significantly reduce the nesting of the code when using the if else constructs. Thus, we can get a more compact program code, which only will benefit from an understanding of what the function does, despite the presence of multiple return.

Consider the example

There are two functions that are called in another main function and the algorithm of the main function is constructed from the result of the work of those first two functions.

bool exampleFunction_1();
bool exampleFunction_2();

The main function, written according to the principles of structured programming

int examlpeFunctionMain()
{
    int result = 0;

    if (exampleFunction_1())
    {
        result = 1;
    }
    else if (exampleFunction_2())
    {
        result = 2;
    }

    return result;
}

If there were more such functions then the nesting of the if else constructs could increase significantly, which would not benefit the program code.

Therefore, we rewrite this code to use multiple return.

int examlpeFunctionMain()
{
    if (exampleFunction_1()) return 1;
    if (exampleFunction_2()) return 2;
    return 0;
}

The code has become much more compact and obvious, which is much clearer. That is, the use of multiple exit points from a function may allow the code to be improved on the contrary, rather than complicate it.

And now we give arguments that one exit point will be better if it is required that a certain program code at the end of the function is always called, and if you use several exit points, you need to duplicate this code and add it before all exit points. For example, this code can be the logging of the result of the function.

Let us consider an example of such an argument.

int examlpeFunctionMain()
{
    int result = 0;

    if (exampleFunction_1())
    {
        result = 1;
    }
    else if (exampleFunction_2())
    {
        result = 2;
    }

    std::cout << "Logging result " << result << std::endl;
    return result;
}

At the end of the above function, there is some code that emulates the logging. Then, if there are several breakpoints, this code will have to be duplicated, and our previous beautiful variant will become such ugly.

int examlpeFunctionMain()
{
    if (exampleFunction_1())
    {
        std::cout << "Logging result " << 1 << std::endl;
        return 1;
    }

    if (exampleFunction_2())
    {
        std::cout << "Logging result " << 2 << std::endl;
        return 2;
    }

    std::cout << "Logging result " << 0 << std::endl;
    return 0;
}

As you can see here, not only is the number of rows increased by one, so there was also the possibility of making a mistake when copying, if you forget to change the digit in the output of the log.

Such an argument for the presence of only one exit point from the function becomes quite reasonable.

But now I suggest that we turn to the modern possibilities of the C ++ programming language.

The idiom RAII implies that when you destroy an object in the destructor, you can free up memory, and also execute some program code. Such code can be the execution of a logging code. But we do not have any such objects? Yes, at the moment, no, but I suggest writing a class for using such an object.

This will be a sample ScopExit class. Let us consider it below.

#ifndef SCOPEEXIT_H
#define SCOPEEXIT_H

#include <functional>

class ScopeExit
{
public:
    template<typename T>
    explicit inline ScopeExit(T&& onScopeExitFunction) :
        m_onScopeExitFunction(std::forward<T>(onScopeExitFunction))
    {
    }

    inline ~ScopeExit()
    {
        m_onScopeExitFunction();
    }

private:
    std::function<void()> m_onScopeExitFunction;
};

#endif // SCOPEEXIT_H

The class has a private field std::function , this field will be responsible for storing the method we need, which will execute the code at the end of the function.

In the class constructor, this field is initialized by the passed template argument, which can be a functor or a lambda function.

In the class destructor, this function is called. That is, when the object is destroyed, a function will be called, which we place in the object of this class when it is created.

Using this class, you can rewrite the above code as follows:

int examlpeFunctionMain()
{
    int result = 0;
    ScopeExit scopeExit([&result](){ std::cout << "Logging result " << result << std::endl; });

    if (exampleFunction_1()) return (result = 1);
    if (exampleFunction_2()) return (result = 2);
    return 0;
}

The meaning of this code is that there is a variable result, which will store the code with which the function will end.

Next, a class object is created for ScopeExit, which at the end of the method will be destroyed and will cause the lambda in the destructor.

This lambda is passed as an argument to the constructor of the ScopeExit class. In this case, the lambda grabs the variable result to get the actual function completion code.

Further checks are performed and the function ends at one of the three exit points, returning the value of the function completion code. What is important, the lambda function will be executed regardless of where the function ended. This means that the logging is guaranteed to be fulfilled, regardless of whether they forgot to register it or not.

Conclusion

This example is artificial and you can also forget to assign the value of the variable result, but if you just need to execute some program code, regardless of where the function ended, then this option is quite suitable. So, the statement that some code may not be executed at the end of the function also loses its foundation.

In general, adhere to the established principles of development - it's good, but they came up with it many years ago, and the development tools stepped forward already. Therefore, you just need to study your programming language well and think with your head. Since many development problems that were 20 years ago can now be elegantly solved using the programming language.

We recommend hosting TIMEWEB
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.

Do you like it? Share on social networks!

Comments

Only authorized users can post comments.
Please, Log in or Sign up
AD

C ++ - Test 004. Pointers, Arrays and Loops

  • Result:50points,
  • Rating points-4
m

C ++ - Test 004. Pointers, Arrays and Loops

  • Result:80points,
  • Rating points4
m

C ++ - Test 004. Pointers, Arrays and Loops

  • Result:20points,
  • Rating points-10
Last comments
ИМ
Игорь МаксимовNov. 22, 2024, 11:51 a.m.
Django - Tutorial 017. Customize the login page to Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii LegotckoiOct. 31, 2024, 2:37 p.m.
Django - Lesson 064. How to write a Python Markdown extension Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZEOct. 19, 2024, 8:19 a.m.
Fb3 file reader on Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь МаксимовOct. 5, 2024, 7:51 a.m.
Django - Lesson 064. How to write a Python Markdown extension Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas5July 5, 2024, 11:02 a.m.
QML - Lesson 016. SQLite database and the working with it in QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Now discuss on the forum
Evgenii Legotckoi
Evgenii LegotckoiJune 24, 2024, 3:11 p.m.
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey1Nov. 15, 2024, 6:04 a.m.
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProjectJune 4, 2022, 3:49 a.m.
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9AnonimOct. 25, 2024, 9:10 a.m.
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

Follow us in social networks