0

I'm trying to implement a strategy pattern. Right now I'm making a vector of function pointers that take in a vector of ints as their type. I called this vector of function pointers "algo". I want each of the function pointers in the vector to point to a different sorting class (merge, bubble, or insertion). My class structure looks like this: Algorithm is the base class, Sort is an abstract class that inherits from Algorithm, and then Merger, Insertion, and Bubble all inherit from Sort. The problem that I'm running into right now is I can't seem to get my mergePointer pointed to the sortFunc() inside the Merger class. When I try to execute my code it says:

main.cpp:59:28: error: use of undeclared identifier 'sortFunc' mergePointer = sortFunc(); ^

I originally figured the problem was scope resolution so I added Merger:: infront of sortFunc() and I got the same error. I'm not to familiar with polymorphism and am not even sure if what I'm trying to do is possible, any thoughts?

class Algorithm{

private:
public:

    vector<int> data;

    static std::vector<void (*)(std::vector<int>&)> algo;

    void (*activeAlgo)(std::vector<int>&);

    enum SortingAlgorithms{
        Merge = 0, Insertion, Bubble, Last
    };

    void load(){
        void (*mergePointer)(vector<int>&);
        mergePointer = sortFunc();
        algo.push_back(mergePointer);
    }
    
    void select(SortingAlgorithms sort){
        
    }
    
};

//abstracted class
class Sort: public Algorithm{
private:
public:
    virtual void sortFunc() = 0; //pure virtual function  
};

class Merger: public Sort{
private:
public:
    void sortFunc(){
        data = mergeSort(data);
        print(data);
    }
};

class Insertion: public Sort{
private:
public:
    void sortFunc(){
        printVec(data);
        insertionSort(data);
        printVec(data);
    }
};

class Bubble: public Sort{
private:
public:
    void sortFunc(){
        printVector(data);
        bubbleSort(data);
        printVector(data);
    }
};

int main(){
    Sort *myAlgo;
    myAlgo->select(Algorithm::Bubble);
}
user4581301
  • 33,082
  • 7
  • 33
  • 54
  • 3
    @CrashMan123 It is evident that the name sortFunc used in the class Algorithm is not yet declared. – Vlad from Moscow Oct 02 '20 at 23:31
  • `Algorithm` should rather be a templated class or function, which can be instantiated with a concrete `Sort` implementaiton. There wouldn't even be an abstract base class needed Take a look at [`std::sort()`](https://en.cppreference.com/w/cpp/algorithm/sort) for example, the algorithm only requires a container (iterator) interface, – πάντα ῥεῖ Oct 02 '20 at 23:34
  • @VladfromMoscow I declared sortFunc in Merger, Bubble, and Insertion classes. How do I go about calling those in class Algorithm? – CrashMan123 Oct 02 '20 at 23:34
  • 2
    @CrashMan123 Any used name shall be declared before its usage. – Vlad from Moscow Oct 02 '20 at 23:36
  • @CrashMan123 In reference to what I've been saying in my last comment, the various algos should be implemented as just different templated functions _"How do I go about calling those in class Algorithm?"_ Just call the different functions as you need them. – πάντα ῥεῖ Oct 02 '20 at 23:39
  • @VladfromMoscow does that mean I can't call a derived function from the base class – CrashMan123 Oct 02 '20 at 23:40
  • 1
    @CrashMan123 A base class knows nothing about its derived classes. – Vlad from Moscow Oct 02 '20 at 23:41
  • @CrashMan123 `Sort *myAlgo;` This defines a pointer value and leaves it uninitialized. You could have something like `Sort *myAlgo = new Bubble;` instead, then `myAlgo->sortFunc();` for polymorphism. Or, use templates as hinted already. – dxiv Oct 02 '20 at 23:42
  • @CrashMan123 you can. that's called call of virtual function. or you can use templates to emulate it. – Swift - Friday Pie Oct 03 '20 at 00:11
  • After you deal with your undeclared identifier, your next problem might be [pointer to non-static member functions](https://stackoverflow.com/questions/45331989/c-pointer-to-non-static-member-functions). – JaMiT Oct 03 '20 at 02:16
  • Hmm... There seems to be a flaw in the logic of your design. Why would every `Algorithm` automatically know merge sort? For that matter, why would every `Algorithm` automatically know any sort? *(For example, an algorithm for solving the Towers of Hanoi has no need for sorting. Even though you presumably will not implement Towers of Hanoi, if its solution does not fit your `Algorithm` class, then the class is at best misnamed.)* You might want to re-think your approach with an eye towards logical consistency. Justify each class' existence, purpose, and name. – JaMiT Oct 03 '20 at 02:23

1 Answers1

0

Note that void(*)(std::vector<int>&) can point only to a namespace function or to static member. Pointers to members are class-specific and have to be called with special operator .* or ->*. What you may implement, is a CRTP with virtual base class (stripped of static vector and other bells and whistles for brevity):

#include <iostream>
#include <cstdlib>

    class ActorBase
    {
    public:
       // virtual interfaces
       virtual void action() = 0;
    };

    template <class  T>
    class Actor : public ActorBase
    {
    protected:
        typedef void(T::* FuncPtr)(/* params */);
        FuncPtr algo;
    public:
        void action() 
        { 
            /* do call algo for appropriate object by treating this as pointer to T */
            (dynamic_cast<T*>(this)->*algo)(/* args */);
        }
    };

    class Concrete : public Actor<Concrete>
    {
       void bar() {  std::cout << "Hello, Concrete!" << std::endl; }
    public:
       Concrete() { algo = &Concrete::bar; }
    };

int main()
{
    Concrete a;
    a.action();
    return EXIT_SUCCESS;
}

Curiously Recurrent Template Actor is very special template which can cast pointer to self to derived class. Still, it can't know anything about Concrete, e.g. typedefs or members. If it required to pass some traits like that, Actor Should be derived from a template class specialized for concrete T, known as trait class.

Not sure that this perverted approach is what actually needed to solve your X problem though, but at least it's syntactically correct. Here is a canonical CRTP.

Note that the call by member pointer requires .* \ -> AND parenthesis, because call operator () got higher priority than .*.

Swift - Friday Pie
  • 12,777
  • 2
  • 19
  • 42