function name from function pointer

  • Thread starter Alessandro Basili
  • Start date
S

Stefan Ram

Alessandro Basili said:
I have been looking for a way to print the function name (as I name it
in my source code) via the function pointer.

In the following example code, two example functions,
named »beta« and »zeta« are being defined. »nameof«
returns the name of one of these two functions, when
passed a pointer to one of these two functions. So the
programm prints »beta« and »zeta«.

#include <stdio.h> /* puts */

#define F(r,n,b) r n b

#define FUNCTIONS \
F( void, beta,( void ){} )\
F( void, zeta,( void ){} )\
/**/

FUNCTIONS

#undef F
#define F(r,n,b) n,
void( * const function[] )( void )={ FUNCTIONS };
#undef F

#define F(r,n,b) #n,
char const * const name[] ={ FUNCTIONS };
#undef F

int const top = sizeof name / sizeof 0[ name ];

char const * nameof( void( * const f )( void ))
{ for( int i = 0; i < top; ++i )
if( f == function[ i ])return name[ i ]; }

int main( void )
{ puts( nameof( function[ 0 ]));
puts( nameof( function[ 1 ])); }
 
L

luser- -droog

On 11/4/2010 11:22 AM, Niklas Holsti wrote:
 > You could represent the state by a struct that contains both the
 > function pointer and a *char to the (constant) name of the function.

Indeed this is what I ended up with before the whole idea bumped into my
mind and I believe I will come back to that.


The reason why I don't wanted to deal with an enumeration description
_and_ a table for dispatching is that I will need to maintain two
"lists", first the enumeration and second the table of correspondences
otherwise I will have problems. Given my attitude in writing code, I
tend to minimize the number of "multiple" representation of an object in
order not to be tied to "multiple" editing whenever I need to add or
remove a part of it.

X-macros to the rescue.
(untested)

#define parallel_data \
X("name", namefunc) \
X("name2", otherfunc) \
X("handle", handfunc) \
/* end parallel_data */

#define X(a,b) a,
char *names[] = { parallel_data };
#undef X

#define X(a,b) b,
ret_type (*funtab[])() = { parallel_data };
#undef X

Voila! Two arrays with related data at the same index.
And the data is all together in one place.
 
N

Nick

luser- -droog said:
On Nov 4, 9:57 am, Alessandro Basili <[email protected]>
wrote:
The reason why I don't wanted to deal with an enumeration description
_and_ a table for dispatching is that I will need to maintain two
"lists", first the enumeration and second the table of correspondences
otherwise I will have problems. Given my attitude in writing code, I
tend to minimize the number of "multiple" representation of an object in
order not to be tied to "multiple" editing whenever I need to add or
remove a part of it.

X-macros to the rescue.
(untested)

#define parallel_data \
X("name", namefunc) \
X("name2", otherfunc) \
X("handle", handfunc) \
/* end parallel_data */

#define X(a,b) a,
char *names[] = { parallel_data };
#undef X

#define X(a,b) b,
ret_type (*funtab[])() = { parallel_data };
#undef X

Voila! Two arrays with related data at the same index.
And the data is all together in one place.

I do something very similar for much longer tables. The difference is
that rather than wrap the X-macros in another macro, I use preprocessor
conditionals and re-include the original file. It avoids those pesky
line-continuations which sooner or later you are going to forget when
extending.

So - just as untested:
xmacros.h:

#ifdef xmacros_selfinclude
X("name", namefunc)
X("name2", otherfunc)
X("handle", handfunc)
#else
#define X(a,b) a,
#define xmacros_selfinclude
char *names[] = {
#include "xmacros.h"
};
#undef X
#define X(a,b) b,
ret_type (*funtab[])() = {
#include "xmacros.h"
};
#undef X
#endif

I use it quite a lot for longish enums, that way I can provide a
"debug_enum_xxx()" function for diagnostic purposes.
 
N

Nick

Nick said:
luser- -droog said:
On Nov 4, 9:57 am, Alessandro Basili <[email protected]>
wrote:
The reason why I don't wanted to deal with an enumeration description
_and_ a table for dispatching is that I will need to maintain two
"lists", first the enumeration and second the table of correspondences
otherwise I will have problems. Given my attitude in writing code, I
tend to minimize the number of "multiple" representation of an object in
order not to be tied to "multiple" editing whenever I need to add or
remove a part of it.

X-macros to the rescue.
(untested)

#define parallel_data \
X("name", namefunc) \
X("name2", otherfunc) \
X("handle", handfunc) \
/* end parallel_data */

#define X(a,b) a,
char *names[] = { parallel_data };
#undef X

#define X(a,b) b,
ret_type (*funtab[])() = { parallel_data };
#undef X

Voila! Two arrays with related data at the same index.
And the data is all together in one place.

I do something very similar for much longer tables. The difference is
that rather than wrap the X-macros in another macro, I use preprocessor
conditionals and re-include the original file. It avoids those pesky
line-continuations which sooner or later you are going to forget when
extending.

