Concatenation giving me a warning message

Started by
10 comments, last by King of Men 17 years, 7 months ago
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?
To win one hundred victories in one hundred battles is not the acme of skill. To subdue the enemy without fighting is the acme of skill.
Advertisement
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 listsort(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?
To win one hundred victories in one hundred battles is not the acme of skill. To subdue the enemy without fighting is the acme of skill.
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 listsort(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());
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
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?
To win one hundred victories in one hundred battles is not the acme of skill. To subdue the enemy without fighting is the acme of skill.
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
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());
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!
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.
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.
To win one hundred victories in one hundred battles is not the acme of skill. To subdue the enemy without fighting is the acme of skill.

This topic is closed to new replies.

Advertisement