C ++ Абстракция действий командной строки с использованием интерфейса - PullRequest
0 голосов
/ 15 февраля 2011

Я создаю приложение, использование которого будет выглядеть примерно так:

application --command --option1=? --option2=2?

По сути, может быть любое количество опций, но только одна команда на экземпляр приложения. Аналогично тому, как работает git.

Теперь, я подумал, что напишу это на C ++, чтобы получить некоторый прирост и опыт и познакомиться с некоторыми из тех шаблонов проектирования, о которых я продолжаю читать. Итак, я реализовал это:

class Action
{
public:
    void AddParameter(std::string key, boost::any p);
    virtual unsigned int ExecuteAction();

protected:
    std::map<std::string, boost::any> parameters;
};

Я все равно объясню свою логику, просто чтобы проверить - это действие с абстрактным выводом. Все действия нуждаются в добавлении опций, отсюда и карта параметров, чтобы мы могли реализовать ее на этом уровне, но мы ожидаем, что ExecuteAction будет реализовано производными классами, как, например, мой простой пример DisplayHelpAction, который в значительной степени выполняет то, что говорится жесть.

Итак, я написал фабрику примерно так:

class DetermineAction
{
public:
    DetermineAction();
    vx::modero::Action getAction(std::string ActionString);
private:
    std::map<std::string, vx::modero::Action> cmdmap;
};

Логика заключается в том, что конструктор создаст карту возможных строк, которые вы можете запросить, и getAction сделает то, что говорит - даст ему командную строку и даст вам класс, производный от Action, который реализует требуемая функциональность.

У меня проблемы с этим конструктором. Я пытаюсь это:

this->cmdmap = std::map<std::string, Action>();
this->cmdmap.insert(pair<string, Action>("help", DisplayHelpAction()));
this->cmdmap.insert(pair<string, Action>("license", DisplayLicenseAction()));

Что вызывает много ошибок. Теперь я привык к интерфейсу Java Way, поэтому вы используете:

Interface I = new ConcreteClass();

и Java это нравится. Вот такую ​​вот идею я и пытаюсь реализовать, потому что я хочу иметь для реализации getAction следующее:

return this->cmdmap[ActionString];

Который должен возвращать класс, полученный из Action, для которого я могу затем начать добавлять параметры и вызвать execute.

Итак, подведем итог, у меня есть два вопроса, которые тесно связаны:

  • Дека. Я сознательно практикую абстрагирование, поэтому есть некоторая дополнительная сложность, но в принципе, мой подход обоснован? Есть ли безумно очевидный ярлык, который я пропустил? Есть ли лучший метод, который я должен использовать?
  • Как мне настроить решение для сопоставления классов, чтобы я мог вернуть правильный класс? Конкретная жалоба связана со временем ссылки:

    Linking CXX executable myapp
    CMakeFiles/myapp.dir/abstractcmd.cpp.o: In function `nf::Action::Action()':
    abstractcmd.cpp:(.text._ZN2vx6modero6ActionC2Ev[_ZN2vx6modero6ActionC5Ev]+0x13): undefined reference to `vtable for nf::Action'
    

Только потому, что это может быть актуально, я использую boost::program_options для разбора командной строки.


Редактировать 1 : Хорошо, теперь я заменил Action на Action* согласно ответу Евгения и пытаюсь добавить new SomethingThatSubclassesAction на карту. Я все еще получаю ошибку vtable.

1 Ответ

3 голосов
/ 15 февраля 2011
  1. Сразу следует сказать, что полиморфизм во время выполнения работает в C ++ через указатели на базовый класс, а не по значению.Таким образом, ваш std::map<std::string, Action> должен быть std::map<std::string, Action*> или ваши производные действия (т.е. DisplayHelpAction) будут нарезанными при копировании в map.Хранение Action* также означает, что вам нужно явно позаботиться об освобождении значений map, когда вы закончите. Примечание : вы можете использовать boost :: ptr_map (boost::ptr_map<std::string,Action>) (как указал @Fred Nurk) или boost :: shared_ptr (std::map<std::string,boost::shared_ptr<Action> >) не беспокоиться о явном освобождении выделенного Action*.
    То же самое с 'Action getAction (std :: string ActionString);'оно должно стать Action* getAction(std::string ActionString);.

  2. Ошибка компоновщика (скорее всего) вызвана отсутствием реализации для virtual unsigned int ExecuteAction();.Также я бы сказал, что имеет смысл сделать его чисто виртуальным (virtual unsigned int ExecuteAction() = 0;) - в этом случае вам не нужно предоставлять реализацию для него.Он также предоставит семантику закрытия для интерфейса Java для класса Action.

  3. Если у вас нет очень веской причины, чтобы производные объекты Action не знали всего boost:program_options Я бы передал его и позволил каждому из них получить к нему доступ напрямую, вместо создания std::map<std::string, boost::any>.

  4. Я бы переименовал DetermineAction во что-то вроде ActionManager или ActionHandler.

...