So - just as untested:
xmacros.h:

#ifdef xmacros_selfinclude
X("name", namefunc)
X("name2", otherfunc)
X("handle", handfunc)
#else
#define X(a,b) a,
#define xmacros_selfinclude
char *names[] = {
#include "xmacros.h"
};
#undef X
#define X(a,b) b,
ret_type (*funtab[])() = {
#include "xmacros.h"
};
#undef X
#endif

I use it quite a lot for longish enums, that way I can provide a
"debug_enum_xxx()" function for diagnostic purposes.

As a self-followup, this is why the standardisation of trailing commas
in enums etc was such a Good Thing.
 
B

BGB

It's actually not a compiler problem alone but also the
linker must be involved - the compiler only deals with
single source files and already puts the names of (non-
static) functions into a table since the linker must
be able to assemble the resulting object files and set
up addresses for function calls from within one object
file to a function in another. But the linker normally
discards this information when done since it is not
needed anymore - it would just increase the size of
the program unnecessarily.

On the other hand there's already all the infrastructure
for mapping function names to addresses when you compile
with debugging support. And on many platforms there are
more or less well-documented ways to get at this informa-
tion which the debugger is using. But, of course, this
isn't standardized by the C standard - if it were each
program would always have to be compiled in debug mode
and C could only be used on platforms where the linker
can be made to create such a map between function names
and addresses - and that for a minority of cases where
this information is of any interest.

Your best bet is probably to check for the platforms
your program is to be used on if there is some support
to get at the debugging information from within your
program. Unfortunately, this is rather likely to be
platform-dependent but may still be the most painless
way to get at such a mapping. I would guess that it's
going to be described in the documentation for the lin-
ker and the format of the executable files produced.
And on e.g. Linux you simply could parse the output of
a utility like 'objdump' which gives you all the infor-
mation required in a rather simple to parse text format.
Other systems may have similar tools.

on Linux, there are libdl extensions for this...
(however, these extensions are not generally portable...).

on Windows, there are the ToolHelp and DbgHelp which offer these
facilities, provided one has DLL exports and/or symbolic debug info
floating around (with debug info, it will provide the exact function,
whereas with only DLL exports it will return the last DLL export).


in a world of DLLs and shared objects, there is really a good amount of
this sort of info around, and some per-target ifdef's get at it fairly
well... (the only time it can really be hidden is in large
statically-linked binaries with symbols stripped, where it depends some
of the OS, compiler, and compiler options as to whether or not symbol
info is stripped by default). AFAIK, GCC defaults to not stripping
symbols (at least with debug options), whereas MSVC defaults to
stripping any non-export symbols, ...


IMO, parsing objdump output is a poor way to do this, and is IMO not
really better than trying to directly rip the data out of the in-memory
PE/COFF or ELF image...
 
B

BGB

This is in no way general, but something like the code below (warning:
not even compiled!) is a portable solution for those cases where you
know which functions are likely to end up pointed at...

That said, I'm fairly certain you're trying to do something that is not
a good idea - this is pretty much *only* useful for debugging, and any
decent debugger will already have this functionality built in...

I disagree on this point...

there are non-debugging uses for reverse-lookups, namely, the ability to
easy looking up function signatures for arbitrary function pointers.

this is useful in implementing a generic apply function, say:

void *someFunctionPointer;

....
fooApply(someFunctionPointer, "hello", 2, 3.0, 4, foo, bar);

or:
void **args;
....
fooApplyV(someFunctionPointer, args);

or:
....


now, this may seem trivial enough, but actually implementing calls like
this get a bit nasty internally (and in fact there is no "good" way to
implement generic apply operators in pure C anyways, hence often some
ASM ends up in the mix somewhere...).


granted, looking up a function by name is often more useful, since then
one can do generic call operators:
fooCall("myFunc", 2, 3);

and the signature can be looked up by the name (no reverse-lookup needed).


a case where this sort of thing is useful then is in implementing
transparent glue code between C and some non-C HLL (IOW: scripting
language), where the ability to do reverse-lookups and marshall
arguments is needed, say, for dealing with structs containing function
pointers and being able to call these function pointers as if they were
methods or similar.

if one spits out chunks of machine code, they can also go the other way
and stuff HLL functions into the struct's function pointers.

though not directly related, maintaining info about struct types and
layouts is also fairly useful, ...


sadly, apart from depending on debug info or parsing C headers or
similar, there is no good way to get a lot of this info... (I opted with
the option of parsing headers and mining info this way).


or such...
 
J

Joachim Schipper

BGB said:
I disagree on this point...

there are non-debugging uses for reverse-lookups, namely, the ability to
easy looking up function signatures for arbitrary function pointers.

this is useful in implementing a generic apply function, say:

void *someFunctionPointer;

