2

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.

GRVigo
  • 43
  • 4
  • 2
    look for member function pointers, they are fundamentally different than pointers to free functions. The members of the array are *not* just void functions with no parameter – 463035818_is_not_an_ai Aug 05 '21 at 11:53
  • `USlice` is an array of member function pointers, so the calling needs to be in context of a `Cube` object. For example, `(some_cube.*(USlice[spin]))()` or `(some_pointer_to_a_cube->*(USlice[spin]))()`. In a non-static member function, this can use the `this` pointer e.g. `(this->*(USlice[spin]))()` – Peter Aug 05 '21 at 12:08
  • Please read [this](https://isocpp.org/wiki/faq/pointers-to-members) – coding monster Aug 05 '21 at 12:16
  • Also https://stackoverflow.com/questions/1485983/calling-c-member-functions-via-a-function-pointer this can help you. – coding monster Aug 05 '21 at 12:18
  • The array can't be static. You need a different array for each instatiated object, if it should map to the member functions. Unless the member functions are also static. – super Aug 05 '21 at 12:24
  • Are there different pointers to the functions? I think there is only one pointer to each function and this pointer should not change during program execution, so I think that static should be admissible. Am I wrong? – GRVigo Aug 05 '21 at 12:35
  • This is the sort of thing that would benefit hugely from non-type template parameters. `void e_U` might very well be better expressed as `e<+1,0,0>( )` and `void e_L` then becomes `e<0,0,-1>( )`. – MSalters Aug 05 '21 at 13:01
  • 1
    `std::array` has better syntax: `std::arrayUSlice;` – Jarod42 Aug 05 '21 at 13:17

3 Answers3

3

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.

coding monster
  • 384
  • 4
  • 18
  • Actually there will be more functions in the array, at least 18, but can be more. I put only six to simplify the question. – GRVigo Aug 05 '21 at 12:19
  • 1
    @GRVigo Then, function pointer will be faster. Try reading https://stackoverflow.com/questions/1485983/calling-c-member-functions-via-a-function-pointer , it can help you a little. – coding monster Aug 05 '21 at 12:20
2

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
};
TonySalimi
  • 8,257
  • 4
  • 33
  • 62
2

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)

Artyer
  • 31,034
  • 3
  • 47
  • 75
  • Thank you very much. Your answer is exactly what I needed! I've tested it in my code and works perfect! I need to do some adjustmenst and I will perform some performace tests . – GRVigo Aug 05 '21 at 13:00
  • I suggest `std::array` usage with simpler/more intuitive syntax. – Jarod42 Aug 05 '21 at 13:18
  • @Artyer You are right. I did several tests using switch statements and functions arrays and there is no significant difference at run time with the optimized code. Thank you again. – GRVigo Aug 06 '21 at 06:43