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
г
  • ги
  • April 23, 2024, 3:51 p.m.

C++ - Test 005. Structures and Classes

  • Result:41points,
  • Rating points-8
l
  • laei
  • April 23, 2024, 9:19 a.m.

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

  • Result:10points,
  • Rating points-10
l
  • laei
  • April 23, 2024, 9:17 a.m.

C++ - Тест 003. Условия и циклы

  • Result:50points,
  • Rating points-4
Last comments
k
kmssrFeb. 8, 2024, 6:43 p.m.
Qt Linux - Lesson 001. Autorun Qt application under Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
Qt WinAPI - Lesson 007. Working with ICMP Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVADec. 25, 2023, 10:30 a.m.
Boost - static linking in CMake project under Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJoDec. 25, 2023, 8:38 a.m.
Boost - static linking in CMake project under Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
GvozdikDec. 18, 2023, 9:01 p.m.
Qt/C++ - Lesson 056. Connecting the Boost library in Qt for MinGW and MSVC compilers Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Now discuss on the forum
G
GarApril 22, 2024, 5:46 a.m.
Clipboard Как скопировать окно целиком в clipb?
DA
Dr Gangil AcademicsApril 20, 2024, 7:45 a.m.
Unlock Your Aesthetic Potential: Explore MSC in Facial Aesthetics and Cosmetology in India Embark on a transformative journey with an msc in facial aesthetics and cosmetology in india . Delve into the intricate world of beauty and rejuvenation, guided by expert faculty and …
a
a_vlasovApril 14, 2024, 6:41 a.m.
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Евгений, добрый день! Такой вопрос. Верно ли следующее утверждение: Любое Android-приложение, написанное на Java/Kotlin чисто теоретически (пусть и с большими трудностями) можно написать и на C+…
Павел Дорофеев
Павел ДорофеевApril 14, 2024, 2:35 a.m.
QTableWidget с 2 заголовками Вот тут есть кастомный QTableView с многорядностью проект поддерживается, обращайтесь
f
fastrexApril 4, 2024, 4:47 a.m.
Вернуть старое поведение QComboBox, не менять индекс при resetModel Добрый день! У нас много проектов в которых используется QComboBox, в версии 5.5.1, когда модель испускает сигнал resetModel, currentIndex не менялся. В версии 5.15 при resetModel происходит try…

Follow us in social networks