Skip to content

A bug with take_first take_last and join  #322

@fpeng1985

Description

@fpeng1985
std::string files;
auto opt = app.add_option("-f,--file,file", files, "File name");
opt->take_first();
opt->take_last();
opt->join();

The above code snipet will throw an exception from multi_option_policy method in Option class. In fact take_first, take_last and join can only be called once. According to the docs, these methods will only be used for vectors(may be flags). So it is better to hide these interfaces in other situations, or fix these bug.

To wholy avoid these interface conflict, a refactor to the Option level is needed. Option class itself or its internal implementation need different types to provide polymorphism. I want to help implement the enhancement, but some part of the source code is still unclear to me, especially type_size_ expected_ and their relationship with multi_option_policy_, would you give some hints about these? Thanks!

The following is a possible implementation. It seems like an enhanced version of CRTP, but with an important differnce. These base classes themselves can inherite from each other, so the code organization is similar to a virtual function based method, but with static dispatching.
Please gives your feedback, thanks again!

template<typename Host>
struct CommonStrategy {
    //common data
    bool configurable_{true};
    std::vector<std::string> snames_;

    //common behavior
    bool configurable() const   { return configurable_; }
    void configurable(bool val) { configurable_ = val;  }
};

template<typename Host>
struct OptionStrategy : public CommonStrategy<Host> {

    //customized behavior
    std::string get_names() {
        std::ostringstream out;
        //access common data
        auto &self = static_cast<Host&>(*this);
        for(auto &name:  self.snames) {
            out << name << ",";
        }
        return out.str();
    }
};

template<typename Host>
struct FlagStrategy : public CommonStrategy<Host> {

    //customized behavior
    std::string get_names() {
        std::ostringstream out;

        //access common data snames_
        auto &self = static_cast<Host&>(*this);
        for(auto &name:  self.snames_) {
            out << name << ",";
        }
        //access extra data fnames_
        for(auto &name : fnames_) {
            out << name << ",";
        }
        return out.str();
    }

    //extra data
    std::vector<std::string> fnames_;

    //extra behavior
    const std::vector<std::string> & get_fnames() const {
        return fnames_;
    }
};

template<template<typename> class Strategy>
struct Host : public Strategy<Host<Strategy>> {
};

int main() {
    Host<FlagStrategy> flag;
    flag.configurable(false);//call common method
    flag.get_names();//will prints name in a specific way
    flag.get_fnames();//call the extra method specific to FlagStrategy

    Host<OptionStrategy> option;
    option.configurable(false);//call common method
    option.get_names();//will prints name in another way
    //option.get_fnames();//this method does even exists for ordinary Options

    return 0;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions