Compiler Error
-
Hi, I have a bit of code that works fine in non MFC C++ project. However when converting to MFC code I get the infamous C2664 compiler error. This is what I'm trying to do:
double CWork::Equation(double x[]) { return (100*(x[1]-x[0]*x[0])*(x[1]-x[0]*x[0])+(1.0-x[0])*(1.0-x[0])); } double CWork::DriverFunc(double (*dblfunc)(double[]), double start[]) { min = lots of operations; return min; } void CWork::WorkIt() { double start[] = {1.,2.,3.}; double min; int i; min=DriverFunc(Equation,start); }
This is the error: Work.cpp(1041) : error C2664: 'DriverFunc' : cannot convert parameter 1 from 'double (double []) const' to 'double (__cdecl *)(double [])' Any suggestions would be great. Kash -
Hi, I have a bit of code that works fine in non MFC C++ project. However when converting to MFC code I get the infamous C2664 compiler error. This is what I'm trying to do:
double CWork::Equation(double x[]) { return (100*(x[1]-x[0]*x[0])*(x[1]-x[0]*x[0])+(1.0-x[0])*(1.0-x[0])); } double CWork::DriverFunc(double (*dblfunc)(double[]), double start[]) { min = lots of operations; return min; } void CWork::WorkIt() { double start[] = {1.,2.,3.}; double min; int i; min=DriverFunc(Equation,start); }
This is the error: Work.cpp(1041) : error C2664: 'DriverFunc' : cannot convert parameter 1 from 'double (double []) const' to 'double (__cdecl *)(double [])' Any suggestions would be great. KashYes. You are trying to send a MEMBER FUNCTION as a "regular" (global-scope) function to CWork::DriverFunc(). You can't send pointers to member functions as "regularly" as you send pointers to "regular" functions. The discussion on this is long, and you would have no problems finding the reasons on the web. It's too long to be included here. In any case, you have three basic options: 1) If you don't care that DriverFunc() will get a CWork-method (rather than "any" function returning double and accepting a double-array), you can change it's prototype to: typedef double (*CWork::dblfunc)(double[]); double CWork::DriverFunc( dblfunc func, double start[] ) { ... } 2) Instead of passing "Equation" to DriverFunc, pass an intermediate function which will call Equation. This function will be a static member of CWork, so even if Equation() is "hidden" it will still be able to call it. This also means you'll have to pass "this". Here's how it goes: static CWork::CallEquation( CWork* That, RestOfParamsForEquation ) { That->Equation( RestOfParamsForEquation ); } and then: typedef double (*WrapperForEquation)( CWork* That, RestOfParamsForEquation ); CWork::DriverFunc( WrapperForEquation w, double start[] ) { ... ( Whever you want to call the passed function, use w(this,ParamsYouWouldLikeToPass) ) } And then in WorkIt(), call DriverFunc with CallEquation() as the first argument 3) The third option is THE ugliest by far, slightly more complex, but does EXACTLY what you want. It will only work if Equation() satisfies a list of requirements (non-virtual, non-static), is NOT portable, and like I said: ugly. But it actual usage it's the simplest, and I like it... The idea is basically to circumnavigate the compiler's type checking system (which is what yells at you in the first place), which WILL allow you get the address of CWork::Equation. You would have to use the value with care, but it can be done. I'll elaborate more if you want, but like I said: it's ugly... Good luck. -- Calius
-
Yes. You are trying to send a MEMBER FUNCTION as a "regular" (global-scope) function to CWork::DriverFunc(). You can't send pointers to member functions as "regularly" as you send pointers to "regular" functions. The discussion on this is long, and you would have no problems finding the reasons on the web. It's too long to be included here. In any case, you have three basic options: 1) If you don't care that DriverFunc() will get a CWork-method (rather than "any" function returning double and accepting a double-array), you can change it's prototype to: typedef double (*CWork::dblfunc)(double[]); double CWork::DriverFunc( dblfunc func, double start[] ) { ... } 2) Instead of passing "Equation" to DriverFunc, pass an intermediate function which will call Equation. This function will be a static member of CWork, so even if Equation() is "hidden" it will still be able to call it. This also means you'll have to pass "this". Here's how it goes: static CWork::CallEquation( CWork* That, RestOfParamsForEquation ) { That->Equation( RestOfParamsForEquation ); } and then: typedef double (*WrapperForEquation)( CWork* That, RestOfParamsForEquation ); CWork::DriverFunc( WrapperForEquation w, double start[] ) { ... ( Whever you want to call the passed function, use w(this,ParamsYouWouldLikeToPass) ) } And then in WorkIt(), call DriverFunc with CallEquation() as the first argument 3) The third option is THE ugliest by far, slightly more complex, but does EXACTLY what you want. It will only work if Equation() satisfies a list of requirements (non-virtual, non-static), is NOT portable, and like I said: ugly. But it actual usage it's the simplest, and I like it... The idea is basically to circumnavigate the compiler's type checking system (which is what yells at you in the first place), which WILL allow you get the address of CWork::Equation. You would have to use the value with care, but it can be done. I'll elaborate more if you want, but like I said: it's ugly... Good luck. -- Calius
-
Simple solutions have their own beauty :) In Equation(), I will eventually need to use member variables from my CWork class so I think the first method won't fit the bill. Please elaborate on the third option. Kash
All options will work for you, since all options will result with DriverFunc() being called on the correct object. The way I usually do it is option (2), actually. It's a small price to pay (1 line of code) for the generic mechanism you gain. Just in case we're not talking about the same thing when we're saying "option (2)", here's what I mean (in an all-madeup example): Let's say you have a function (or a method of some class) which needs to call some "callback" function with three parameters: an int, a double, and a char. It's customary anyways to add a "context" or "userdata" argument to such callback functions (even in plain C) so you can use the same function for several callbacks, and each of the callers will pass a different first argument ("userdata") according to what was requested when the cb func was registered. In our case we replace this "context" / "user data" with a pointer to the actual C++ instance. We define a type for the cb func: ClassMethodCbFunc -- it gets our int,double,char arguments, plus another parameter which will become the "this". ------------------------------------------------------------------------------------------- typedef int (*ClassMethodCbFunc)( void* This, int Param1, double Param2, char Param3 ); // (Note: "This" and not "this" !) ------------------------------------------------------------------------------------------- The function (or some class method) which wants to use this type looks like: ------------------------------------------------------------------------------------------- void FunctionWhichCallsMethodCb( ClassMerhodCbFunc Func, void* This ) { ... int Res = Func( This, 1, 2.0, 'c' ); ... } ------------------------------------------------------------------------------------------- ... that is: it calls it's "Func" argument with the "This" parameter it receives, and the values for Param1, Param2 and Param3 can be anything. ("Func" and "This" go together: when one is passed for a func to another, so will the other one. Just like the "CbFunc" and "userdata" in "plain" C). Now for the class which actually contains the method that will eventually be called: ------------------------------------------------------------------------------------------- class CMyClass { public: int MemthodBeingCalled( int Param1, double Param2, char Param3 ) { return Param1 + Param2 + Param3; } static int MethodBeingCalled__Wrapper( void* This, int Param1, int Param2, char Param3 ) { return
-
All options will work for you, since all options will result with DriverFunc() being called on the correct object. The way I usually do it is option (2), actually. It's a small price to pay (1 line of code) for the generic mechanism you gain. Just in case we're not talking about the same thing when we're saying "option (2)", here's what I mean (in an all-madeup example): Let's say you have a function (or a method of some class) which needs to call some "callback" function with three parameters: an int, a double, and a char. It's customary anyways to add a "context" or "userdata" argument to such callback functions (even in plain C) so you can use the same function for several callbacks, and each of the callers will pass a different first argument ("userdata") according to what was requested when the cb func was registered. In our case we replace this "context" / "user data" with a pointer to the actual C++ instance. We define a type for the cb func: ClassMethodCbFunc -- it gets our int,double,char arguments, plus another parameter which will become the "this". ------------------------------------------------------------------------------------------- typedef int (*ClassMethodCbFunc)( void* This, int Param1, double Param2, char Param3 ); // (Note: "This" and not "this" !) ------------------------------------------------------------------------------------------- The function (or some class method) which wants to use this type looks like: ------------------------------------------------------------------------------------------- void FunctionWhichCallsMethodCb( ClassMerhodCbFunc Func, void* This ) { ... int Res = Func( This, 1, 2.0, 'c' ); ... } ------------------------------------------------------------------------------------------- ... that is: it calls it's "Func" argument with the "This" parameter it receives, and the values for Param1, Param2 and Param3 can be anything. ("Func" and "This" go together: when one is passed for a func to another, so will the other one. Just like the "CbFunc" and "userdata" in "plain" C). Now for the class which actually contains the method that will eventually be called: ------------------------------------------------------------------------------------------- class CMyClass { public: int MemthodBeingCalled( int Param1, double Param2, char Param3 ) { return Param1 + Param2 + Param3; } static int MethodBeingCalled__Wrapper( void* This, int Param1, int Param2, char Param3 ) { return
Thanks for your help. I was just thinking whilst reading half way down, that you should write an article on this subject. People will definitely benefit from this. I solved the problem using ideas from your reply and now all seems to work fine. Thanks again. Kash