1

I'm trying to implement the strategy pattern in C++, and I'm having trouble figuring out the correct syntax. Here's a (rather trivial) java code example of what I'd like to do:

public static interface Processor<T> {

    public void process(T toProcess);
}

public static class Printer implements Processor<String> {

    @Override
    public void process(String toProcess) {
        System.out.println(toProcess);
    }

}

public static class StringHandler {

    public void handleData(Processor<String> processor) {
        processor.process("Hello World!");
    }
}

public static void main(String... args) throws Exception {
    new StringHandler().handleData(new Printer());
}

Attempt at a C++ Equivalent:

#include <string>

using namespace std;

template <typename T>class Processor {
public:
    virtual void process(T rhs) = 0;
};

class Printer : public Processor<string*> {
public:

    void process(string* a) {
        printf("string = %s\n", a->c_str());
    }
};

class StringHandler {
public:

    void handleData(Processor<string*> processor) {
        string hello = "Hello World!!";
        processor.process(&hello);
    }
};

int main(int argc, char** argv) {
    StringHandler h;
    Printer p;
    h.handleData(p);
}

When I attempt to compile the code, I get the following error:

Undefined symbols for architecture x86_64:
  "Processor<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*>::process(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*)", referenced from:
      StringHandler::handleData(Processor<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*>) in main.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Any insights regarding why my C++ code won't compile, or how to implement this template pattern in C++, would be greatly appreciated. A solution that compiles with C++98 would be preferable, though I am also interested in an approach that uses Functional libraries.

  • 1
    Unrelated to your problem, use of pointers as parameters is vaguely rare, a lot of C++ functions take references instead. `printf` is rare in C++, more often you see `std::cout`. When you have a "interface", you almost always want to give it a `virtual` destructor with an definition that doesn't do anything (but must exist!) – Mooing Duck May 07 '15 at 22:47
  • Barry's answer is correct. But I also suggest being wary of your approach here. This pattern comes up very infrequently and does not make much sense until you have a real problem to solve. – QuestionC May 07 '15 at 22:48

2 Answers2

6

You're trying to pass a Processor by value instead of reference, which would require instantiating Processor (which isn't possible because it's an abstract class).

You can fix that by passing it by reference instead:

void handleData(Processor<string*> &processor) {
    // ...

That said, your approach looks to me a lot like you're still basically writing Java (with a little C thrown in for bad measure), and just changing the syntax enough to get a C++ compiler to accept it.

In C++, the strategy pattern will usually be implemented as a template parameter. I'd probably write the code more like this:

struct print {
    void operator()(std::string const &s) const {
        std::cout << "string = " << s << "\n";
    }
};

template <class Process>
void handle(Process p) {
    p("Hello World!!");
};

int main() {
    handle(print());
}

Note, however, that in many cases the strategy part is used in only one place, in which case it can make sense to define it as a lambda expression:

handle([](std::string const &s) { std::cout << "string = " << s << "\n"; });
T.C.
  • 133,968
  • 17
  • 288
  • 421
Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
5

The issue is how you declared handleData:

void handleData(Processor<string*> processor) {

Processor<T> is an abstract class. It cannot be constructed. If you could, you would slice the derived part of the object, which is where the actual implementation for process(T ) lives. gcc gives a much clearer error for me:

error: cannot declare parameter processor to be of abstract type Processor<std::basic_string<char>*>

You need to instead take your processor by reference:

void handleData(Processor<string*>& processor) {
Community
  • 1
  • 1
Barry
  • 286,269
  • 29
  • 621
  • 977