Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Turn a functor into a function pointer

67 views
Skip to first unread message

Frederick Gotham

unread,
Sep 24, 2019, 6:31:27 AM9/24/19
to

Have they considered adding a one-liner to Boost that can turn a functor into a function pointer, for example so that we can use "std::atexit" with "boost::bind", like so:

extern void closeAnimation(int prefix);

int main()
{
atexit( boost::bind(closeAnimation, 0) );
}

Such a one-liner should really be in boost.

Ian Collins

unread,
Sep 24, 2019, 6:39:57 AM9/24/19
to
What's wrong with

atexit( [](){ closeAnimation(0); });

?

--
Ian

Frederick Gotham

unread,
Sep 24, 2019, 7:51:34 AM9/24/19
to
That will work fine when there's nothing captured by the lambda function.

There could be a situation where a handle is obtained inside a function and stored in a local variable, something like:

void Init_Flight_Simulator()
{
FSim *handle = ObtainSimulator();

if ( nullptr != handle )
atexit( [handle](){ ReleaseSimulator(handle); } );

/* Continue doing stuff with handle */
}


This won't compile for me.

David Brown

unread,
Sep 24, 2019, 8:31:36 AM9/24/19
to
It's good that it doesn't compile, because it would go horribly wrong -
your handle is at block scope, but needs to be available at program exit
time. You have to figure out the best way to make it life long enough.
For example, you could write:

void Init_Flight_Simulator()
{
static FSim *handle = ObtainSimulator();

if ( nullptr != handle )
atexit( [](){ ReleaseSimulator(handle); } );

/* Continue doing stuff with handle */
}

But you'd be better doing this with RAII and smart pointers - releasing
resources with "atexit" is not a typical C++ solution.

Paavo Helde

unread,
Sep 24, 2019, 8:47:15 AM9/24/19
to
And neither would a boost::bind solution. A single function pointer
fixed at compile time just cannot contain or otherwise hold another
value ('handle' here) whose value will become available at run time only.

Technically it could be done via trampolines by run-time code
generation, but I believe it's a good thing this is not part of C++.



Paavo Helde

unread,
Sep 24, 2019, 9:02:19 AM9/24/19
to
PS. In Linux there is also on_exit() which takes an extra callback
pointer and would work fine with this example, but is arguably even more
unfriendly to lambdas.


Frederick Gotham

unread,
Sep 24, 2019, 11:06:12 AM9/24/19
to
I've been playing around with this code that still has a few issues:

#include <functional>

/* Pretend that these next 2 functions are defined in another translation unit */
int GetHandle(void) { return 5; }
void ReleaseHandle(int) { /* Whatever */ }

template <class RetType, class T>
RetType (*(Make_Funcptr(T &&functor)))(void)
{
class InnerClass {
public:

static T *Set_Or_Get_Functor(T *const arg)
{
static T *pfunctor = nullptr;

if ( nullptr != arg )
pfunctor = arg;

return pfunctor;
}

static RetType ActualFunction(void)
{
return (*(Set_Or_Get_Functor(0)))();
}
};

InnerClass::Set_Or_Get_Functor(&functor);

return InnerClass::ActualFunction;
}

void SomeFunc(void)
{
int h = GetHandle();

std::bind(ReleaseHandle, h);

atexit( Make_Funcptr<void>( std::bind(ReleaseHandle, h) ) );
}

int main(void)
{
SomeFunc();
}

A few problems:
(1) Taking the address of the temporary object returned from "bind"
(2) The static pointer will have one instance for each combination of T and RetType (when really we need a static pointer for every invocation of Make_FuncPtr)

Paavo Helde

unread,
Sep 24, 2019, 1:55:40 PM9/24/19
to
To accomplish such things with atexit() you will need a global static
anyway, so better be explicit about this:

#include <deque>
#include <functional>

int GetHandle(void) { return 5; }
void ReleaseHandle(int) { /* Whatever */ }

static std::deque<std::function<void()>> globalRegistry;

void SomeFunc()
{
int h = GetHandle();
globalRegistry.push_back([h]() {ReleaseHandle(h);});
}

int main() {
atexit([]() {
for (auto& functor : globalRegistry) {
functor();
}}
);

SomeFunc();
}





Richard

unread,
Sep 24, 2019, 3:38:28 PM9/24/19
to
[Please do not mail me a copy of your followup]

Frederick Gotham <cauldwel...@gmail.com> spake the secret code
<08338ab3-c58c-4972...@googlegroups.com> thusly:

