Function Pointers in c++

Started by
9 comments, last by nmi 16 years, 9 months ago
Hi all, I am building an error logging system for my program and I have run into a few problems. First of all, I am programming in the Linux environment using g++ 4.1.2 in ubuntu. I wrote a class called ErrorLogger that has a public member function called LogError. All of my other classes have a function pointer so that I can allow them access to LogError. So what my intention is to not integrate ErrorLogger into the rest of my classes because if I changed ErrorLogger it would become a headache to change them all. Here is what I am doing -ErrorLogger object is stored globally (for now) -I instantiate a class object from my library and set the function pointer to ErrorLogger::LogError ErrorLogger ErrLogr; int main() { ClassA NewObject; NewObject.SetErrorFunc(&ErrorLogger::LogError); NewObject.ErrorFuncPointer(arg1,arg2); ..... } The compiler complains : invalid use of non-static member function ErrorLogger::LogError How can I make it so that the global ErrLogr member function LogError is specifically called instead of just the generic function?
"Only two things are infinite, the universe and human stupidity, and I'm not sure about the former." - Albert Einstein (1879-1955) That is so very true...
Advertisement
Grandiloquent
I don't know how C++ does method pointers (though, it's sure to be weird).

You could try doing it the Java-approved way. Instead of passing a function pointer, pass an object which implements an HandleError interface. Then, you can simply call the virtual HandleError method... which will either be your old method or your new one.
Why do you only pass the function as an argument, when it would make just as much sense to pass the logger itself?

Ultimately, your problem comes from trying to use a member function pointer as if it were a normal function pointer. Check your favourite book, tutorial or search engine for the massive and unavoidable difference between the two.
Thanks, ToohrVyk

The reason why I do not pass the logger itself is that I don't want to have to integrate the error logger into all of my classes. If I were to make a change to a function name or write a new logger, I don't want to have to go through all of the classes to change the implementation. I would rather just call a function through a generic function pointer with a set amount and type of arguments.
Thanks again, I am totally clear on the difference between the two, just wasn't sure about the syntax.

Thanks for the reference Sneftel, it has some good info with member function pointers that I didn't have in my book. I will try this again using different syntax.
"Only two things are infinite, the universe and human stupidity, and I'm not sure about the former." - Albert Einstein (1879-1955) That is so very true...
In that case, you can use a (smart) pointer to a virtual base class that offers the logger functionality. Then you only need to recompile the clients if the logger interface changes, which you'd have to do anyway with function pointers.
Couldn't I just also use a namespace?
I know those can get kind of hairy but that might make things a little easier to access.

I have a feeling that not many people would support using namespaces..
"Only two things are infinite, the universe and human stupidity, and I'm not sure about the former." - Albert Einstein (1879-1955) That is so very true...
As far as maintenance, it should be identical to use either a logger interface, or a typedef'd function pointer - IF you create a header file for such a thing that does not include other, unrelated information.

IE, if you have a header ILogger.h with a class ILogger { public: void LogError(/*parameters*/); };

or a file LogErrorFunc.h with typdef void (*LogErrorFunc)(/*parameters*/);

each should be just as resistent to future changes as each other - with 1 exception. Its just a decision if you like someone having the option of implementing logging as a standalone non-member function (like for instance a C client, or other language that can export C functions), or if you like them having the option of writing their logger class to contain additional state useful to logging (such as filters) without having to resort to global / static tricks.

I personally use the ILogger method in my system, because nearly all usefull loggers have some basic state it might keep cached when appropriate (for instance a file based logger has a path or file, a database logger a connection, etc).

Good luck.
Quote:each should be just as resistent to future changes as each other - with 1 exception. Its just a decision if you like someone having the option of implementing logging as a standalone non-member function (like for instance a C client, or other language that can export C functions), or if you like them having the option of writing their logger class to contain additional state useful to logging (such as filters) without having to resort to global / static tricks.


In the case that compatibility with clients using C is an issue I would go with the function version but #ifdef it to use boost.function when in c++ so that c++ clients can use boost.bind or std::mem_fn and std::bind1st/std::bind2nd to associate state with it.
#ifdef __cplusplus#include <boost/function.hpp>typedef boost::function<void (/* Params */)> error_log_function;#elsetypedef void (*error_log_function)(/* Params */);#endif
You could consider using a thin adaptor class as the interface to your logger.
template <typename ARG1_TYPE, typename ARG2_TYPE>class ErrorLogInterface{	public:		virtual void operator()(ARG1_TYPE arg1, ARG2_TYPE arg2) = 0;};template <typename LOGGER_TYPE, typename ARG1_TYPE, typename ARG2_TYPE>class ErrorLogAdaptor : public ErrorLogInterface<ARG1_TYPE, ARG2_TYPE>{	public:		typedef void (LOGGER_TYPE::*pointer)(ARG1_TYPE arg1, ARG2_TYPE arg2);	private:		pointer p;	public:		ErrorLogAdaptor(pointer _pf):p(_pf){}		void operator()(ARG1_TYPE arg1, ARG2_TYPE arg2){			p(arg1,arg2);		}};

Usage: pass the address of an ErrorLogAdaptor to each class that can access the logger. In each of these classes, store it in an ErrorLogInterface pointer and access it through this pointer. You declare a single instance of your ErrorLogAdaptor as in
ErrorLogAdaptor<ErrorLogger,ErrorLogger::arg1_type,ErrorLogger::arg2_type> ErrLogr(&ErrorLogger::LogError);

This topic is closed to new replies.

Advertisement