Declaring an array of functions inside a class

Viewed 168

I have a batch of functions in a class:

class Edges // Edges of a Rubik's cube
{
    // Stuff
    
    protected:
        // Edges movements
        void e_U();
        void e_D();
        void e_F();
        void e_B();
        void e_R();
        void e_L();
        
    // More stuff
};

A new class inherits this functions:

class Cube: public Edges // Rubik's cube class
{
    public: 
        void U(int); // U slice edges movement relative to spin
        
    // Stuff
}

The call for the needed function depends on a number, as you see in the next code:

void Cube::U(int spin) // U slice movement for the given spin
{
    switch (spin)
    {
        case 0: e_U(); return;
        case 1: e_U(); return;
        case 2: e_U(); return;
        case 3: e_U(); return;
        case 4: e_D(); return;
        ...
        case 23: e_L(); return;
        default: return;
    }
}   

I want to improve the performance, so I put the functions in an array:

class Cube: public Edges // Rubik's cube class
{
    public: 
        void U(int); // U slice edges movement relative to spin
        
    // Stuff
    
    private:
    
        const static void (*(USlice[24]))(); // U slice edges movement relative to spin
        
    // More stuff
}

const void Cube::( (*(USlice[24]))() ) =
{ 
    e_U, e_U, e_U, e_U,
    e_D, e_D, e_D, e_D,
    e_F, e_F, e_F, e_F,
    e_B, e_B, e_B, e_B,
    e_R, e_R, e_R, e_R,
    e_L, e_L, e_L, e_L
};

So the U function should be now something like this:

void Cube::U(int spin) // U slice movement for the given spin
{
    USlice[spin]();
}

My question is that I can't find the right way to declare the functions array inside the class. I've tried everything I can think of (remove "const" and "static" statements, make all public, ...)

The array should be "static" and "const", and the members are 24 "void" functions with no parameters.

Actually I don't know if declaring the functions in an array will be faster than using a switch statement, but I would like to check it.

Thanks.

3 Answers

Actually, for only performance, you don't need to use array of function pointers.

There is no speed difference.
Using function pointers, it tooks 1 MOV operation in asm(about 2 DWORD), while using switch tooks 1~2 CMP or TEST operations(about 2-6 WORD, because there are only 6 unique functions in 24sets).

The errors you mentioned is that, values of array are the function pointers to protected member function - not global function.
This means, you have to think about 1st function parameter is *this which is invisible to c++ code.

The function pointers that you want to keep inside the array are pointers to the methods of a Cube object. But you want to define that array member variable as an static variable. And as you know, static variables can be accessed without any class objects. So, this is a contradiction.

You can write your class like this without the array to be static.

class Cube : public Edges // Rubik's cube class
{
public:
   Cube() :
      mFuncs{ 
         std::bind(&Cube::e_U, this),
         std::bind(&Cube::e_U, this),
         std::bind(&Cube::e_U, this), 
         std::bind(&Cube::e_U, this), 
         std::bind(&Cube::e_D, this),
         std::bind(&Cube::e_L, this) }
   {
   }

   void e_U() {}
   void e_D() {}
   void e_L() {}

   void U(size_t spin) // U slice edges movement relative to spin
   {
      if (spin < mFuncs.size())
      {
         mFuncs[spin]();
      }
      else
      {
         // report the error
      }
   }
protected:
   const std::vector<std::function<void()>> mFuncs;
// Stuff
};

The answer to your question is that e_U, e_D, etc are all member functions, and not just plain functions, so the type of pointers to them are pointer-to-member functions.

The declaration and usage for your array would look like:

class Cube : public Edges {
    // ...
    static void (Cube::* const USlice[24])();
};

void (Cube::* const Cube::USlice[24])() = {
    &Cube::e_U, &Cube::e_U, &Cube::e_U, &Cube::e_U,
    &Cube::e_D, &Cube::e_D, &Cube::e_D, &Cube::e_D,
    &Cube::e_F, &Cube::e_F, &Cube::e_F, &Cube::e_F,
    &Cube::e_B, &Cube::e_B, &Cube::e_B, &Cube::e_B,
    &Cube::e_R, &Cube::e_R, &Cube::e_R, &Cube::e_R,
    &Cube::e_L, &Cube::e_L, &Cube::e_L, &Cube::e_L
};

void Cube::U(int spin) {
    (this->*USlice[spin])();
}

Alternatively you can make all your functions into static functions and make the this parameter explicit (void e_U(); -> static void e_U(Edges&);), and you can store those as function pointers


However, the version with the array isn't particularly optimised. gcc and clang still have a lot of overhead to deal with pointers to virtual functions. The switch version becomes optimised to a (better) lookup table and a simple jump to the value in the table, just like you want. This pattern is very easy to optimise into a table, so your compiler should already be doing it.

See here for a disassembly: https://godbolt.org/z/beaPYvzMY (The switch LUT is labelled .L4, the explicit pointer-to-member table is labelled Cube::U_lookup(int)::table)

Related