|
error C2562: '()' : 'void' function returning a value
|
|
|
|
|
1. In order to leverage the flexibility of Functor both objects and methods should be (re-) setable. What I mean is best explained in an example (BTW, operator() should be const!)
#include <stdexcept>
template <typename Class, typename Return, typename Arg1>
class Functor {
public:
typedef Return (Class::*FunctionPointer)(Arg1);
Functor() : object_(0), function_(0) {}
Functor(Class* obj, FunctionPointer ptr) : object_(obj), function_(ptr) {}
Return operator()(Arg1 arg1) const {
if(! (object_ && function_)) {
throw std::runtime_error("XX");
}
return (object_->*function_)(arg1);
}
void set (Class* obj) { object_ = obj; }
void set (Functor::FunctionPointer ptr) { function_ = ptr; }
private:
Class* object_;
FunctionPointer function_;
};
The following function calls different member-functions on each object in a collection (an array) of objects;
class Test { ... };
typedef Functor<Test, int, const int&> Function;
void TestFunc3() {
printf ("TestFunc3\n");
Test t1, t2, t3;
Test tests[] = { t1, t2, t3 };
int nTest = sizeof(tests) / sizeof(tests[0]);
Function::FunctionPointer funcs[] = { &Test::Add, &Test::Mul };
int nFuncs = sizeof(funcs) / sizeof(funcs[0]);
Function f;
for (int i = 0; i < nTest; ++i) {
f.set (&tests[i]);
for (int k = 0; k < nFuncs; ++k) {
f.set (funcs[k]);
f(5);
}
}
}
2. Functor is polymorphic. You should mention that you can specify a base class as template argument and pass a derived object to the constructor:
typedef Functor <BaseClass, int, int> Function;
Function myadd (derivedObject, &Test::Add);
|
|
|
|
|
First let me say that I find your work instructive and comprehensible.
There are more sophisticated approaches
(eg. http://www.cuj.com/documents/s=8464/cujcexp0308sutter/)
which I'm not really fond of (typical Boost code).
Hints to make Functor compile with VC++ 6.0:
- #include <stdexcept>
- void return types don't work with VC++ 6.0 (probably a compiler bug)
- templates must be inline (in VC++ 6.0), see: http://www.decadentplace.org.uk/womble/cplusplus/template-faq.html#vc6-non-inline-mem
I don't quite understand your rationale with respect to the one-argument
constructor (ie. Functor(Class* cls) : class_(cls), function_(NULL)) and operator=
(ie. operator=(typename Functor::FunctionPointer ptr)). I left them out in
my destilled version that now looks like this (default copy ctor and operator= are ok):
#include <stdexcept>
template <typename Class, typename Return, typename Arg1>
class Functor {
public:
typedef Return (Class::*FunctionPointer)(Arg1);
Functor() : object_(0), function_(0) {}
Functor(Class* obj, FunctionPointer ptr) : object_(obj), function_(ptr) {}
Return operator()(Arg1 arg1) {
if(! (object_ && function_)) {
throw std::runtime_error("XX");
}
return (object_->*function_)(arg1);
}
private:
Class* object_;
FunctionPointer function_;
};
Unfortunately const functions cannot be used with the Functor template. You need an
extra template for const functions (maybe this can be overcome with template tricks):
template <typename Class, typename Return, typename Arg1>
class FunctorConst {
public:
typedef Return (Class::*FunctionPointer)(Arg1) const;
FunctorConst() : object_(0), function_(0) {}
FunctorConst(const Class* obj, FunctionPointer ptr)
: object_(obj), function_(ptr) {}
Return operator()(Arg1 arg1) {
if(! (object_ && function_)) {
throw std::runtime_error("XX");
}
return (object_->*function_)(arg1);
}
private:
const Class* object_;
FunctionPointer function_;
};
ToDos (for you ):
- Examples, especially examples with const and/or refernce argument and return types
- Functor templates for 0 - 8 arguments
I hope you find time to refactor code and article (keep it simple, don't boost the code).
It's a "Nice topic" and there's "a lot of room for improvement though" (Kevin Hall).
Best wishes,
Roland Pibinger
PS.: This has been my test code (compiles with VC++ 6.0, BCC 5.5, gcc 3.2(MingW)):
#include <stdio.h>
#include "functor.h"
class Test {
public:
Test() : value_(0) {}
int Add (const int& x) { printf ("Add\n"); value_ += x; return value_; }
int Mul (const int& x) { printf ("Mul\n"); value_ *= x; return value_; }
int Foo (const int& x) const { printf ("Foo\n"); return value_; }
private:
int value_;
};
typedef Functor<Test, int, const int&> Function;
typedef FunctorConst<Test, int, const int&> FunctionConst;
void DoSomething (int x, Function f) {f(x);}
void DoSomething (int x, FunctionConst f) {f(x);}
void TestFunc1() {
printf ("TestFunc1\n");
Test t1;
Function myadd (&t1, &Test::Add);
Function mymul (&t1, &Test::Mul);
Function myfuncTest;
DoSomething (5, myadd);
DoSomething (5, mymul);
Function myfunc (myadd);
DoSomething (5, myfunc);
myfunc = mymul;
DoSomething (5, myfunc);
}
void TestFunc2() {
printf ("TestFunc2\n");
Test t2;
FunctionConst myconst (&t2, &Test::Foo);
DoSomething (5, myconst);
FunctionConst myfuncConst;
myfuncConst = myconst;
DoSomething (5, myfuncConst);
}
int main() {
TestFunc1();
TestFunc2();
return 0;
}
|
|
|
|
|
(1) "Test::TestFunction()" should be "Test::TestFunc()"
(2) Within the definition of Test::TestFunc(), you should replace "Function myadd(this, Add);" with "Function myadd(this, &Test::Add);". More standard-compliant compilers will report errors otherwise.
(3) This is more of a suggestion. When you mention typedef'ing "Function", you should mention that the typedef will need to take place inside the Test class.
(4) Another suggestion: include a sample project based off the code you show in the article.
|
|
|
|
|
Actually, you are passing the return value of your inner functions as arguments to the outer function. You are not passing the functions themselves.
Function overloading is a powerful feature, but is a compile-time feature. Passing function pointers (and functors in general) are run-time features (much like polymorphism is a run-time feature). Anyway, I suggest you examine some articles about functors and see (a) why they are useful and (b) how people implement them.
|
|
|
|
|
You are correct and I'm withdrawing my comment.
With regard to my examination of articles on "functors", yes, I DO KNOW about them and their primary use to save state between calls (especially among multiple users), they are also multithread-safe, and are mainly implemented through the use of the "call" operator. Particularly, they cleverly allow an object to act as a function, but it's because they are objects, is why they are uniquely able to save state between calls. Anything else?
If I've missed out on any, kindly fill me in on them (other than they are runtime features which is a "given" since that's when objects are instantiated).
William
Fortes in fide et opere!
|
|
|
|
|
"If I've missed out on any, kindly fill me in..."
I don't know if you really missed out on anything. You described things in a pretty general way. I'm sure we could write a whole book on specific applications of functors.
Maybe you know this, and maybe you don't, but, one of the states that a functor can maintain is the function (or another functor) that it is supposed to call -- much like you would use a function pointer in C. So why not just use a function pointer? Well, a well written functor (which I don't think I would be able to come up with on my own) can connect to a function, a class member function, or another functor. I mean the SAME object instatiation can choose between the three at run-time. That's pretty amazing. Of course you may not need that flexibility. But it's nice to have if you do!
|
|
|
|
|
As you said, "That's pretty amazing." I'll have to experiment with that one day!!
I don't know about you, but one of the things that always get a bit mirky for me at times (which subsequently never fails to force me to ponder their respective uses before going forward), is the ability to distinguish the overall benefits of using a functor over that of a smart pointer.
Smart pointers are pretty smart, and can be insufferably so (depending on the level of creativity of the programmer). The same can be also said of functors, which is what never fails to cause me to ponder when should I use one over the other.
Your thoughts on that point would be appreciated.
In a way, I often wonder whether C++ programmers might not be gluttons for pain and torture!!
William
Fortes in fide et opere!
|
|
|
|
|
"In a way, I often wonder whether C++ programmers might not be gluttons for pain and torture!!"
Yes! I think you're right!!!
I suppose there are some applications that could be served by either smart pointers or functors. However, I think usually the concept of what you are trying to do more closely relates to one category. If you are dealing with something that acts like a pointer to an object, use a smart pointer. If you are using something that needs to act like a function or a pointer to a function, use a functor. That's my take. Of course you'll have to evaluate these situations yourself.
|
|
|
|
|
Preliminary investigation into how a functor would be able to differentiate the state to which it should connect to, has something to do with the multiple overloading of the "call" operator inside the class.
This can be done by designating a different argument (meaning, a different signature for each of the "call" operators) inside the second "()" of the operator function. In that case, when the functor is invoked, it's the function overloading mechanism which causes it to determine which of the several definitions (meaning, which of the different signatures) it should execute.
Confer Jeff Alger's "Secrets of the C++ Masters", page 152.
William
Fortes in fide et opere!
|
|
|
|
|
Like KevinHall said - this is just passing the return value of functions.
The usage of function passing if more complex and actually very useful in some problems. If you once program in languages like Scheme and Lisp you'll certainly know what I mean
|
|
|
|
|
"Funtion pointers are a good system for C, but not for C++, you have to define a non-class member or static class member... So we cannot access non-static data members of a class without using global variables."
You don't need global variables. That's why C++ provides pointer to member operators ".*" and "->*" -- which you use in your code.
Also, you have started an interesting idea about using templates for functors. But there can be a lot of improvement here. Have you checked out the Loki library? Alexi Alexandrescu has covered this very topic (and discussed it in his book "Modern C++ Design"), and provided a very flexible and capable functor template. The template can handle normal function pointers, other functors, and class function pointers (via the pointer to member operators), and will work with any number of arguments (via a typelist). It also allows other features such as argument conversion:
const char* functionToWrap(double, double);
Loki::Functor<std::string, typelist_2(int,="" int)=""> aFunctor(functionToWrap);
std::string s = aFunctor(3,5);
The int arguments are convertible to doubles, and the returned const char* is convertible to std::string, and because of this the code compiles correctly without any errors.
This is the best darn functor template I have seen in C++. The only downside is there are a few layers of indirection which translates into a couple extra function calls in the call stack. Oh well, that's what you pay for flexibility.
- Kevin
[EDIT] By the way, I was NOT the one that gave the low vote!
|
|
|
|
|
Kevin, although I agree with the general senso of your statement, it seemed to me there is an improper comparison between two clearly differently orieted solution.
How many lines of code there are in the loki library ?
How many in this 2KB solution ?
I'm still having the feeling that "things" (A template library is NOT a library, at least if for library we mean "compiled code". Especially if we compare to C) like "loki" or "boost" have a completly different scope, not be be compared with the "quick and dirty" solutions easy to be implemented to solve a specific problem in a fast way (and fast, means also fast to compile !)
Alexandrescu book is like a "Bible".
And like a "Bbible", can become dangerous if read by zealots!
Don't make "loki" a religion !
2 bugs found.
> recompile ...
65534 bugs found.
|
|
|
|
|
Yes, yes, yes.... I actually don't use the boost library because it is so big. The difference with the Loki library is that there are only two .CPP files, and using functors with default policies only requires that you compile one. If you switch up the policies (specifically the allocator policy), then you don't need to compile any .CPP files from Loki. So, yes, it is a library that is perhaps larger than you need, but since it is 95% header files and templates, there is very little overhead to your final application -- even for small functions.
And maybe I do seem a little zealous for the Loki library, but I agree that libraries must be used carefully. But regardless, the author of the article can still learn a great deal by looking at the Loki library and seeing how certain other things can be implemented. Forgetting typelists and allocators, he can still come up with a lean and mean functor library that has much more functionality than the one he posted. And I wasn't trying to get down on the author -- this is a good subject and I'm glad he experimented with it.... I just think he can improve his library by looking at other implementations (or he can improve the flexibility by using a lightweight library that already exists [ok, I'll stop promoting other libraries now ]).
|
|
|
|
|
There is one point I don't agree (but ... may br I misunderstood)
KevinHall wrote:
since it is 95% header files and templates, there is very little overhead to your final application -- even for small functions.
That's correct: template means ... nothing, if don't intantiate them.
Or .. they ARE nothing for the executable, but not for the compiler, that has always to deal with all the symbols the template declares.
It's not a problem "of" loki, but it's a problem "with" loki (and also even with WTL, sometimes ...) that doesn't affect the product, but the production process.
The solution requires compilers able to "translate" into obj-s also the generic code. But that's far from now
2 bugs found.
> recompile ...
65534 bugs found.
|
|
|
|
|
Granted, the compilers must deal with the symbols. I give you that. However, Loki is a rather small library compared to others. A small sample application using Loki functors will still compile in well under one second. That is definitely NOT like boost!
Out of curiosity, have you ever looked at it or its sources? There are really only a about 15 files for a given platform, and unlike boost and other large libraries, it is not littered with #if's everywhere. If you needed to extract something small, it's very easy to do so!
As far as compilers being able to "translate" template code into obj-s: you're right that's far off. But Loki is small and doesn't add that much to compilation time.
|
|
|
|
|
"Have you checked out the Loki library?"
No I didn't, but now I saw your reply I'll certainly check it out!
The number of arguments are idd a problem here, I didn't had the time yet to create something for any number of arguments
"The int arguments are convertible to doubles, and the returned const char* is convertible to std::string, and because of this the code compiles correctly without any errors."
Actually this has not much to do with Loki, but more with the C++ compiler, your compiler can convert doubles to ints and const char* to std::string object without any problem
Thx for you comments!
- Kenny
|
|
|
|
|
"Actually this has not much to do with Loki, but more with the C++ compiler, your compiler can convert doubles to ints and const char* to std::string object without any problem "
Yes, the C++ normally does this, but the compiler cannot do the conversions for function signatures. For example, this code will not compile with your library:
#include "/functor.h"
class Test
{
public:
typedef Functor<Test, void, int> Function;
void TestFunc();
private:
void Add(int x);
void Mul(double x);
void DoSomething(int x, Function f);
int value_;
};
void Test::Add(int x)
{
value_ += x;
}
void Test::Mul(double x)
{
value_ = int(value_*x);
}
void Test::DoSomething(int x, Function f)
{
f(x);
}
void Test::TestFunc()
{
Function myadd(this, &Test::Add);
Function mymul(this, &Test::Mul);
DoSomething(5, myadd);
}
|
|
|
|
|