Suppose I am programming a game, designing a mechanism for input remapping on it. It has those two structures:
struct KeyboardSettings
{
InputSource dashInput;
InputSource jumpInput;
InputSource bombInput;
InputSource switchScreenLeft;
InputSource switchScreenRight;
InputSource pauseInput;
InputSource moveUp;
InputSource moveDown;
InputSource moveLeft;
InputSource moveRight;
};
struct JoystickSettings
{
InputSource dashInput;
InputSource jumpInput;
InputSource bombInput;
InputSource switchScreenLeft;
InputSource switchScreenRight;
InputSource pauseInput;
InputSource movementAxisX;
InputSource movementAxisY;
};
Now, I want my system do find out duplicates when reassigning a key, something akin to:
void UIInputRemappingButtonGroup::assignRemappingUniquely(InputSource& curSource, InputSource newSource)
{
for (InputSource* orgSource : /* iterate through structure's members */)
if (*orgSource == newSource) *orgSource = curSource;
curSource = newSource;
}
Since I want this code to be as generic as possible I thought of treating both structures as if they were an "array" of InputSource
s, so the UIInputRemappingButtonGroup
would receive an InputSource*
and a size_t
representing the array's beginning and size, and I would do something like this:
void UIInputRemappingButtonGroup::assignRemappingUniquely(InputSource& curSource, InputSource newSource)
{
for (InputSource* orgSource = sourceCollection; orgSource != sourceCollection+sourceCollectionSize; ++orgSource)
if (*orgSource == newSource) *orgSource = curSource;
curSource = newSource;
}
Thus, if I was working on the screen to remap keyboard buttons, I would have a KeyboardSettings keyboardSettings
and I would pass it to the UIInputRemappingButtonGroup
like this:
buttonGroup.setInputSourceCollection(&keyboardSettings.dashInput, sizeof(keyboardSettings)/sizeof(InputSource));
And, similarly, if I had a JoystickSettings joystickSettings
, I could just pass it to the UIInputRemappingButtonGroup
using this code:
buttonGroup.setInputSourceCollection(&joystickSettings.dashInput, sizeof(joystickSettings)/sizeof(InputSource));
Problem is, I suspect this will trigger Undefined Behavior. Even though the two structures are standard-layout and trivial (and thus POD), I have not found anything on the standard that supports the validity of this technique or at least takes it out of the UB realm. Is it safe?
The InputSource
is defined as this:
class InputSource final
{
private:
enum class Type : uint8_t { Keyboard, MouseButton, MouseAxis, JoystickButton, JoystickAxis };
Type type;
size_t attribute;
// Constructor private
InputSource() = default;
InputSource(Type type, uint32_t attr) : type(type), attribute(attr) {}
public:
std::string getInputName(LocalizationManager& lm);
static InputSource keyboardKey(size_t scanCode);
static InputSource mouseButton(sf::Mouse::Button button);
static InputSource mouseX, mouseY;
static InputSource mouseWheel(sf::Mouse::Wheel wheel);
static InputSource joystickButton(unsigned int button);
static InputSource joystickAxis(sf::Joystick::Axis axis);
friend bool operator==(InputSource in1, InputSource in2);
friend bool operator<(InputSource in1, InputSource in2);
friend bool readFromStream(sf::InputStream& stream, InputSource& in);
friend bool writeToStream(OutputStream& stream, const InputSource& in);
friend struct std::hash<InputSource>;
};
As seen, it satisfies the standard-layout requirements (all non-static data members having the same access control, no virtual functions or virtual base classes, no non-static data members of reference type, and all non-static data members standard-layout), and the trivial requirements (trivial constructor, copy operator and destructor), so it is also POD.
So, is this mechanism I devised safe from the standard's view?