martinmoene / scope-lite

A migration path to C++ library extensions scope_exit, scope_fail, scope_success, unique_resource
Boost Software License 1.0
43 stars 6 forks source link

c++98 doc/example #4

Closed hayguen closed 3 years ago

hayguen commented 3 years ago

is it possible to cleanup function local resources - with access to local variables .. without lambda functions, having only C++98? i couldn't figure out, how to use nonstd::make_scope_exit() in this case.

what i'm thinking of is:

void foo() {
  int resource = 1;  // local resource to be processed somehow at cleanup
  // some helper function or class?
  nonstd::scope_exit e = nonstd::make_scope_exit( ??? );
  ...
}

is there a simple example? can this be documented?

martinmoene commented 3 years ago

Thanks for your interesting question.

As the C++98 implementation is now, nonstd::make_scope_exit() cannot do this, as it can only take a normal function pointer.

To be able to cleanup a local resource, nonstd::make_scope_exit() should be able to construct and use something like class cleanup in the following example.

#include "nonstd/scope.hpp"

int global = 1;

int main()
{
    // local scope:
    {
        // local resource to be processed at cleanup:

        int & resource = global;

#if 0
        nonstd::scope_exit guard = nonstd::make_scope_exit( nonstd::wrap_local_resource(resource) );
#else
        // cannot be template at block scope:

        struct cleanup
        {
            int & resource;

            cleanup( int & resource_ )
            : resource( resource_)
            {}

            ~cleanup()
            {
                resource = 42;
            }
        };

        cleanup guard( resource );

        // code using resource:
        // ...
#endif
    } // end local scope

    return global;
}

// cl -nologo -EHsc -I../include 04-local-scope-cpp98.cpp && 04-local-scope-cpp98
// echo %ERRORLEVEL%

// g++ -std=c++98 -Wall -I../include -o 04-local-scope-cpp98 04-local-scope-cpp98.cpp && ./04-local-scope-cpp98 || echo $?

Whereas, in C++11 it would look like:

#include "nonstd/scope.hpp"

int global = 1;

int main()
{
    // local resource to be processed at cleanup:

    { int & resource = global; auto guard = nonstd::make_scope_exit( [&](){ resource = 42; } ); }

    return global;
}

// cl -nologo -EHsc -I../include 04-local-scope-cpp11.cpp && 04-local-scope-cpp11
// echo %ERRORLEVEL%

// g++ -std=c++11 -Wall -I../include -o 04-local-scope-cpp11 04-local-scope-cpp11.cpp && ./04-local-scope-cpp11 || echo $?

I'll have to think a bit if I can fit local resource cleanup in the C++98 implementation of scope-lite.

martinmoene commented 3 years ago

One step in the direction would be to let nonstd::make_scope_exit() (also) accept a function object.

#include "nonstd/scope.hpp"

int global = 1;

int main()
{
    // local scope:
    {
        // local resource to be processed at cleanup:

        int & resource = global;

        // cannot be template at block scope:

        struct cleanup
        {
            int & resource;

            cleanup( int & resource_ )
            : resource( resource_)
            {}

            void operator()()
            {
                resource = 42;
            }
        };

#if 0
        cleanup action( resource );
        nonstd::scope_exit guard = nonstd::make_scope_exit( action );
#else
        nonstd::scope_exit guard = nonstd::make_scope_exit( cleanup(resource) );
#endif

        // code using resource:
        // ...
    } // end local scope

    return global;
}

// cl -nologo -EHsc -I../include 04-local-scope-cpp98-fo.cpp && 04-local-scope-cpp98-fo
// echo %ERRORLEVEL%

// g++ -std=c++98 -Wall -I../include -o 04-local-scope-cpp98-fo 04-local-scope-cpp98-fo.cpp && ./04-local-scope-cpp98-fo || echo $?
hayguen commented 3 years ago

accepting a function object sounds good .. but: why should one prefer make_scope_exit() instead simply implementing cleanup::~cleanup() destructor?

martinmoene commented 3 years ago

The idea of most of the lite libraries is to offer a migration path to what is, will be, or might end up in later C++ standards than one is using at a certain moment in time.

With C++98, clearly some approaches are not possible, however, even being able to recognize scope_* may be beneficial.

hayguen commented 3 years ago

okay .. would you add accepting function objects .. and that example?

martinmoene commented 3 years ago

With function pointers you mean function objects (functors)?

I'd like to have a go at that :)

hayguen commented 3 years ago

ahem, yep, fixed my comment. great thanks!

martinmoene commented 3 years ago

Have added the ability to use function objects with C++98, see .e.g. example/04-local-scope-cpp98.cpp.

C++98 prohibits to use a local class as a template parameter with another class, like in:

int global = 1;

int main()
{
    struct local_functor
    {
        int & resource;

        local_functor( int & resource_ )
        : resource( resource_)
        {}

        void operator()()
        {
            resource += 40;
        }
    };

    int & resource = global;

    // Cannot write `nonstd::scope_exit<local_functor>` in C++98:
    nonstd::scope_exit<local_functor> guard = nonstd::make_scope_exit( local_functor(resource) );
}

I didn't want to make complicated constructs to circumvent this limitation.

Edit-1 15-Sep-2021 8:43: added missing template<typename T> in example above. Edit-2 16-Sep-2021 14:03: made it a non-template as intended.

hayguen commented 3 years ago

now had a look: nice. thank you!

just one detail question: 04-local-scope-cpp98.cpp uses a templated template< typename T > struct functor. there's no need, that struct functor is a template - as it usually will need a special implementation of the operator()() anyway. do you see any advantages in using a template there?

martinmoene commented 3 years ago

Indeed, there may be no need for the functor to be a class template, however I like the idea of defining the resource type for it at the location of use, rather than at the 'somewhat distant' location of the definition of the functor.

hayguen commented 3 years ago

thank you

martinmoene commented 3 years ago

Thanks for asking :)