Closed hayguen closed 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.
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 $?
accepting a function object sounds good ..
but: why should one prefer make_scope_exit()
instead simply implementing cleanup::~cleanup()
destructor?
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.
okay .. would you add accepting function objects .. and that example?
With function pointers you mean function objects (functors)?
I'd like to have a go at that :)
ahem, yep, fixed my comment. great thanks!
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.
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?
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.
thank you
Thanks for asking :)
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:
is there a simple example? can this be documented?