0

I'm experimenting with c++ as an excerise. I'm implementing a template class "MioVettore" that implements different reordering algorithms.

I'm implementing ShellSort and I want to be able to tell what kind of numerical series to use. To do that I thought it was a good a idea to assign a different function to a function pointer depending on the option selected by the user.

template <class T>
class MioVettore
{
private :
    std::vector<T> data;

    int SerieKnuth(const int &h) {
        return h * 3 + 1;
    }
    int SerieSequenza(const int& h) {
        return 0;
    }
    int SerieSedgewick(const int& h) {
        return 0;
    }

public:
    MioVettore() {};

    enum SerieType { KNUTH, SEQUENZA, SEDGEWICK };

    void ShellSort(SerieType t) {

        int (*evaluateH) (const int&);

        switch ((int)t)
        {

        case (int)KNUTH:
            evaluateH = SerieKnuth;
            break;

        case (int)SEDGEWICK:
            evaluateH = SerieSedgewick;
            break;

        case (int)SEQUENZA:
            evaluateH = &SerieSequenza;
            break;

        default:
            std::cout << "valore serie non riconosciuto" << std::endl;
            return;
    }

    /* ... */
}

When I try to assign a function to evaluateH I got some errors that I have a hard time understanding, like:

'operator' : illegal operation on bound member function expression
The compiler found a problem with the syntax to create a pointer-to-member.

As I know a function pointer is defined as,

int f(int a) {return a;}

int (*pf) (int) = f;

So I can't understand where is the error.

D-RAJ
  • 3,263
  • 2
  • 6
  • 24
  • 1
    https://isocpp.org/wiki/faq/pointers-to-members – alter_igel Jan 14 '21 at 16:57
  • 2
    A pointer to a free function and a pointer to a member function have different syntax. Note that your `Serie*` functions don't need any state from `MioVettore` and they aren't dependent on the template parameter, so they could be made `static` or free. – 0x5453 Jan 14 '21 at 16:59
  • 1
    Unrelated: there's no need to cast your enum to `int` all the time, and the C-style casts look suspicious – alter_igel Jan 14 '21 at 17:00
  • 1
    It would be better to pass the `int` parameter by value and not const reference. – Retired Ninja Jan 14 '21 at 17:00
  • @alterigel and @0x5453 reading your answers I understood that member function as a type related to the class who define them so if: `class Test{ int memberFunction(int a) {//...}` then: _memberFunction_ ha type "int Test:: (int)" I don't know if I wrote the type correctly, but correct me if I'm wrong. So to define a pointer to a member function I should write: `int(Test::* pToMembFunction) (int) = &Test::memberFunction` is that right? – Fabio Gavinelli Jan 14 '21 at 18:32
  • @RetiredNinja why should I pass the int by value? I mean, I know int is small so it's not a problem to pass an int by value, but shouldn't be more correct to pass it to _const ref_ if the function is not meant to modify it? – Fabio Gavinelli Jan 14 '21 at 18:35
  • Did you mean to have your pointer store the address of SerieSequenza? – Adam G. Jan 14 '21 at 18:37
  • @AdamG. Yes so I can change the behaviour of the ShellSort just saying what type of numerical serie I wanted to use. I don’t know if it’s the same for member functions but for normal functions _pointerToFunct = function_ and _pointerToFuncion = &function_ should behave in the same way – Fabio Gavinelli Jan 14 '21 at 18:44

1 Answers1

0

There are two ways to approach this issue.

  1. State that the member variables are static.
  2. Say where the function pointers are pointed to (to which class).

For the first option,
When looking at the three functions, we can see that it doesn't do anything/ doesn't have anything to do with the member variables. It performs calculations using the arguments. This means that specifying it as static wouldn't be an issue and this is how it would look like,

template <class T>
class MioVettore
{
private:
    std::vector<T> data;

    static int SerieKnuth(const int& h) {
        return h * 3 + 1;
    }
    static int SerieSequenza(const int& h) {
        return 0;
    }
    static int SerieSedgewick(const int& h) {
        return 0;
    }

public:
    MioVettore() {};

    enum SerieType { KNUTH, SEQUENZA, SEDGEWICK };

    void ShellSort(SerieType t) {

        int (*evaluateH) (const int&);

        switch (t)
        {

        case KNUTH:
            evaluateH = SerieKnuth;
            break;

        case SEDGEWICK:
            evaluateH = SerieSedgewick;
            break;

        case SEQUENZA:
            evaluateH = SerieSequenza;
            break;

        default:
            std::cout << "valore serie non riconosciuto" << std::endl;
            return;
        }

        /* ... */
    }
};

For the second option,
We can specify where to take the function pointer from, or in other words, which class contains the function pointers. This is how you can implement this.

template <class T>
class MioVettore
{
private:
    std::vector<T> data;

    int SerieKnuth(const int& h) {
        return h * 3 + 1;
    }
    int SerieSequenza(const int& h) {
        return 0;
    }
    int SerieSedgewick(const int& h) {
        return 0;
    }

public:
    MioVettore() {};

    enum SerieType { KNUTH, SEQUENZA, SEDGEWICK };

    void ShellSort(SerieType t) {

        int (MioVettore<T>::*evaluateH) (const int&);

        switch (t)
        {

        case KNUTH:
            evaluateH = &MioVettore<T>::SerieKnuth;
            break;

        case SEDGEWICK:
            evaluateH = &MioVettore<T>::SerieSedgewick;
            break;

        case SEQUENZA:
            evaluateH = &MioVettore<T>::SerieSequenza;
            break;

        default:
            std::cout << "valore serie non riconosciuto" << std::endl;
            return;
        }

        // This is how to call it.
        (this->*(evaluateH))(1);

        /* ... */
    }
};

This advantage of using this over the other is that we can access member variables from the member functions which we cant do if we specify the functions as static.

Note: You don't have to say (int)t and (int)SEDGEWICK in the switch and case statements as the compiler does that implicitly. enums are basically integer constants which has a name to make it easier for developers to group them.

D-RAJ
  • 3,263
  • 2
  • 6
  • 24
  • Thank you for your answer that is complete and clear! May I ask you to explain me why I have to use `(this->*(pointer))(1)` isn't the pointer already defined in the scope of the object? (My guess is that `(this->*())`represents the type of the pointer is that right?) And what if the pointer to the member function is defined outside the function? Reading the FAQ proposed by "_alter ieger_" I tried with a simple test class (not a template class) but I can't use the pointer in the way is proposed – Fabio Gavinelli Jan 15 '21 at 08:23
  • @FabioGavinelli This would clear your doubts: https://stackoverflow.com/questions/65733375/how-to-use-function-pointers-to-access-member-functions/65733376#65733376 – D-RAJ Jan 15 '21 at 09:18