Sign in to follow this  

Concatenation giving me a warning message

This topic is 4087 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I am probably trying to be a little too clever for my own good, but what the heck, it's fun. I'm trying to define <, >, <=, and >= for a class of mine, and though it would be efficient to do it as a macro, like so : Edit : Gah, I cannot figure out how to make this stupid thing put in \ at the end of a line without actually removing the newline. Please consider each line of my 'define' to have a backslash at the end, ok?
#define FormationOperator(x) bool Formation::operator##x (Formation f) {
  switch (whichSort) { 
    case SCOUT:
      return getScouting() x f.getScouting();
    case SPEED :
    default :
      return getSpeed() x f.getSpeed();  
  }
}

FormationOperator(>)
FormationOperator(>=)
FormationOperator(<)
FormationOperator(<=)


Now, this seems to compile, but the preprocessor gives me warnings of a cryptic nature: Formation.cc:348:20: warning: pasting "operator" and ">" does not give a valid preprocessing token Formation.cc:349:21: warning: pasting "operator" and ">=" does not give a valid preprocessing token Formation.cc:350:20: warning: pasting "operator" and "<" does not give a valid preprocessing token Formation.cc:351:21: warning: pasting "operator" and "<=" does not give a valid preprocessing token I do not understand why 'operator<=' should not be a valid preprocessing token. What are the requirements?

Share this post


Link to post
Share on other sites
Come to think of it, I have another question along these lines. Let's say I've managed to define these operators for class Foo. Now, if I do this:


std::vector<Foo*> myFooPointerList;
// Fill up the list
sort(myFooPointerList.begin(), myFooPointerList.end());


will my operators get used in comparing the Foo pointers, or will my list get sorted by the numerical value of the addresses?

Share this post


Link to post
Share on other sites
Quote:
Original post by King of Men
Come to think of it, I have another question along these lines. Let's say I've managed to define these operators for class Foo. Now, if I do this:


std::vector<Foo*> myFooPointerList;
// Fill up the list
sort(myFooPointerList.begin(), myFooPointerList.end());


will my operators get used in comparing the Foo pointers, or will my list get sorted by the numerical value of the addresses?


I'm not sure about your first post, but that sorting will definitely not work like you want. It'll do some kind of sorting according to the pointer address. You'll have to give it a function, like so:



struct myFooPointerSorter {
bool operator()(myFooPointer *f1, myFooPointer *f2) {
if(f1 < f2) return true;
}
};

sort(myFooPointerList.begin(), myFooPointerList.end(), myFooPointerSorter());

Share this post


Link to post
Share on other sites
Quote:
Original post by King of Men
What are the requirements?

The primary requirement is that you have a reason to use a macro in the first place.

As to your other question, ignore containers. How does (int* < int*) behave?

CM

Share this post


Link to post
Share on other sites
A little test snippet for int* tells me that < compares pointer addresses unless you dereference the pointers. It seems to me that this means there is a typo in okonomiyaki's post;

if(f1 < f2) return true;

should be

if (*f1 < *f2) return true;

Right?

Share this post


Link to post
Share on other sites
Quote:
Original post by King of Men
A little test snippet for int* tells me that < compares pointer addresses unless you dereference the pointers. It seems to me that this means there is a typo in okonomiyaki's post;

if(f1 < f2) return true;

should be

if (*f1 < *f2) return true;

Right?

Yes.

CM

Share this post


Link to post
Share on other sites
1) You should, assuming normal circumstances, be passing Formations by const reference to relational operators, not by value.

2) Similarly, it makes sense to define the operator as a free function. You get extra conversions for free this way. You should prefer free functions for most binary function overloads (except assignment). Arithmetic binary operators can often be implemented in terms of the corresponding unary immediate operator, which would be a member function.

3) To get the real benefit of code reuse here, you would probably do better to implement a "3-way comparison" ala strcmp(), and then just make the operators wrap that.

4) You're depending on some internal state of the left-hand object to decide how to compare the two objects. That's likely to cause huge problems. Consider sorting a container of Formations which have different states. Inconsistencies abound. Instead, use comparators.

Something like this should do the trick:

template <typename T, typename CMP>
struct ComparatorBase {
bool operator<(const T& lhs, const T& rhs) {
return CMP::cmp(lhs, rhs) < 0;
}
bool operator<=(const T& lhs, const T& rhs) {
return CMP::cmp(lhs, rhs) <= 0;
}
bool operator>(const T& lhs, const T& rhs) {
return CMP::cmp(lhs, rhs) > 0;
}
bool operator>=(const T& lhs, const T& rhs) {
return CMP::cmp(lhs, rhs) >= 0;
}
bool operator==(const T& lhs, const T& rhs) {
return CMP::cmp(lhs, rhs) == 0;
}
bool operator!=(const T& lhs, const T& rhs) {
return CMP::cmp(lhs, rhs) != 0;
}
}

