0

I'm trying to implement better key state detection on a wrapper class for a GLFW window. I needed to make sure that if my key state was continually set as pressed, certain actions wouldn't re-trigger. So I encapsulated the idea of rising, falling, floor and ceiling edge triggers. The issue is that I won't necessarily need all triggers on all compiled parts of code. so I came up with the following solution:

class Window{
public:
GLFWwindow *m_window_ptr = nullptr;
//...
bool isKeyPressed(int key) const{
    return glfwGetKey(m_window_ptr, key) == GLFW_PRESS;
}

bool isKeyReleased(int key) const{
    return glfwGetKey(m_window_ptr, key) == GLFW_RELEASE;
}
enum class KeyState {
    pressed,
    released
};

enum class SignalEdge {
    rising,
    falling,
    floor,
    ceiling,
};

template<int key_t>
SignalEdge getKeyState() {
    static KeyState last_key_state = KeyState::released;
    switch (last_key_state) {
        case KeyState::released:
            if (isKeyPressed(key_t)) {
                last_key_state = KeyState::pressed;
                return SignalEdge::rising;
            } else {
                return SignalEdge::floor;
            }

            break;
        case KeyState::pressed:
            if (isKeyReleased(key_t)) {
                last_key_state = KeyState::released;
                return SignalEdge::falling;
            } else {
                return SignalEdge::ceiling;
            }
            break;
    }
}

template<int key_t>
bool isKeyRisingEdge() {
    return getKeyState<key_t>() == SignalEdge::rising;
}
};

Only to realize that I'll need this per window object, so static variables inside each getKeyState template instantiation will apply to every window in GLFW.

My only way of avoiding this then, is to use member variables per templated key type but I don't want to spend the effort to map every single key type in my class just to set a state variable for each. I would like to automatically map per-object variables to the existence of a given template function, ie exactly what I've set up here, but where the state is saved per GLFW window object.

If I eliminate manually mapping the GLFW enum ints, I'm not sure what I can do, I thought about variable templates but I'm not seeing a path forward on that either ( how would I have a variable only exist on template instantiation of a function?). The only other option I see is storing a dynamically alocated array of states as the member function static variable, internally storing a incremented index per each window object used to index into the static variable array, or do the opposite with the functions but that seems messy? Is there another way I can do this at compile time with templates?

Jarod42
  • 203,559
  • 14
  • 181
  • 302
Krupip
  • 4,404
  • 2
  • 32
  • 54
  • `std::map` member ? – Jarod42 Sep 25 '18 at 21:26
  • BTW, don't you have **events** for key pressed and key released with your framework (and not just "status" of the key)? – Jarod42 Sep 25 '18 at 21:30
  • @Jarod42 It would work, but I'm trying to avoid dealing with runtime objects, I know it is possible for *me* to figure out what I want at compile time, I was hoping this would hold for the actual language itself. – Krupip Sep 25 '18 at 21:30
  • If you store all 348 current defines in an array as bits, it will take less than 45bytes, is that too much per window? – Quimby Sep 25 '18 at 21:32
  • @Jarod42 It has callbacks on key pressed, if I could do what I'm trying to do with out this whole map another variable for rising and falling edge state then I would be all ears. – Krupip Sep 25 '18 at 21:32
  • @Quimby it's not the amount of memory, its the time for me to do so and redundancy. – Krupip Sep 25 '18 at 21:32
  • @opa sorry, I don't follow, time of what? – Quimby Sep 25 '18 at 21:33
  • @Quimby I don't want to spend the effort to maintain that, and deontelogically I feel I shouldn't have to. – Krupip Sep 25 '18 at 21:34
  • 1
    From https://www.glfw.org/docs/latest/input_guide.html#input_keyboard, You can set a callback for the event. Just ignore `GLFW_REPEAT`. – Jarod42 Sep 25 '18 at 21:36
  • @Jarod42 That's actually exactly what I needed, except I don't necisarily want to ignore repeat, I didn't realize `glfwGetKey` never returns GLFW_REPEAT. Thanks. – Krupip Sep 25 '18 at 21:40
  • `std::bitset<348> lastStates;` and replacing `last_key_state` with `lastStates[key_t]` is all that would be needed. I wouldn't call that maintenance. – Quimby Sep 25 '18 at 21:41
  • @Quimby I technically have no guarantee that the bit set will be contiguous over 0->347 even with 348 elements. Thus I will still have to set each element individually even if this is the case. And I won't be able to use a bitset, as this requires a max bound for a number (which I technically don't have) and forces me to move to a hash table solution. – Krupip Sep 28 '18 at 17:57
  • @op Not sure what you mean by setting individual elements? It's index operator will work for [0,size-1], GLFW definitions are not continuous, thus need for 348 instead of actual amount of definitions. – Quimby Sep 28 '18 at 18:01
  • @Quimby if GLFW keys are not contiguous, then I cannot index into bitset <348> and not expect a segmentation fault. Technically with no guarantee of max value, I'll need `std::bitset::max()>` or something for bitset to work (clearly not workable) – Krupip Sep 28 '18 at 18:14
  • @opa That's why there is `GLFW_KEY_LAST` definition, which is the max you are looking for, currently 348. A new version of GLFW will not break the `bitset` only make it larger, but I don't expect many more keys to come and they don't release versions every day. It's true that it will be mostly empty, but as I said, it's only 45 bytes per window. – Quimby Sep 28 '18 at 18:22
  • @Quimby At this point Jarod42 has already answered my question, but I'm still looking for a solution using compile time template variables that only exist when function is instantiated. – Krupip Sep 28 '18 at 18:52
  • @opa No problem, may I know why you want compile time and on-demand variables? What's the advantage? – Quimby Sep 28 '18 at 18:56
  • @Quimby There are no spaces in the data that isn't used (at least generally), variables all exist on the stack, zero access overhead, and if this can be done with templates, no maintenance overhead. – Krupip Sep 28 '18 at 19:05
  • @opa Static variable are not on the stack, but I agree that there are no dynamic allocations. I really can't image a usefull program where any of this can even show on profiler. And in that case you might also be worried about all those instantiations for each key value. But if you solution works for you, I won't force you into mine, although I still think that `bitset` would be applicable here. – Quimby Sep 28 '18 at 19:18

0 Answers0