Help with Command-Line Validation

I’m updating from CLANG 3.5.0 to 3.7.0. In the 3.5.0 version I have a std::string option with validation.

struct ProjectParser : public llvm::cl::parserstd::string {

bool parse(llvm::cl::Option& option,

llvm::StringRef argName,

const std::string& argValue,

std::string& project);

};

llvm::cl::opt<std::string, false, ProjectParser> Project;

This no longer compiles since the llvm::cl::parserstd::string template was specialized and made ‘final’. The docs don’t address this issue.

Any help will be appreciated. Thanks in advance.

-Uri

I’m updating from CLANG 3.5.0 to 3.7.0. In the 3.5.0 version I have a
std::string option with validation.

struct ProjectParser : public llvm::cl::parser<std::string> {

    bool parse(llvm::cl::Option& option,

                          llvm::StringRef argName,

                          const std::string& argValue,

                          std::string& project);

};

llvm::cl::opt<std::string, false, ProjectParser> Project;

This no longer compiles since the llvm::cl::parser<std::string> template
was specialized and made ‘final’. The docs don’t address this issue.

Sorry about that - I've an open bug telling me I need to update the docs
here. I made the change to make parser final (bunch of API design reasons
and that extension point wasn't needed inside LLVM/Clang - though I didn't
realize we documented it as an extension point for external users)

I'm not quite sure why we documented this as a useful extension point for
external users yet have no examples of using that inside the LLVM project -
I imagine we do something else inside LLVM instead.

Off the cuff, I would suggest you derived from basic_parser and add the two
member typedefs that parser<T> was providing for you. (but I'm still
curious what LLVM does if it doesn't do the derivation from parser<T> and
whether we should understand/encourage that instead)

- Dave

Hi Dave;

Thanks for your help. I changed my code per your suggestion. I’m not sure if you’re the author of the code or you’re just maintaining the docs, but there’s a small change which I added, to make the derived class compile:
struct ProjectParser : public llvm::cl::basic_parser<std::string> {
    ProjectParser(__in llvm::cl::opt<std::string, false, ProjectParser>& Option);
    ProjectParser& operator=(const ProjectParser&) = delete;

    typedef std::string parser_data_type;
    typedef llvm::cl::OptionValue<std::string> OptVal;

    bool parse(__in llvm::cl::Option& option,
               __in llvm::StringRef argName,
               __in const std::string& argValue,
               __in std::string& project);
    // Needed to for CLANG 3.7.0
    operator llvm::cl::parser<std::string>&(void) const;
};

I had to add the conversion operator since template<class ParserClass, class ValDT> void printOptionDiff() methods expects llvm::cl::parser<std::string>. In my case the conversion operator never gets called. If the above method is called, it may try to access members which do not exists.

Thanks again.
-Uri

Hi Dave;

Thanks for your help. I changed my code per your suggestion. I’m not sure
if you’re the author of the code

I'm the author of the change that broke your code, but not the original
author or core maintainer of that specific API.

or you’re just maintaining the docs, but there’s a small change which I
added, to make the derived class compile:

Thanks for the details

struct ProjectParser : public llvm::cl::basic_parser<std::string> {
    ProjectParser(__in llvm::cl::opt<std::string, false, ProjectParser>&
Option);
    ProjectParser& operator=(const ProjectParser&) = delete;

Why the need for these ctors/assignment operator? The original class
derived from parser<string> wouldn't've had anything other than a default
ctor (& maybe a default/implicit copy assignment operator)?

    typedef std::string parser_data_type;
    typedef llvm::cl::OptionValue<std::string> OptVal;

    bool parse(__in llvm::cl::Option& option,
               __in llvm::StringRef argName,
               __in const std::string& argValue,
               __in std::string& project);
    // Needed to for CLANG 3.7.0
    operator llvm::cl::parser<std::string>&(void) const;
};

I had to add the conversion operator since template<class ParserClass,
class ValDT> void printOptionDiff() methods expects
llvm::cl::parser<std::string>. In my case the conversion operator never
gets called. If the above method is called, it may try to access members
which do not exists.

Hmm, that's curious - I'll have to look into that. Might be able to fix
it... should be able to remove that requirement. Hmm, do you have the error
message from that?

Hi Dave;

