In this first of a three-part series of articles, J. Nakamura covers regular function pointers. These pointers allow you to access the code you write for your applications in the form of functions.
Introduction
In a previous series of articles named “Pointer Perfect,” I looked at how data stored in computer memory is accessible through pointers. The code you write for your application in the form of functions (these can be global or class member functions) is very much accessible through pointers in the same way, and I will introduce you to the syntax and usage of these function pointers in this article.
Function pointers are great for implementing callbacks and are very useful when you want to introduce late binding in your application. They help you parse configuration files, help you read protocols and can help you store a function call in an object when you want to delay or record the call (very useful in an undo/redo system).
Most C++ books treat function pointers superficially, which is very unfortunate; maybe this is because their syntax looks a bit weird when you first start using them. I hope to be able to convince you that the right function pointer at the right moment in the right place will make your code clearer, cleaner and easier to understand. Once you get past the odd syntax and come to understand the power of calling functions through pointers, a whole new level of abstraction will open up to you.
A Quick ExampleThere is nothing like a quick example to take you straight to the heart of a topic. Here is a simple code example that uses a function pointer.
// main.cpp
#include
int IncOne(int var) {
return ++var;
}
int main(int argc, char *argv[]) {
// creating pointer to function
int (*inc_one)(int) = &IncOne;
// use the function the standard way
int result = IncOne(1);
(void)printf(“result is: %d\n”, result);
// utilize the function pointer
result = inc_one(1);
(void)printf(“result is: %d\n”, result);
return 0;
}
When you run this simple example, you will notice that both times the result printed is "2" -- as expected, of course. So what has the compiler come up with for us? Let’s take a peek into the debugger at the value stored in inc_one.
int (*inc_one)(int) = &IncOne;
00411A9E mov dword ptr [inc_one],offset IncOne (411523h)
It holds the address 0x00411523, and looking at the memory at that position we find the following line:
0041126C jmp IncOne (411A40h)
Our code is ordered to jump to 0x411A40, where we of course will find the code of our IncOne() function:
int IncOne(int var)
{
00411A40 push ebp
00411A41 mov ebp,esp
...
}
So what does the assembly in the debugger look like when we make a normal function call to IncOne() ?
int result=IncOne(1);
00411AA5 push 1
00411AA7 call IncOne (41126Ch)
Hold on! The debugger tells us that we go to 0x0041126c, where we are made to jump to IncOne (0x00422A40). So what happens when we call the function using the function pointer inc_one?
result=inc_one(1);
00411AC3 mov esi,esp
00411AC5 push 1
00411AC7 call dword ptr [inc_one]
Exactly the same thing! The content of inc_one is 0x0041126C, which, when called, makes our code jump to IncOne (0x00422A40), where the function is executed. This basically covers the whole concept of function pointers. All that is left now are the many (strange) forms in which they can appear.
The function pointer in our quick example points to the address of an ordinary C function. There are no special considerations for us to make in this case.
The code you compile to create your executable takes up a certain space in the computer's memory, where your data and functions will reside during its execution. Just like the data accessible through pointers, we have seen that functions are accessible in the very same way. The only thing a compiler has to know is how to interpret the address to which a pointer is pointing; this is where things can become slightly confusing, as we run into the often odd syntax of function pointers.
Defining and Using Regular Function PointersJust like regular pointers, function pointers are variables that need to be declared and defined. In the quick example you saw that inc_one is our function pointer variable, holding the pointer to a function that returns an int and that takes an int as a parameter.
To give you another example, if you declare the following function:
void AnotherFunction(std::string const &argument, float value);
Then you would assign the address of this function to a function pointer variable like this:
void (*fncptr)(std::string const&,float) = &AnotherFunction;
Hopefully you can see a pattern emerging here. The function declaration is basically being repeated, with the function name replaced by a "*varName," and only the types of the parameters are named. The retrieval of the address of a function works just like the address retrieval of a variable; you can use the address dereference operator "&."
Personally, I find the above demonstrated way of defining a function pointer a bit hard to read. When you need more variables to hold pointers to the same function, it can become quite laborious and sensitive to typing errors, too. Not to mention the fact that when the function definition changes, you would have to chase down and change every function pointer pointing to the changed function as well.
So here we will find the use of the typedef keyword very useful:
typedef int (*FncPtr)(int);
When we stick to the example, we can replace the add_one definition with:
FncPtr inc_one = &IncOne;
So you see that it is really easy to declare function pointers. Just as with normal pointers, you either use the pointer directly or you use it by dereferencing the pointer explicitly:
int result1 = inc_one(1);
int result2 = (*inc_one)(1);
Making the function call through a pointer or just the plain way will yield not very noticeable differences. It's quite nice to be able to abstract a function call into a variable, isn’t it?
Passing a function pointer as an argument to a function is quite essential when you want to implement callback functions. Although we won’t be looking at the implementation and usage of callback functions until later on, let’s take a look at the syntax right now.
The benefit of passing a function pointer as an argument to another function lies in the fact that this allows us to dictate which function that other function has to execute. There is nothing really different from what we have seen before, except for the fact that it looks slightly different.
// a raw declaration
void foo(int (*FPtr)(char const*));
// using a typedef
typedef int (*FuncPtr)(char const*);
void foo(FuncPtr fptr);
Did you recognize the function passed in through a pointer to foo? It is a function that takes a char const pointer as an argument and returns an int.
int func(char const *param);
Another tricky syntax is the raw declaration of a function that returns a function pointer.
// a raw declaration
int (*getFuncPtr1(int ID))(char const*);
// using a typedef
typedef int (*FuncPtr)(char const*);
FuncPtr getFuncPtr2(int ID);
It is pretty clear that the usage of a typedef clears things up a lot in this case. Did you notice that getFuncPtr1 returns a pointer to the same function (func) we had actually already declared before?
Function Pointer ArraysSince they are stored as variables, it is possible to have an array of function pointers -- but mixing the two guarantees a confusing syntax when you are going to do it all "raw." The beauty of function pointer arrays comes from the fact that you can bind functions to indices, which in turn could come from an enumeration you declared earlier on. This way you can create statements like: int res = (*opArray[PLUS])(a,b);.
// a raw declaration
int (*funcArray1[10])(char const*);
// using a typedef
typedef int (*FuncPtr)(char const*);
FuncPtr funcArray2[10];
// an example assignment
funcArray1[0] = funcArray2[5] = &func;
// example usage
int result1 = (*funcArray1[0])(“a test”);
int result2 = (*funcArray2[5])(“and then some”);
Let’s try to make this even more interesting and take a look at how we define pointers to templated functions. First we need a templated function to point at:
template
T Inc(T var) { return ++var;
}
Unfortunately the C++ standard doesn’t allow us to make template declarations in the scope of function bodies, so we cannot define a pointer to a templated function like this in main():
template
T (*IncPtr)(T) = &Inc;
It is important to understand how compilers instantiate templates as well. They only instantiate templated code when it is needed, and they will not even detect a syntax error if the templated function is never called (a lot of clever and tricky meta template programming makes good use of this compiler behavior). It doesn’t make sense, and hopelessly complicates things for compiler builders, when you try to declare a templated pointer at a point where the compiler is hard at work instantiating code (as in function bodies). When you make a function call, data is going to be moved onto the memory stack, and the compiler needs to know which parameters to use. By trying to template the pointer, we are denying it that very information, and it won’t be able to continue the compilation.
So what is it we can do with the templated function Inc() we have? When we tell the compiler how we are planning to use the function, defining a function pointer to it works fine:
int main(int argc, char *argv[]) {
// Use Inc to increase an integer
int (*incN)(int) = &Inc;
int nres = incN(1);
(void)printf(“result is: %d\n”, nres);
// Use Inc to increase a float
float (*incF)(float) = &Inc;
float fres = incF(1.f);
(void)printf(“result is: %f\n”, fres);
}
When you run this example you will see that it works fine.
So is there no way we can delay the type definition of the function we want to define a function pointer to? Well, there is one location where this is possible: inside a templated function:
template
T Foo(T var) {
T (*incPtr)(T) = &Inc
; return incPtr(var); }
Adding the following line to our example shows that it works fine:
int foores = Foo
(1); (void)printf(“result is: %d\n”, result);
When you think about it, it actually makes sense, because by the time Foo is instantiated, typename T is known by the compiler, and it has no problem instantiating the function pointer to Inc.
There is much more to function pointers than actually meets the eye.
In the next article I will take a look at class member function
pointers and place them in the same context I have done with the
regular function pointers in this article. Finally, in the third
article you will find a callback implementation example, function
pointers mixed with calling conventions and an introduction to C++
functors.
In the previous article, Jun Nakamura introduced you to the use of regular
function pointers, but when you write C++ code, you will be interested in C++
class member function pointers too. It is time to look at how to declare
pointers to the members of classes you write for your applications.
Class Member Function Pointer Syntax The declaration of a C++ member function pointer is a bit trickier
than the regular ones we have seen so far and I have to say they look
pretty strange as well. You might wonder what the use of a member function pointer is. One
of the uses I have found for it is in a protocol definition I wrote for
a server application. The protocol is described in a static struct,
where string messages are tied to pointers to the member functions in
the protocol class that can handle specific messages. This ties the
abstract knowledge of the messages to the lower functionality in the
protocol class. Here is a section of that code: static struct RQRSTable { By now you should be able to see that &ProtocolImpl::login
is the function pointer stored in this struct. It describes which
function has to be called for which header. A simple loop in a parser
function can extrapolate the header from a message, look it up in the
struct and execute the attached function by making a call to Though a function pointer to a member function looks a bit more
complex, it shouldn’t be too difficult to understand how they work now.
Here is the function pointed at by the example: bool ProtocolImpl::login(string const &msg, RQRSTable const *rt); And this is the typedef I used to define the function pointer as used in the example struct: typedef bool (ProtocolImpl::*HandleFunc) It all doesn’t look that different from the regular function pointer
syntax when you take a closer look at it. The main difference lies in
the fact that you have to specify the class scope of the function
pointer, so that the compiler understands that the function pointer is
aimed at the member of a class. Of course there are more ways to declare a member function in a
class, so let’s use this simple class definition to try them out: class MyClass { The function pointer comparable to the one used in the example struct is one that points to void MyClass::SetValue(int): // raw declaration and assignment // utilizing a typedef So you see that the function pointer to a member function only needs ‘MyClass::’
in front of the variable name to declare it (which is probably the
reason why it instinctively looks a bit weird). Member functions can
have more identities than this one so let’s see how we define pointers
to the other functions. // raw declaration and assignment // utilizing a typedef A const member function pointer is defined the same way we have been declaring function pointers up to now: just add const. // raw declaration and assignment // utilizing a typedef The function pointer above may come as a small surprise, because you
would expect the code of an inlined member function to be pasted into
where we normally would find the function call. However when you
declare a member function pointer to an inlined function, the compiler
creates a body for that function so that it can be executed when you
call that member function pointer. // raw declaration and assignment // utilizing a typedef Can you guess which member function is being pointed at by FooPtr without having to go back to the declaration of MyClass? It is pointing at: ‘static void foo (char const * msg, int val );’
Again this may come a bit unexpected, but pointers to static member
functions are declared just like the regular function pointers. And
when you think about it… static member functions can be used just like
regular functions, with them main difference being the fact that you
have to provide the class scope (‘MyClass::’) when calling them. Defining member function pointers is one thing; now let’s see how we
use them. There are some specifics you have to be aware of when calling
member functions in objects. Let’s write some code that tests the above
defined member function pointers. int main (int argc, char *argv[]) { = GValue gvalue = &MyClass::GetValue; InlineFoo inlinefoo = &MyClass::InlineFoo; FooPtr fooptr = &MyClass::foo; It will generate the following output: result is 0x00001234 When you plan to use a member function pointer you will need an
object of the class you are going to use it on. The syntax boils down
to (myobject.*fncptr)(), where <MYOBJECT.> is the name of the instantiated class and <*FNCPTR> is the name of the used member function pointer. How do member function pointers hold up in the face of Polymorphism?
Will the function in a derived class be executed when we use a member
function pointer containing the address of the baseclass function? Let
me show you what I am getting at. A typical way to make good use of polymorphism is to have a
collection of base class pointers that are pointing to instantiated
derived objects. To do this, you have to create the derived object and
use the pointer to its baseclass: class Base { class Deriv1 : public Base { public: int foo() { return 0; } }; // somewhere in your code One of the great benefits of polymorphic classes is that you don’t
have to be familiar with their implementation, only with their
interface as it is declared in the baseclass. In the example above we
know that there is a function called foo()
but we don’t have to know what it exactly does. This can be a very
useful concept for example in a game where there could be a list of
game objects that all implement a run()
function. The main game loop can call this run function on every game
object, without having to know it is dealing with physics, A.I. or
particle system objects. This also means you can add new game objects
that implement different behaviors (and/or visuals) without having to
make changes to the main code base. So basically my question is: “When I use a member function pointer to Base::run() and use it on a Base*
pointer that actually points to a derived game object… will the code in
that game object be executed or will the code in the base object be
executed?” A flipside question is whether it is possible to combine the
member function pointer to Base::run() on a pointer (or reference) to a derived game object… thus not directly on Base itself. Let’s try this out with our Base and Deriv1 and Deriv2 example. typedef int (Base::*BaseFooPtr)(); Running this code you will find that member function pointers behave
the same way you would expect normal function calls to behave in the
face of polymorphism… answering both of my questions. The results are
the same as when we wouldn’t have used a member function pointer but
the regular functions calls instead. Thus result1 is 0 and result2 is 1! It is very well possible that you want to pass a member function
pointer to a function or return one from a function. We have seen that
all we need to do is add the ‘MyClass::’
scope in front of the variable name, so lets look at how this
translates to function parameters and return values. Let’s place the
example function we’ve used before into class MyClass: int MyClass::func(char const *param); Here are some functions that accept a pointer to this member function: // a raw declaration // using a typedef If you compare these function declarations with the ones in the
previous article, you will see that indeed all you need to add is ‘MyClass::’. Declaring a function that returns a function pointer, we do the exact same thing: // a raw declaration // using a typedef I honestly think that the raw declaration above (possibly combined
with a couple of function pointers as parameters) is a good candidate
for usage in an obfuscated C++ coding contest. Do you understand the following function declaration? char const* const ( MyClass::*FuncAt( Lets not even go there ;-) ! Member Function Pointer Arrays Similar to the previous member function pointers described before;
in order to create member function pointer arrays, we only need to add
the class scope to the regular function pointer array declaration. // a raw declaration // using a typedef // initializing ome // and finally making use of it Now that we have seen the many forms function pointers can take, it
is quite understandable that many C++ programming books treat this
topic superficially. I hope I was able to shed some light in this
rather shaded corner of the language and in the concluding article, I
will show you the effect calling conventions have on function pointers,
provide you with an example and introduce you to C++ functors. In previous articles, Jun Nakamura introduced using
regular function pointers, C++ class member function pointers, and
declaring pointers to the members of classes. In this article, he
writes about calling conventions, callback functions, and begins to
talk about using functors. There
are many programming languages out there and a lot of them come with
very useful libraries and APIs. It is possible to tie all these
different libraries (coded in different languages) together in your
main application. Maybe you want to develop the user interface in Visual Basic, need a
performance sensitive engine coded in C and C++ and have some legacy
functionality of which nobody understands the implementation anymore
running in Fortran. As you might have guessed, I have actually worked
for a company that strung applications together that used libraries
like these. Different languages use different calling conventions because there
are many ways to implement function calls on a computer. The main issue
here is the order in which function parameters are passed to the
function when it is called. These parameters are put onto the memory
stack so that the function that is about to execute has access to them.
The next issue is whether the caller or the called function will clean
up these parameters. Though I don’t want to go too deep into calling conventions, stacks etcetera, let me give you a small overview. Alright, here is an overview of how different some Windows calling conventions can be (__pascal, __fortran and __syscall are no longer supported): __cdecl __stdcall __fastcall (applies to Intel CPUs, this is the default calling convention for Borland) thiscall (used automatically by C++ code) You see that there are many ways leading to Rome, and this is just
on a Windows machine. What is interesting for this article though, is
how we declare a function pointer to the following function definition:
void __stdcall Convention(int value, char const *string) Again, the answer here is simple; we simply integrate the calling convention we need into the function pointer declaration: void (__stdcall *ConvPtr)(int, char const*) = &Convention; The principle remains the same whether it is a member function, a
function argument, etc. Just bolt what you need into the declaration. When your linker is giving you errors that it cannot resolve
external symbols while you know that you are linking with the correct
libraries; be sure to check which calling conventions are being used. One of the great concepts of function pointers is that of the
callback function. As you have seen it is pretty easy to transform the
identity of a function into a pointer, which can point to different
implementations of a function with that same identity (they all return
the same type and accept the same type of parameters in the same
sequence). This means that as long as you create a function that
returns the right argument and takes the right argument(s), you can
insert your own functionality into other people’s code. An excellent example of this is a library that can read and render
Macromedia Flash files, but has to run on a lot of different platforms
including game consoles. Since memory management is radically different
from console to console (there are no malloc or free
functions standard on a Nintendo GameCube), the library allows you to
register your own memory allocation and deallocation callback functions
with it. Whenever it needs a piece of memory, it will use these
callbacks, using your memory manager that implements the functionality
they need. The library can focus on what it does best: playing .swf
files and you can take care of the platform specific details without
having to make any changes to the library. Let’s look at an example a bit closer to home: qsort. By now you should be able to understand the following function declaration: void qsort( void *base You will find it in <stdlib.h> and it is a quicksort implementation that sorts the items in base
for you, according to your specification. Now how do you specify it to
order your items when it doesn’t know anything about the type of the
elements? Simple: provide it with a callback function that tells qsort
how to compare two elements with each other. The base address of your list of items is given to qsort through the
void *base parameter. It can then figure out how to iterate through the
items in the list, because you tell it how many items it contains with
num and how big each item is with width. Finally the callback function you provide tells qsort whether an item is smaller than, bigger than, or equal to the other item. The following example (for Microsoft only, because of the use of _stricmp which is not part of the ANSI standard) reads and sorts the command line parameters using qsort and a callback function. #include <stdlib.h> int comp(void const *arg1, void const *args) { int main(int argc, char const *argv[]) Callback functions can also be very useful when you want to bind a
token to a function. One of the examples I have shown you before in the
previous article, contains a struct named RQRSTable. It contains a member named function and another one named header.
Declared in a .cpp source file, structs like these allow you to
configure your token/function binding centrally in the source code.
They generally take on the following form: static struct <StructName> { Now that you have a static data object that can describe which
function has to execute when a certain token is used. This can be very
useful when you are parsing messages, upon which action has to be
taken. If you don’t want to iterate to the array sequentially, you can
always store the data in a hash table (like std::map) upon
initialization and use the token as a key. The function pointers in these structs are also callback functions. This is because, just like qsort,
the function using them binds data to functions dynamically at runtime.
This level of abstraction is very powerful because it allows you to
create generic functions like qsort, platform independent libraries
etc. C++ has more tricks up its sleeve though. Allow me introduce you to functors. Regular function pointers are quite easy to use, but as soon as you
start using member function pointers, you need an object to use them
on. They are no good to you otherwise. You can also store both of them in a Functor and treat that functor object as a replacement function instead. How does an object behave like a function? You overload operator ()
in a polymorphic MyFunctor base class from which we will implement
derived functors. This way our main application can deal with MyFunctor
without having to know what kind of specific derived implementations we
have provided it. You can give it functors with extra debugging
functionality or performance enhanced ones etc. The application won’t
care; it will just call operator (). // the abstract baseclass // derived implementation int operator ()(char const *str) { The constructor of DerFunctor
takes a pointer to the object it can use the member function pointer
on, which it receives as an argument as well. When we call operator () on an instantiated DerFunctor, it then executes that member function pointer on that object. Easy, isn’t it? Here is how we can make good use of this. class Dummy1 { class Dummy2 { int main (int argc, char const *argv[]) { // make the call This functor example is extremely simplified and there is a lot more
to be discussed. The base class I provided here for example, isn’t
really generic and you won’t be able to reuse it really. Since these
articles were about function pointers I will save the functors for
another one.
unsigned int privileges;
ProtocolImpl::RQType requestType;
HandleFunc function;
string header;
} rqrsTable[] =
{
// LOGIN -------------------------------------------------
// (CLT)RQ::LOGIN::<`username(str)`>
// example RQ::LOGIN::`Jun`
{
Read | Write | Admin, // privileges
ProtocolImpl::Request, // request type
&ProtocolImpl::login, // function
“CLT::RS::LOGIN” // header
},
...
};
rqrsTable[idx].function().
(string const&, struct RQRSTable*);
public:
MyClass() : m_value(0) {}
void SetValue(int value) { m_value=value; }
int GetValue() const { return m_value; }
inline void InlineFoo() { (void)printf(“inlined\n”); }
static void foo(char const *msg, int value)
{ (void)printf(“%s %d\n”, msg, value); }
private:
int m_value;
};
void (MyClass::*svalue)(int) = &MyClass::SetValue;
typedef void (MyClass:*SValue)(int);
SValue svalue = &MyClass::SetValue;
int (MyClass::*gvalue)() const = &MyClass::GetValue;
typedef void (MyClass::GValue)() const;
GValue gvalue = &MyClass::GetValue;
void (MyClass::*inlfoo)() = &MyClass::InlineFoo;
typedef void (MyClass::*InlFoo)();
InlFoo infloo = &MyClass::InlineFoo;
void (*foo)(char const*, int) = &MyClass::foo;
typedef void (*FooPtr)(char const*, int);
FooPtr foo = &MyClass::foo;
MyClass myclass;
SValue svalue = &MyClass::SetValue;
(myclass.*svalue)(0x1234);
int res = (myclass.*gvalue)();
(void)printf(“result is 0x%d\n”, res);
(myclass.*inlinefoo)();
Fooptr(“result is: “, (myclass.*gvalue)());
}
inlined
result is: 4660
public:
virtual ~Base()=0;
virtual int foo() const=0;
};
class Deriv2 : public Base { public: int foo() { return 1; } };
Base *ptr1 = new Deriv1;
int result = ptr1->foo(); // result will be 0
// ... later
BaseFooPtr fooptr = &Base::foo;
// ... test1 – try the BaseFooPtr on Base* to a Deriv1 object.
Base *baseptr = new Deriv1;
int result1 = (baseptr->*fooptr)();
// ... test2 – try the BaseFooPtr on a Deriv2 object.
Deriv2 myDeriv2;
Int result2 = (myDeriv2.*fooptr)();
void foo(int (MyClass::*FPtr)(char const*));
typedef int (MyClass::*FuncPtr)(char const*);
void foo(FuncPtr fptr);
int (MyClass::*getFuncPtr1(int ID))(char const*);
typedef int (MyClass::*FuncPtr)(char const*);
FuncPtr GetFuncPtr2(int ID);
void (MyClass::*Fptr1)(char, char),
float (MyClass::*Fptr2)(int) ))(char const*, int);
int (MyClass::*funcArray1[10])(char const*);
typedef int (MyClass::*FuncPtr)(char const*);
FuncPtr funcArray2[10];
funcArray1[0] = &MyClass::func;
MyClass myClass;
int result1 = (MyClass.*funcArray1[0])(“a test”);
Argument Passing: right to left
Stack Maintenance: Calling function pops arguments from the stack
Name Decoration (C only): ‘_’ prefixed to function names (e.g. ‘_foo’)
Argument Passing: right to left
Stack Maintenance: Called function pops its own arguments from the stack
Name Decoration (C only): ‘_’ prefixed to function name, ‘@’ appended followed by the number of decimal bytes in the argument list. (e.g. ‘_foo@10’)
Argument Passing: First two DWORD arguments are passed in ECX and EDX, the rest is passed right to left
Stack Maintenance: Called function pops its own arguments from the stack
Name Decoration (C only): ‘@’ is prefixed to the name, ‘@’ appended followed by the number decimal bytes in the argument list. (e.g. ‘@foo@10’)
Argument Passing: ‘this’ pointer put in ECX, arguments passed right to left
Stack Maintenance: Calling function pops arguments from the stack
Name Decoration: None
{
(void)printf(“%d, ‘%s’\n”, value, string);
}
, size_t num
, size_t width
, int (__cdecl *comp)
(const void* e1, const void* e2)
);
#include <string.h>
#include <stdio.h>
// case insensitive comparison
return _stricmp( *(char**)arg1, *(char**)arg2);
}
{
// remove application name from argv
++argv;
--argc;
// sort remaining arguments
qsort((void*)argv, (size_t)argc, sizeof(char*), comp);
// display result
for (int idx=0; idx<argc; ++idx)
(void)printf(“%s “, argv[idx]);
(void)printf(“\n”);
}
<member1 type> <member1 name>;
...
<memberN-1 type> <memberN-1 name>;
<memberN type> <memberN name>;
} <array name>[] =
{ { <member value>, ..., <memberN-1 value>, <memberN value> }
, { <member value>, ..., <memberN-1 value>, <memberN value> }
...
};
class MyFunctor {
public:
virtual ~MyFunctor() {}
virtual int operator ()(char const *str)=0;
};
template <class T> class DerFunctor : public MyFunctor {
public:
DerFunctor(T* pObj, int (T::*funcPtr)(const char*))
: m_funcPtr(funcPtr), m_pObject(pObj) {}
return (m_pObject->*m_funcPtr)(str);
}
private:
int (T::*m_funcPtr)(const char*); // member func.ptr.
T* m_pObject; // ptr. to object.
};
public:
int Run(char const *str) { /*...*/ return 0; }
};
public:
int Run(char const *str) { /*...*/ return 1;
};
// here are the objects
Dummy1 dummy1;
Dummy2 dummy2;
// here are the functors
DerFunctor<Dummy1> functor1(&dummy1, &Dummy1::Run);
DerFunctor<Dummy2> functor2(&dummy2, &Dummy2::Run);
// put them in an array to make it more interesting \
MyFunctor* vtable[] = { &functor1, &functor2 };
int res1 = functor1(“this is a test”);
int res2 = (*vtable[1])(“this is a test”);
}
- Multithreading in C++ (0)2007/07/30
- A Simple Garbage Collector for C++ (0)2007/07/30
- Function Pointers (1)2007/07/30
- DLL Conventions: Issues and Solutions (0)2007/07/27
- More on Handling Basic Data Types (0)2007/07/27

수안이의 컴퓨터 연구실



Leave your greetings.
C++ 콜백함수에 대한 자료를 찾다가 여기까지 오게 되었네요. 좋은 자료 잘 보았구요.
2008/01/21 01:55 [ Permalink : Modify/Delete : Reply ]링크된 사이트에서 Part1/2/3 모두 잘 읽었습니다. SETI에 대해서 다시한번 더 알게 되었구요.
모쪼록 성공하시길 빕니다.