>There could be a situation where a handle is obtained inside a function
>and stored in a local variable, something like:
>
>void Init_Flight_Simulator()
>{
> FSim *handle = ObtainSimulator();
>
> if ( nullptr != handle )
> atexit( [handle](){ ReleaseSimulator(handle); } );
>
> /* Continue doing stuff with handle */
>}

IMO, this particular example would better be served by an RAII wrapper
class rather than through the use of atexit.
--
"The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline>
The Terminals Wiki <http://terminals-wiki.org>
The Computer Graphics Museum <http://computergraphicsmuseum.org>
Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com>

Frederick Gotham

unread,
Sep 25, 2019, 3:48:38 AM9/25/19
to
On Tuesday, September 24, 2019 at 6:55:40 PM UTC+1, Paavo Helde wrote:

> #include <deque>
> #include <functional>
>
> int GetHandle(void) { return 5; }
> void ReleaseHandle(int) { /* Whatever */ }
>
> static std::deque<std::function<void()>> globalRegistry;
>
> void SomeFunc()
> {
> int h = GetHandle();
> globalRegistry.push_back([h]() {ReleaseHandle(h);});
> }
>
> int main() {
> atexit([]() {
> for (auto& functor : globalRegistry) {
> functor();
> }}
> );
>
> SomeFunc();
> }


This is my favourite solution so far, thank you Paavo.

I'd make it pretty like this:

#include <deque>
#include <functional>

int GetHandle(void) { return 42; }
void ReleaseHandle(int) { /* Whatever */ }

void atexit_ftr(std::function<void()> &&f)
{
/* If this function is never called then the following container is never created */

static std::deque< std::function<void()> > stuff_to_do;

static bool first_time = true;

if ( first_time )
{
first_time = false;

atexit(

[]()
{
for (auto &functor : stuff_to_do)
{
functor();
}
}

);
}

stuff_to_do.push_back(f);
}

void SomeFunc(void)
{
int h = GetHandle();

atexit_ftr( [h](){ReleaseHandle(h);} );
}

int main(void)
{
SomeFunc();
}