struct Scouting {
static int cmp(const Formation& lhs, const Formation& rhs) {
// assuming each returns an int.
// This gives a negative value when lhs < rhs,
// 0 when equal, positive when lhs > rhs.
return lhs.scouting - rhs.scouting;
}
};

struct Speed {
static int cmp(const Formation& lhs, const Formation& rhs) {
// assuming each returns an int.
// This gives a negative value when lhs < rhs,
// 0 when equal, positive when lhs > rhs.
return lhs.speed - rhs.speed;
}
};

class Formation {
typedef ComparatorBase<Formation, Scouting> ScoutingComparator;
friend class Scouting;
friend class ScoutingComparator;
typedef ComparatorBase<Formation, Speed> SpeedComparator;
friend class Speed;
friend class SpeedComparator;
// everything else here
};

std::sort(myFormations.begin(), myFormations.end(),
Formation::SpeedComparator());

Share this post


Link to post
Share on other sites
Quote:
Original post by King of Men
A little test snippet for int* tells me that < compares pointer addresses unless you dereference the pointers. It seems to me that this means there is a typo in okonomiyaki's post;

if(f1 < f2) return true;

should be

if (*f1 < *f2) return true;

Right?


I apologize, yes you are correct.

Zahlman - excellent points!

Share this post


Link to post
Share on other sites
Quote:
I do not understand why 'operator<=' should not be a valid preprocessing token. What are the requirements?

The syntax defines what preprocessing tokens are, and they don't include strings like "operator>" - that's two tokens ("operator" and ">"). And it says "If the result [of concatenation with ##] is not a valid preprocessing token, the behavior is undefined", which is why the compiler is warning about it. But because "operator>" is meant to be two tokens, you can just do "#define FormationOperator(op) ... operator op ..." and it'll work the way you wanted.

Share this post


Link to post
Share on other sites
Quote:
Original post by Zahlman
1) You should, assuming normal circumstances, be passing Formations by const reference to relational operators, not by value.


Good point.

Quote:
2) Similarly, it makes sense to define the operator as a free function. You get extra conversions for free this way. You should prefer free functions for most binary function overloads (except assignment). Arithmetic binary operators can often be implemented in terms of the corresponding unary immediate operator, which would be a member function.


I don't understand what you mean by 'extra conversions for free'; could you explain?

Quote:
3) To get the real benefit of code reuse here, you would probably do better to implement a "3-way comparison" ala strcmp(), and then just make the operators wrap that.


That would be simpler, yes. Or just define the <, and define the others in terms of it.

Quote:
4) You're depending on some internal state of the left-hand object to decide how to compare the two objects. That's likely to cause huge problems. Consider sorting a container of Formations which have different states. Inconsistencies abound.


Ah, but no - whichSort is a static member variable, it cannot differ between Formations.

Share this post


Link to post
Share on other sites
Quote:
Original post by King of Men
Quote:
Original post by Zahlman
2) Similarly, it makes sense to define the operator as a free function. You get extra conversions for free this way. You should prefer free functions for most binary function overloads (except assignment). Arithmetic binary operators can often be implemented in terms of the corresponding unary immediate operator, which would be a member function.


I don't understand what you mean by 'extra conversions for free'; could you explain?


I think he means that you'll get automatic conversions on the left hand side of the operator. Imagine you made an Integer class, and defined a unary conversion operator that converts an int to your custom Integer class. If you define the comparison operators as member functions, you could not do this:


Integer myInt(5);
if(4 < myInt) { ... }






However, if the comparison operators are free functions, I believe the 4 will automatically be converted to Integer and it will be called.

What you are trying to do is basically the Policy pattern/idiom/whatever, which Zahlman's implementations makes much clearer and nicer.

Edit: obviously, you could do that if you defined all the appropriate functions, but that's the point, you get it for free this way.

Quote:

Quote:
4) You're depending on some internal state of the left-hand object to decide how to compare the two objects. That's likely to cause huge problems. Consider sorting a container of Formations which have different states. Inconsistencies abound.


Ah, but no - whichSort is a static member variable, it cannot differ between Formations.



Still, it's an internal state, whether static or not. What if you changed that variable in one place and forgot that it changed the operations somewhere else? It would still be better to avoid using that kind of technique.

Edit: Not that internal states are bad, but it would make it clearer if you use an obvious Policy that determines how it will be compared.

Share this post


Link to post
Share on other sites
Ok, I see what you mean about free conversions; as a general rule, I can see it's a good idea. In this specific case, though, it makes absolutely no sense to compare my Formation class with anything but another Formation, and there certainly is no conversion from int.

About the whichSort variable, the intention is that it should be set before every call to sort, and all calls to sort should be internal to the Formation class. So I do think this is a case where you can safely leave it to the user to determine when the variable should be set; there's only one of him, and he has access to the internal workings of the class anyway.

Share this post


Link to post
Share on other sites

This topic is 4087 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this