I'm using VS2013 to compile the code. Here's the output when the additional methods are not added:

// CommonFrontendAction.h
struct ProjectParser : public llvm::cl::basic_parser<std::string> {
    //ProjectParser(__in llvm::cl::opt<std::string, false, ProjectParser>& Option);
    //ProjectParser& operator=(const ProjectParser&) = delete;

    typedef std::string parser_data_type;
    typedef llvm::cl::OptionValue<std::string> OptVal;

    // parse - Return true on error.
    bool parse(__in llvm::cl::Option& option,
               __in llvm::StringRef argName,
               __in const std::string& argValue,
               __in std::string& project);
    // Needed to for CLANG 3.7.0
    //operator llvm::cl::parser<std::string>&(void) const;
};

1> CommonFrontendAction.cpp
1>c:\<my_path>\CommonFrontendAction.h(97): warning C4510: 'ProjectParser' : default constructor could not be generated
1> c:\<my_path>\CommonFrontendAction.h(83) : see declaration of 'ProjectParser'
1>c:\<my_path>\CommonFrontendAction.h(97): warning C4610: struct 'ProjectParser' can never be instantiated - user defined constructor required
1>C:\<llvm_path>\llvm\include\llvm/Support/CommandLine.h(1251): error C2664: 'ProjectParser::ProjectParser(const ProjectParser &)' : cannot convert argument 1 from 'llvm::cl::opt<std::string,false,ProjectParser>' to 'const ProjectParser &'
1> Reason: cannot convert from 'llvm::cl::opt<std::string,false,ProjectParser>' to 'const ProjectParser'
1> No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
1> C:\<my_path>\CommonFrontendAction.cpp(143) : see reference to function template instantiation 'llvm::cl::opt<std::string,false,ProjectParser>::opt<const char[8],llvm::cl::desc,llvm::cl::NumOccurrencesFlag,llvm::cl::cat>(const char (&)[8],const llvm::cl::desc &,const llvm::cl::NumOccurrencesFlag &,const llvm::cl::cat &)' being compiled
1> C:\<my_path>\CommonFrontendAction.cpp(143) : see reference to function template instantiation 'llvm::cl::opt<std::string,false,ProjectParser>::opt<const char[8],llvm::cl::desc,llvm::cl::NumOccurrencesFlag,llvm::cl::cat>(const char (&)[8],const llvm::cl::desc &,const llvm::cl::NumOccurrencesFlag &,const llvm::cl::cat &)' being compiled
1>C:\<llvm_path>\llvm\include\llvm/Support/CommandLine.h(1026): error C2664: 'void llvm::cl::OptionDiffPrinter<std::basic_string<char,std::char_traits<char>,std::allocator<char>>,ValDT>::print(const llvm::cl::Option &,const llvm::cl::parser<std::string> &,const DT &,const llvm::cl::OptionValue<std::string> &,size_t)' : cannot convert argument 2 from 'const ProjectParser' to 'const llvm::cl::parser<std::string> &'
1> with
1> [
1> ValDT=std::string
1> , DT=std::basic_string<char,std::char_traits<char>,std::allocator<char>>
1> ]
1> Reason: cannot convert from 'const ProjectParser' to 'const llvm::cl::parser<std::string>'
1> No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
1> C:\<llvm_path>\llvm\include\llvm/Support/CommandLine.h(1225) : see reference to function template instantiation 'void llvm::cl::printOptionDiff<ParserClass,DataType>(const llvm::cl::Option &,const llvm::cl::basic_parser<DataType> &,const ValDT &,const llvm::cl::OptionValue<std::string> &,size_t)' being compiled
1> with
1> [
1> ParserClass=ProjectParser
1> , DataType=std::string
1> , ValDT=std::string
1> ]
1> C:\<llvm_path>\llvm\include\llvm/Support/CommandLine.h(1222) : while compiling class template member function 'void llvm::cl::opt<std::string,false,ProjectParser>::printOptionValue(size_t,bool) const'
1> c:\<my_path>\CommonFrontendAction.h(108) : see reference to class template instantiation 'llvm::cl::opt<std::string,false,ProjectParser>' being compiled
1>
1>Build FAILED.

If you're using a different compiler your mileage may differ.
Best,
-Uri