...
fooApply(someFunctionPointer, "hello", 2, 3.0, 4, foo, bar);

or:
void **args;
...
fooApplyV(someFunctionPointer, args);

or:
...


now, this may seem trivial enough, but actually implementing calls like
this get a bit nasty internally (and in fact there is no "good" way to
implement generic apply operators in pure C anyways, hence often some
ASM ends up in the mix somewhere...).

granted, looking up a function by name is often more useful, since then
one can do generic call operators:
fooCall("myFunc", 2, 3);

and the signature can be looked up by the name (no reverse-lookup needed).


a case where this sort of thing is useful then is in implementing
transparent glue code between C and some non-C HLL (IOW: scripting
language), where the ability to do reverse-lookups and marshall
arguments is needed, say, for dealing with structs containing function
pointers and being able to call these function pointers as if they were
methods or similar.

if one spits out chunks of machine code, they can also go the other way
and stuff HLL functions into the struct's function pointers.

though not directly related, maintaining info about struct types and
layouts is also fairly useful, ...


sadly, apart from depending on debug info or parsing C headers or
similar, there is no good way to get a lot of this info... (I opted with
the option of parsing headers and mining info this way).

Okay, I admit that this may be useful when trying to interoperate with
another language. But if your program is written in C (and only C),
there's no reason not to just go with

void (*apply)(void (*fn)(void *), void *);

instead.

Joachim
 
M

Mark Wooding

Nick said:
As a self-followup, this is why the standardisation of trailing commas
in enums etc was such a Good Thing.

I always found that having an additional enum constant to give me an
exclusive upper bound on the other constants as sufficiently useful that
I never had a problem with the trailing comma being disallowed.

-- [mdw]
 
B

BGB

Okay, I admit that this may be useful when trying to interoperate with
another language. But if your program is written in C (and only C),
there's no reason not to just go with

void (*apply)(void (*fn)(void *), void *);

instead.

well, there is a downside:
a function pointer like the above is fixed-form, meaning one is limited
to calling functions where the argument list is known at compile time.

granted, yes, this situation is more rare in the pure C case.


in other cases, people have resorted to large switch statements encoding
"every possible reasonable" set of arguments, but this is bulky and
awkward, and usually has arbitrary limitations (restricting argument
types or number, ...).


granted, the raw function pointer is much cheaper though, as often the
logic for, say, repacking the args list (according to a signature string
or similar), copying the args to where they need to go (on stack or in
registers), and calling the function, is not exactly free (as opposed to
costing, say, 5ns or 10ns, it then costs 50ns or 100ns...).

if one adds the cost of reading arguments from a varargs function, doing
a vtable lookup, ... a method call (via a generic API function, say in
an OO library), can easily take 200ns-300ns or more...

though some cycles can be shaved via generating (and caching) machine
code internally to handle parts of the operation (or "transfer thunks"
as I have called them before, although some added cost remains).

directly calling a method via a function pointer is much cheaper, but
can be a little more awkward (and has annoying edge cases).


so, it is tradeoffs...

often in cases where one needs generic apply and call operations, their
usefulness exceeds their cost.


much like with dynamic types:
a static type is much cheaper and covers the majority of use cases;
however, there are times when a dynamic type is rather useful.

so, support for dynamic types is worth having around, even if most of
the time one may end up not using them.


or such...
 
S

Stefan Ram

BGB said:
often in cases where one needs generic apply and call operations, their
usefulness exceeds their cost.

»Any sufficiently complicated C or Fortran program
contains an ad-hoc, informally-specified bug-ridden slow
implementation of half of Common Lisp.«

Greenspun's Tenth Rule of Programming
 
J

Jorgen Grahn

On 11/3/2010 10:58 PM, Jorgen Grahn wrote: ....

I have implemented a state machine, using pointers to function for
states. The dispatcher provides events to the states and a change in the
state is simply accomplished changing the "state" pointer to yet another
function (the layout of the program can be found here:
http://www.netrino.com/Embedded-Systems/How-To/State-Machines-Event-Driven-Systems,
in listing 1,2 and 3).

I found the approach quite nice and easily scalable to more complex
problems, without having the need to maintain any table (events, states)
or the burden of so many switch/case scattered around.

My problem though is that in this approach is not quite easy to print in
which state I am, since the state is represented only by the function
pointer. That is why I thought that having the possibility to get the
name of the function from its pointer would have helped me out.

Ah, good. Now I see what you want and why.

I guess I'd go: "Damn, it's hard to print what state I'm in. Oh well
-- maybe I don't really need to print that after all." And then I'd
wait until I had an urgent need to print that info.

If it's just for debugging (which IMHO it should be) I'd probably be
satisfied with seeing the function pointers in hex, knowing I can
translate them to names if I want to (using nm(1) on Unix).

/Jorgen
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,756
Messages
2,569,540
Members
45,025
Latest member
KetoRushACVFitness

Latest Threads

Top