This will be very useful in my current project (embedded Linux for a product running a dozen or two services simultaneously that are constantly grabbing and releasing resources -- it's very important that any given service releases all handles when it dies).

Frederick Gotham

unread,
Sep 25, 2019, 3:51:10 AM9/25/19
to
On Wednesday, September 25, 2019 at 8:48:38 AM UTC+1, Frederick Gotham wrote:

> This is my favourite solution so far, thank you Paavo.
>
> I'd make it pretty like this:
>
> #include <deque>
> #include <functional>
>
> int GetHandle(void) { return 42; }
> void ReleaseHandle(int) { /* Whatever */ }
>
> void atexit_ftr(std::function<void()> &&f)
> {
> /* If this function is never called then the following container is never created */
>
> static std::deque< std::function<void()> > stuff_to_do;
>
> static bool first_time = true;
>
> if ( first_time )
> {
> first_time = false;
>
> atexit(
>
> []()
> {
> for (auto &functor : stuff_to_do)
> {
> functor();
> }
> }
>
> );
> }
>
> stuff_to_do.push_back(f);
> }
>
> void SomeFunc(void)
> {
> int h = GetHandle();
>
> atexit_ftr( [h](){ReleaseHandle(h);} );
> }
>
> int main(void)
> {
> SomeFunc();
> }


Two more things:

(1) #include <cstdlib>
(2) Write the namespace "std" before atexit, i.e. "std::atexit"

Paavo Helde

unread,
Sep 25, 2019, 4:49:56 AM9/25/19
to
On 25.09.2019 10:48, Frederick Gotham wrote:

>
> This will be very useful in my current project (embedded Linux for a product running a dozen or two services simultaneously that are constantly grabbing and releasing resources -- it's very important that any given service releases all handles when it dies).

atexit() functions are not called when the process dies because of a
signal like SIGTERM or SIGPIPE; these need special handling.

In Linux all file descriptors are closed automatically when the process
dies because of any reasons, so special release is needed only for
resources which do not follow the standard "everything is a file"
design. Or is this different in embedded world?


Frederick Gotham

unread,
Sep 25, 2019, 5:19:16 AM9/25/19
to
On Wednesday, September 25, 2019 at 9:49:56 AM UTC+1, Paavo Helde wrote:
> On 25.09.2019 10:48, Frederick Gotham wrote:

> atexit() functions are not called when the process dies because of a
> signal like SIGTERM or SIGPIPE; these need special handling.


I think I can use 'sigaction' to nominate a callback function for these signals, and then inside the callback function I call "std::exit".


> In Linux all file descriptors are closed automatically when the process
> dies because of any reasons, so special release is needed only for
> resources which do not follow the standard "everything is a file"
> design. Or is this different in embedded world?


I use a Text To Speech engine in an embedded device, and you have to get a handle to the engine, then a handle to the voice, then the engine creates a buffer internally. From reading the documentation, they seem quite adamant about you releasing everything before your program ends. To be honest I could probably just contact them and ask if everything gets released when a program dies. The documentation is written by a person who has English as a second language. There are other problems with the documentation... for example typographical errors in the sample program.

Frederick Gotham

unread,
Sep 25, 2019, 5:37:39 AM9/25/19
to
On Tuesday, September 24, 2019 at 6:55:40 PM UTC+1, Paavo Helde wrote:

> static std::deque<std::function<void()>> globalRegistry;
>
> for (auto& functor : globalRegistry)


By the way, I see just now that atexit doesn't work FIFO. The last function you give it is the first to be called. So it works LIFO.

Frederick Gotham

unread,
Sep 25, 2019, 9:14:17 AM9/25/19
to
On Wednesday, September 25, 2019 at 8:51:10 AM UTC+1, Frederick Gotham wrote:

> > atexit_ftr( [h](){ReleaseHandle(h);} );

Actually this way of doing it will screw up the order of the execution of functions that have been registered with atexit (i.e. All of the functions registered with atexit_ftr will be executed in a sequence, instead of being interspersed with functions registered with atexit).

Paavo Helde

unread,
Sep 26, 2019, 5:57:17 AM9/26/19
to
On 25.09.2019 12:19, Frederick Gotham wrote:
> On Wednesday, September 25, 2019 at 9:49:56 AM UTC+1, Paavo Helde wrote:
>> On 25.09.2019 10:48, Frederick Gotham wrote:
>
>> atexit() functions are not called when the process dies because of a
>> signal like SIGTERM or SIGPIPE; these need special handling.
>
>
> I think I can use 'sigaction' to nominate a callback function for these signals, and then inside the callback function I call "std::exit".

exit() is not among the functions you can legally call from a signal
handler; see "http://man7.org/linux/man-pages/man7/signal-safety.7.html"

This is easy to understand: consider what happens when the signal
handler is invoked at the moment when the program is in a malloc() call
and actively modifying the global memory manager data structures, and
there is an installed atexit() function which wants to call free() at
the same time.




Frederick Gotham

unread,
Sep 26, 2019, 6:48:54 AM9/26/19
to
On Thursday, September 26, 2019 at 10:57:17 AM UTC+1, Paavo Helde wrote:

> exit() is not among the functions you can legally call from a signal
> handler; see "http://man7.org/linux/man-pages/man7/signal-safety.7.html"
>
> This is easy to understand: consider what happens when the signal
> handler is invoked at the moment when the program is in a malloc() call
> and actively modifying the global memory manager data structures, and
> there is an installed atexit() function which wants to call free() at
> the same time.

Ok I'm really starting to think that this should be done from scratch all by ourselves. Maybe we should abandon the use of "atexit".

How about we change the "minimalistic C++ program" to the following?

#include <exception> /* exception */
#include <functional> /* function */
#include <stack> /* stack : Note that this is Last In First Out */
#include <cstdlib> /* EXIT_FAILURE */

inline void Perimortem_Run(std::function<void()> const &f, int const hidden_param = 0)
{
static std::stack< std::function<void()> > stuff_to_do;

if ( 42 != hidden_param )
{
stuff_to_do.push(f);
return;
}

for ( ; !stuff_to_do.empty(); stuff_to_do.pop() )
{
stuff_to_do.top()();
}
}

int GetHandle(void) { return 42; }
void ReleaseHandle(int) { /* Whatever */ }

void SomeFunc(void)
{
int h = GetHandle();

Perimortem_Run( [h](){ReleaseHandle(h);} );

/* Re-use 'h' again, maybe even get another handle */
}

int real_main()
{
/* Your program goes here */

SomeFunc();

return 0;
}

int main()
{
int retval = EXIT_FAILURE;

try
{
retval = real_main();
}
catch(std::exception const &e)
{
/* Do something with e.what() */
}
catch(...)
{

}

Perimortem_Run( std::function<void()>(), 42 ); /* Passing the hidden 42 makes it do its thing */

return retval;
}


How does that look?



Frederick Gotham

unread,
Sep 26, 2019, 7:15:21 AM9/26/19
to
On Wednesday, September 25, 2019 at 2:14:17 PM UTC+1, Frederick Gotham wrote:

> Actually this way of doing it will screw up the order of the execution of functions that have been registered with atexit (i.e. All of the functions registered with atexit_ftr will be executed in a sequence, instead of being interspersed with functions registered with atexit).


I figured out a way of getting around the sequence problem:

#include <exception> /* exception */
#include <functional> /* function */
#include <stack> /* stack : Note that this is Last In First Out */
#include <cstdlib> /* EXIT_FAILURE, atexit */

int GetHandle(void) { return 42; }
void ReleaseHandle(int) { /* Whatever */ }

std::stack< std::function<void()> > container_for_atexit_ftr;

void atexit_ftr_Helper(void)
{
container_for_atexit_ftr.top()();

container_for_atexit_ftr.pop();
}

void atexit_ftr(std::function<void()> &&f)
{
container_for_atexit_ftr.push(f);

std::atexit( atexit_ftr_Helper );
}

void SomeFunc(void)
{
int h = GetHandle();

atexit_ftr( [h](){ReleaseHandle(h);} );

/* Re-use 'h' again, maybe even get another handle */
}

int real_main()
{
/* Your program goes here */

SomeFunc();

return 0;
}

int main()
{
int retval = EXIT_FAILURE;

try
{
retval = real_main();
}
catch(std::exception const &e)
{
/* Do something with e.what() */
}
catch(...)
{

}

return retval;
}


Paavo Helde

unread,
Sep 26, 2019, 7:50:04 AM9/26/19
to
I do not like the hidden magic parameter. Also, this solution does not
address signals either.

What's wrong with proper wrapping of the third-party library?

class LibHandle {
int rawHandle_;
public:
LibHandle(int rawHandle): rawHandle_(rawHandle) {}
~LibHandle() {if (rawHandle_!=-1) {ReleaseHandle(rawHandle_);}}
LibHandle(const LibHandle&) = delete;
LibHandle& operator=(const LibHandle&) = delete;

// Support simple move of the handle into another variable
LibHandle(LibHandle&& b): rawHandle_(b.rawHandle_) {
b.rawHandle_ = -1;
}
// Support passing the handle directly to the library routines
operator int() const {
if (rawHandle_!=-1) {
return rawHandle_;
} else {
throw std::logic_error("defunct");
}
}
};

void SomeFunc(void) {
LibHandle h = GetHandle();
// Do whatever you need to do with the handle
// done
}

Regarding the terminating signals, I have found that in a Linux C++
program the best way to deal with them is to set up an extra thread
calling sigwait() and notifying the main thread(s) which then can do
proper cleanup by throwing a special termination exception, which winds
up the stack and properly releases all resources like std::fstream() or
the above LibHandle(). YMMV.



Jorgen Grahn

unread,
Sep 26, 2019, 9:46:38 AM9/26/19
to
On Wed, 2019-09-25, Frederick Gotham wrote:
> On Wednesday, September 25, 2019 at 9:49:56 AM UTC+1, Paavo Helde wrote:
>> On 25.09.2019 10:48, Frederick Gotham wrote:
>
>> atexit() functions are not called when the process dies because of a
>> signal like SIGTERM or SIGPIPE; these need special handling.
>
> I think I can use 'sigaction' to nominate a callback function for
> these signals, and then inside the callback function I call
> "std::exit".
>
>> In Linux all file descriptors are closed automatically when the process
>> dies because of any reasons, so special release is needed only for
>> resources which do not follow the standard "everything is a file"
>> design. Or is this different in embedded world?

> I use a Text To Speech engine in an embedded device, and you have to
> get a handle to the engine, then a handle to the voice, then the
> engine creates a buffer internally. From reading the documentation,
> they seem quite adamant about you releasing everything before your
> program ends.

That may be bullshit. You should find out, e.g. by running strace.

Few resources survive the process in Unix; the ones that come to mind
are plain disk files and some kinds of named shared memory.

Another is a really badly designed kernel module (where the kernel
resources aren't owned by an open file descriptor).

> To be honest I could probably just contact them and
> ask if everything gets released when a program dies.
...

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .
0 new messages