Home > front end >  C implement class constructs instance of another classes depending on string it consumes
C implement class constructs instance of another classes depending on string it consumes

Time:02-28

I need to implement one abstract class, three its concrete subclasses, class which goal to create one of this three classes instances and last class executor of three classes. Requirements are c 98, and not to use if/elseif/else to construct class instance, like i did in a Maker class method make Form. What mechanism i need to avoid if / elseif / else?

For example:

test.h

#ifndef TEST_H
#define TEST_H
#include <iostream>

class Executor {
private:
    const std::string name;
public:
    Executor(const std::string &name = "") {};

    const std::string getname() const {return name;}
};

class BForm {
private:
    const std::string _name;
public:
    BForm(const std::string &name = "") : _name(name) {};
    virtual ~BForm() {};

    virtual void execute(const Executor &src) = 0;

    const std::string getname() {return _name;}
    virtual const std::string gettarget() = 0;
};

class Form1 : public BForm{
private:
    std::string _target;
public:
    Form1(const std::string &target = "") : BForm("form1"), _target(target) {};
    virtual ~Form1() {};

    virtual void execute(const Executor &src) {
        std::cout << src.getname() << " exec form1 target:" << _target << std::endl;
    }

    virtual const std::string gettarget() {return _target;}
};

class Form2 : public BForm {
private:
    std::string _target;
public:
    Form2(const std::string &target = "") : BForm("form2"), _target(target) {};
    virtual ~Form2() {};

    virtual void execute(const Executor &src) {
        std::cout << src.getname() << " exec form2 target:" << _target << std::endl;
    };
    virtual const std::string gettarget() {return _target;}
};

class Form3 : public BForm {
private:
    std::string _target;
public:
    Form3(const std::string &target = "") : BForm("form3"), _target(target) {};
    virtual ~Form3() {};

    virtual void execute(const Executor &src) {
        std::cout << src.getname() << " exec form3 target:" << _target << std::endl;
    };
    virtual const std::string gettarget() {return _target;}
};

class Maker {
public:
    BForm *makeForm(const std::string &name, const std::string &target)
    {
        /* need to avoid */
        if (name == "form1")
            return new Form1(target);
        else if (name == "form2")
            return new Form2(target);
        else
            return new Form3(target);
    }
};

#endif

main.cpp

#include "test.h"

int main() {
    Maker maker;
    BForm *form;
    Executor exec("executor");

    form = maker.makeForm("form1", "A");
    std::cout << form->getname() << " " << form->gettarget() << std::endl;
    form->execute(exec);
    delete form;
    return (0);
}

CodePudding user response:

You could typedef a pointer to function and then use a map from string to this type (pointer to function). And then use your parameter with indexer syntax to access the correct pointer to function.

Here is an example:

#include <iostream>
#include <map>

// The class definitions with a virtual function hello() common to all
class Base { public: virtual void hello() = 0; };
class Derived1 : public Base { public: void hello() { std::cout << "Derived1"; } };
class Derived2 : public Base { public: void hello() { std::cout << "Derived2"; } };

// The object making functions
Base* Maker1() { return new Derived1; }
Base* Maker2() { return new Derived2; }

int main()
{
    // In C  98, without auto, it's worthwhile to typedef complicated types.
    // The first one is a function type returning a pointer to Base...
    typedef Base* MakerT();
    // ... the second one is a map type projecting strings to such function pointers
    typedef std::map<std::string, MakerT*> StrToMakerT;

    /// The actual map projecting strings to maker function pointers
    StrToMakerT strToMaker;
    // Fill the map
    strToMaker["D1"] = &Maker1;
    strToMaker["D2"] = &Maker2;

    // user input
    std::string choice;

    // as long as output works, input works, and the user didn't say "Q":
    while (std::cout << "Please input 'D1' or 'D2' or 'Q' for quit: " 
        && std::cin >> choice 
        && choice != "Q")
    {
        // Prevent adding new entries to the map foir unknown strings
        if (strToMaker.find(choice) != strToMaker.end())
        {
            // Simply look the function up again, the iterator type is too 
            // cumbersome to write in C  98
            Base* b = (*strToMaker[choice])();
            b->hello();
            std::cout << '\n';
            delete b;
        }
        else
        {
            std::cout << "Didn't find your choice, try again.\n";
        }
    }
    std::cout << "Thank you, good bye\n";
}
  • Related