I have a comparison/ordering function that relates to a class. I can use it if I define it as a separate closure object. I would like to make it into a static method of the class it operates on so it is tidier. I guessed how to do this but I get an error that I can't interpret.
Generally I would like to know how to treat static methods as callable objects.
Minimal related example code (not working):
#include <set>
class MyClass {
// More code here
static auto compare(MyClass a, MyClass b) {
return a < b;
}
};
int main() {
std::set<MyClass, decltype(MyClass::compare)> s(MyClass::compare);
return 0;
}
Update, I'm not sure if my example confused the issue, so I updated it.
Couple of issues:
compare is private, make it public.
One must use & to get the address of functions.
#include <set>
class MyClass {
public:
static auto compare(int a, int b) {
return a < b;
}
};
int main() {
std::set<int, decltype(&MyClass::compare)> s(&MyClass::compare);
return 0;
}
Make the function public, and add & in decltype:
std::set<int, decltype(&MyClass::compare)>
I wouldn't consider this to be "tidier" though.
A functor occupies 0 bytes when used as a std::set comparator. But a function pointer (as in your example) occupies 4 or 8 bytes. It also forces you to pass the function to the set's constructor.
Using a function pointer this way only makes sense if you want to switch between different comparators at runtime.
If you do want a pointer, the class itself is unnecessary. You might as well use a free function.
Related
I want to create a multiset of objects of my own Class with a custom comparator, and every example I have seen out there does it without classes.
The code I have so far main.cpp is like this:
multiset<MyClass> myMultiSet;
whereas in Myclass.cpp there is a comparator function:
bool MyClass::operator<(const MyClass c) const {
return (this->data->size() < c.size());
}
Multiset seems to take the less operator by default, and it actually uses the operator< function in the class, but I cannot find a way to specify it to use another operator (such as operator<=)
For example, I would like it to look like this:
multiset<MyClass, MyClass::operator<=> myMultiSet;
bool MyClass::operator<=(const MyClass c) const { // <----- Now it's using this one
return (this->data->size() <= c.size());
}
Member functions are different to functions: they take an the this pointer. As a result, you can't use pointer-to-member-functions directly (and you would need to take the address explicitly as member functions don't decay to pointer-to-member-functions, i.e., &MyClass::operator<=). Also, the parameter is type, not a value. Also, as was pointed out, operator<= doesn't implement a strict weak order, i.e., you can't use that anyway.
The easiest to use an actual operator, whether member of not, is to use the names from <functional>, e.g.
std::multiset<MyClass, std::greater<MyClass>> s;
If the member isn't a known name, I'd think using a lambda is the easiest approach but requires C++20:
std::multiset<MyClass, decltype([](MyClass const&l, MyClass const&r){ return l.member(r); })> s;
Without C++20 I think you can use std::mem_fn although that is slightly annoying as you'll need to provide the pointer to member as a constructor argument:
#include <functional>
#include <set>
struct MyClass
{
bool member(MyClass const&) const { return false; }
};
int main()
{
std::multiset<MyClass, decltype(std::mem_fn(&MyClass::member))> s(std::mem_fn(&MyClass::member));
}
I have a class cl1:
class c1
{
long double * coords;
...
}
I also have a second class cl2:
class cl2
{
vector<cl1*> cl1_vec;
unsigned int d;
...
}
I would like to sort cl1_vec, from cl2, based on coords[d], using the sort function for vectors.
So i could have something like
sort(cl2_inst->cl1_vec.begin(),cl2_inst->cl1_vec.end(), ??? );
I tried approches like
sort the 'std::vector' containing classes
C++ std::sort with predicate function in Class
but i couldn't make my way to solving this.
Thanks for any help that comes this way.
The code i've tried:
class cl1 {
public:
long double* coords;
cl1(long double *, unsigned int);
cl1();
cl1(const cl1& orig);
virtual ~cl1();
};
class cl2 {
public:
unsigned int d;
vector<cl1*> cl1_vec;
//the srting functions
static bool compareMyDataPredicate(cl1* lhs, cl1* rhs)
{
return (lhs->coords[d] < rhs->coords[d]);
};
// declare the functor nested within MyData.
struct compareMyDataFunctor : public binary_function<my_point*, my_point*, bool>
{
bool operator()( cl1* lhs, cl1* rhs)
{
return (lhs->coords[d] < rhs->coords[d]);
}
};
...
...
}
then in main
std::sort(cl2_inst->cl1_vec.begin(),cl2_inst->cl1_vec.end(),cl2::compareMyDataPredicate() );
The error is because you are accessing a non-static member d from a static context of the comparator function. Use the second approach, in the following way:
Provide a constructor to that struct, which takes a parameter unsigned int and sets a member to that value.
Create an object of type compareMyDataFunctor, and pass in the value of d to the constructor.
Use this object for sorting (3rd argument to std::sort)
I am not sure about the problems, because you were not precise enough about what exactly "does not work" means in your case (does not compile, does compile but does not sort etc). In case it does not compile (most likely guess) you did not post the error messages, which also makes finding and explaining the problem very hard.
Here are some guesses based on the code you posted:
Both static function as well as the functor use the member d to decide which column to sort on. However d is an instance variable, so it is not available for anything which is static. Neither the functor nor the static member function would know which of the possible ds to use, as there is one d per instance.
The best way to do this without resorting to C++11 features (lamdas) is to provide a constructor to the functor which takes the d you are planning to use. Something like this:
struct compareMyDataFunctor : public binary_function<cl1*, cl1*, bool>
{
compareMyDataFunctor( unsigned int d ) : d( d ) {}
bool operator()( cl1* lhs, cl1* rhs)
{
return (lhs->coords[d] < rhs->coords[d]);
}
unsigned int d;
};
This should do the trick.
There are some more things wrong with the code you posted though:
Arrays should be indexed using type size_t not a unsigned int. Same goes for std::vectors
The types in the std::binary_function instantiation do not match the actual types in the method (may be a problem of reducing the code).
Do not use using namespace std (I assume you do from the declarations in your code).
Functors such as these should take parameters as const-reference, not by value.
That's all I can think of. Next time try to present short, self contained, complete examples, then people will not have to resort to guessing.
I'm just learning C++ and I'm having difficulties regarding pointers to methods. Lets say:
class One {
public:
int Add (int a, int b) {return a+b;}
};
typedef int (One::*pAdd) (int, int);
class Other {
public:
int Next (pAdd funct, int c){ return funct (c, 1);}
};
int main (){
One one;
Other other;
other.Next(one.Add, 2);
return 0;
}
I have a number of problems, as reported by my MinGW. First, I'm not invoking funct correctly, as compiler insists on using .* or ->* . Have no idea how to incorporate this request and any help is welcomed. Now, I could solve my problems by making methods static to use c-style pointers or pass objects and invoke methods from within Next, but I want to understand pointers to methods. Basically, I'm puzzled why one.Add is not an acceptable input. Method to call is unambiguously defined (.Add) and conforms my typedef. Also, I'm providing instance of class (one) from typedef thus providing context in which method is to be executed. But compiler output looks like I didn't only miss the syntax, but like I missed the concept. So, how to pass pointer to method with object as context as a single argument?
The main problem here is that member functions are not associated with an object instance, they are just function pointers with a slightly different signature.
So, when you want to call a member function you need two things: a pointer to the member function and the object instance in which to call it.
I changed your code sample a bit:
#include <iostream>
class One {
public:
int Add (int a, int b) {return a+b;}
};
typedef int (One::*pAdd) (int, int);
class Other {
public:
int Next (One one, pAdd funct, int c){
return (one.*funct)(c, 1);
}
};
int main (){
One one;
Other other;
std::cout << other.Next(one, &One::Add, 2) << std::endl;
return 0;
}
And it works now. It can probably be improved a bit, but I think you can take it from here.
I recommend that you read Pointers to member functions section of the c++ faq lite, which explains this very well.
So, how to pass pointer to method with object as context as a single argument?
Using just member-function pointers, you can't. Although your syntax looks like something that should do that, it just isn't allowed. You need an object to apply the function to:
class Other {
public:
int Next (pAdd funct, One & o, int c){ return (o.*funct) (c, 1);}
}
int main (){
One one;
Other other;
other.Next(&One::Add, one, 2);
}
If you want to create a function object that calls a particular member function of a particular object, then one possibility is to use std::bind (or boost::bind if you can't use C++11 yet):
#include <functional>
class Other {
public:
int Next (std::function<int(int,int)> funct, int c){ return funct (c, 1);}
};
int main (){
One one;
Other other;
using namespace std::placeholders;
other.Next(std::bind(&One::Add, &one, _1, _2), 2);
}
or a lambda:
other.Next([&](int a, int b){return one.Add(a,b);}, 2);
Pointers to members need an instance to operate on. Essentially, they are functions which take an addition parameter which becomes the implicit this pointer. To call a function through a pointer to member for the current object you can use code like this:
(this->*funct)(c, 1);
(you'd access member variables similarly but without a function call).
The object you call the member on isn't part of the pointer to member. As a result you need to get it something like this:
&One::Add
This becomes more interesting if the member function is overloaded: in this case you need to provide a context from which the overload can be determined when taking the address. I tupically use a static_cast<>() for this:
static_cast<int (One::*)(int,int)>(&One::Add)
You have a bunch of problems here: one.Add is a member function and
you cannot just invoke it. You need to have a pointer to the class to
invoke it on as well. Also, you need to use the special operator.*
or operator->*. You can also not take the address of a bound member
function.
All in all, you should use a template and boost/std::bind to make
all this bearable or stay away from it.
Here is modified, working code:
class One {
public:
int Add (int a, int b) {return a+b;}
};
typedef int (One::*pAdd) (int, int);
class Other {
public:
int Next (One* one, pAdd funct, int c){ return (one->*funct)(c, 1);}
};
int main (){
One one;
Other other;
other.Next(&one, &One::Add, 2);
return 0;
}
Say you have:
struct c_struct {
int value;
/* other stuff */
void (* dump)();
};
and you'd like to, at some point:
c_struct_obj->dump();
I assume there's no way you could instantiate a c_struct object such that its particular "dump" function knows its particular "value" the way C++ methods know member variables (via the implicit "this" I suppose)? I guess I know the answer already ("no"). If so, are there other ways of using C structs in an OOPy way?
Sure, you just have to pass this by yourself:
struct c_struct {
int value;
/* other stuff */
void (* dump)(struct c_struct *this);
};
And then call it with:
c_struct_obj->dump(c_struct_obj);
You can use structs in this way but its a pain. All c++ member function get a *this pointer passed to them you can do the same but your dump functions will need to take the structure that its contained in as a parameter.
If you are using a C++ compiler, the only difference between a struct and a class is the default visibility of member variables (classes being private, structs being public). The this pointer is available within member functions.
struct test
{
int x;
void inc();
};
void test::inc()
{
x++;
}
int main(void)
{
test a;
a.x = 1;
a.inc();
int b = a.x;
return 0;
}
b == 2 here.
You can do this and can even do inheritance. But the interfacing with it is a total mess and you get nowhere near, for example, the resource safety involved with C++'s deterministic automatic cleanup. Ultimately, you CAN do OOP in C, but it's just not worth the hassle compared to just using C++, as well as all the other features that C++ offers.
I have a std::map which I'm trying to store void pointers for the values. The problem is, most of the pointer I'm trying to store are methods in a class and have different amount of params. I know for the params I can use a va list so thats not too much of a problem, the problem would be the actual pointer itself.
This is what I have:
class A
{
public:
A();
void methodA(...);
};
class B
{
public:
B();
void methodB(...);
};
void method_no_class(...) { }
std::map<int, void(*)(...)> my_map;
my_map[0] = &method_no_class;
B* cb = new B();
my_map[1] = &cb->methodB; // will return error
Maybe this information my help you:
http://www.parashift.com/c++-faq-lite/pointers-to-members.html#faq-33.1
Pointer to method is of different type than pointer to function. If you want to store them both in single collection you have to do manual casts.
The clean OO way would be to define a command interface. The interface would take an instance (of A or B) and all parameters. In the invoke() method, it would call the method of the instance.
You could then use a map of these command interfaces (just define a common subclass for them which defines the abstract invoke() method). The compiler would check all types and arguments for you, and you wouldn't have to use varargs.
Following up on Kamil Szot's answer, the C++ FAQ (and the book) is an excellent reference to the murky depths of C++ and object oriented programming in general. Section 33 addresses specifically the problem you are having:
In C++, member functions have an implicit parameter which points to the object (the this pointer inside the member function). Normal C functions can be thought of as having a different calling convention from member functions, so the types of their pointers (pointer-to-member-function vs. pointer-to-function) are different and incompatible.
Of course, the answer to your question is somewhat lacking in details.
You might want to look at method operaters ->, ::, and their friends. I'll try to find a better link but start here.
UPDATE: hopefully this is a better article for method pointers and operators.
You should functionoids here. They can be used as a flexible and type safe replacement for function pointers with different signatures. A abstract base class is needed. It contains the actual function invocation with the common parameters, if there are any.
class Functioniod: public YourClass {
virtual void execute(char d, common_parameters,...) = 0
}
For every function you want to use, you create a derived class. The constructor contains the function-specific parameters, and the execute() function the actual call. This execute function is later called instead of the function pointer. It needs to have the same signature in every functionoid. It could call something different in any other class too, of course.
class FuncA: public Functionoid {
FuncA(int _a, float _b, string _c, function-specific-parameters...) {
a = _a; b = _b; c = _c;
}
void execute(char d, common-parameters,...) {
call-to-member(d, a, b, c);
}
int a;
float b;
string c;
}
Now if you want to use this as a replacement for your member function pointer, you would do:
std::map<int, *Functionoid> my_map;
my_map[0] = new FuncA(someInt, someFloat, someString);
my_map[1] = new FuncB(some-other-parameters...);
and execute them with
my_map[0]->execute(common-parm);
my_map[1]->execute(common-parm);
Here's an example code to get you started. Haven't compiled it, so might require some tuning.
#define func(Instance,Method,Class) \
(__int64(Instance)<<32 + __int64(&Class::Method))
#define invoke(Func,Method,Class) \
invoke1(Func,(Class*)0)->*invoke2(Func,&Class::Method)
template<class Class>
Class* invoke1(__int64 Func,Class*)
{
return (Class*)(int)(Func>>32);
}
template<class Method>
Method invoke2(__int64 Func,Method)
{
return (Method)(int)Func;
}
------------ USAGE ------------
class B
{
void methodB(int a,float b){}
};
std::map<int, __int64> my_map;
my_map[0] = func(cb,methodB,B);
invoke(my_map[0],methodB,B)(1,2.f);