2

I am getting the following errors:

  1. '<': illegal, left operand has type 'const_Ty'
  2. '>: illegal, right operand has type 'const_Ty'

in the below code.

It's a relative simple iterator on a function pointer map where the functions are of the form void (Game::*)(UINT). I check the value against a float, then run the function. The problem seems to be in the for line, although I've got another substantially similar for loop somewhere else that works without a problem.

using FuncPtr = void (Game::*)(UINT);
std::map<FuncPtr, float> funcDelayedTriggerMap;
void Game::PollProcessDelayedTriggers()
{
    for (std::map<FuncPtr, float>::iterator it = funcDelayedTriggerMap.begin(); it != funcDelayedTriggerMap.end(); ++it)
    {
        float currentS = m_timer.GetElapsedSeconds();
        if (it->second < currentS)
        {
            (this->*(it->first))(UINT_MAX);
            funcDelayedTriggerMap.erase(it->first);
        }
    }
}
Rikkles
  • 3,372
  • 1
  • 18
  • 24
  • 3
    Member function pointers [do not implement operator<,](https://stackoverflow.com/questions/1765431/c-comparing-member-function-pointers) so they aren't valid std::map keys – Turtlefight Oct 21 '21 at 15:33
  • Oh thank you. That is exactly the answer that I was looking for. – Rikkles Oct 21 '21 at 15:34
  • Store void* in the map and, then they can be compared. See [How to compare pointers?](https://stackoverflow.com/questions/9086372/how-to-compare-pointers). – 273K Oct 21 '21 at 15:37
  • 2
    @S.M. member function pointers are not guaranteed to be the size of a single pointer due to multiple inheritance and virtual inheritance. Casting it to void* would result in [undefined behaviour](https://stackoverflow.com/questions/1307278/casting-between-void-and-a-pointer-to-member-function) – Turtlefight Oct 21 '21 at 15:39
  • 1
    Or more directly: member function pointers are not actually pointers into memory. –  Oct 21 '21 at 15:42
  • So I'm going to have to use one more dereference layer, where I can map the funcPtr with an id, and then have a map with – Rikkles Oct 21 '21 at 15:44

1 Answers1

1

Member function pointers don't implement operator<, which is the default sorting function std::map uses.

Member function pointers only implement operator== and operator!= .

An easy way to fix this woud be to have a separate key and put the function pointer into the value of the map, e.g.:

std::map<int, std::pair<FuncPtr, float>>

or if you don't need the fast lookup of std::map, a simple vector would also work:

std::vector<std::pair<FuncPtr, float>>

An alternative approach would be to use the function pointer type as key:

using FuncPtr = void (Game::*)(int);

// Just a helper to get a unique type for each function pointer
template<FuncPtr ptr>
struct Tag {};

struct DelayedTrigger {
    FuncPtr ptr;
    float value;

    DelayedTrigger() : ptr(nullptr), value(0.0f) {}
    DelayedTrigger(FuncPtr _ptr, float _value) : ptr(_ptr), value(_value) {}
};

std::map<std::type_index, DelayedTrigger> funcDelayedTriggerMap;

void Game::PollProcessDelayedTriggers()
{
    for (std::map<std::type_index, DelayedTrigger>::iterator it = funcDelayedTriggerMap.begin(); it != funcDelayedTriggerMap.end(); ++it)
    {
        float currentS = 1.0;
        if (it->second.value < currentS)
        {
            (this->*(it->second.ptr))(0);
            funcDelayedTriggerMap.erase(it->first);
        }
    }
}

This essentially uses the specific function pointer as a unique key.

You could then add new entries like this:

funcDelayedTriggerMap.emplace(typeid(Tag<&Game::DoIt>), DelayedTrigger{&Game::DoIt, 1.0f});
// or
funcDelayedTriggerMap[typeid(Tag<&Game::DoIt>)] = {&Game::DoIt, 1.0f};

And check if a function is present:

if(funcDelayedTriggerMap.contains(typeid(Tag<&Game::DoIt>))) {
  // ...
}

This however only works if you know all the functions you want to use with the map at compile time.

Turtlefight
  • 9,420
  • 2
  • 23
  • 40