cxxopts
cxxopts copied to clipboard
`parse_positional` sets `m_positional`. We need a way to append into it too.
Are you able to read the positional arguments before you call it again?
What do you mean under positional arguments
(the spec for them or the actual arguments provided by a user) and what do you mean under it
?
My wrapper currently looks like this:
cxxopts.cpp
#include <HydrArgs/HydrArgs.hpp>
#include <HydrArgs/toolbox.hpp>
#include <HydrArgs/fallback/errors.hpp>
#include <cxxopts.hpp>
#include <memory>
#include <string>
#include <algorithm>
#include <streambuf>
#include <sstream>
struct CXXOptsBackend: public IBackendOwnStoredSpec{
cxxopts::Options app;
cxxopts::OptionAdder optAdder;
std::vector<std::string> remaining;
std::vector<char *> remainingPtrs;
std::vector<cxxopts::Option> nativeArgs;
std::vector<std::string> positionalArgs;
bool show_help = false;
CXXOptsBackend(const std::string& name, const std::string& descr, const std::string& usage [[maybe_unused]], std::vector<Arg*> dashedSpec, std::vector<Arg*> positionalSpec, Streams streams): IBackendOwnStoredSpec(dashedSpec, positionalSpec, streams), app(name, descr), optAdder(app.add_options()){
std::vector<std::string> positionals;
for(auto argPtr: dashedSpec){
_addArg(argPtr, false);
}
for(auto argPtr: positionalSpec){
_addArg(argPtr, true);
}
app.parse_positional(positionals);
app.allow_unrecognised_options();
//app.set_tab_expansion();
optAdder(DEFAULT_HELP_ARG.long_name.undashed, DEFAULT_HELP_ARG.doc);
}
virtual void _addArg(Arg* argPtr, bool isPositional) override {
switch(argPtr->type){
case ArgType::flag:
{
auto specOptPtr = static_cast<FlagArg*>(argPtr);
optAdder(specOptPtr->name, specOptPtr->description);
}
break;
case ArgType::s4:
{
auto specOptPtr = static_cast<IntArg*>(argPtr);
std::stringstream s;
s << specOptPtr->value;
optAdder(specOptPtr->name, specOptPtr->description, cxxopts::value<decltype(specOptPtr->value)>()->default_value(s.str()));
}
break;
case ArgType::string:
case ArgType::path:
{
auto specOptPtr = static_cast<StringArg*>(argPtr);
optAdder(specOptPtr->name, specOptPtr->description, cxxopts::value<decltype(specOptPtr->value)>()->default_value(specOptPtr->value));
}
break;
}
if(isPositional){
// `m_positional` is overridden (not appended) on every `app.parse_positional` (this function adds positional arguments). There is no way to only append element there. So we have to populate an own array and then call `app.parse_positional` in `_seal`
positionalArgs.emplace_back(argPtr->name);
/*
quote from the docs:
options.parse_positional({"first", "second", "last"})
where "last" should be the name of an option with a container type, and the others should have a single value.
*/
}
}
virtual ~CXXOptsBackend() override = default;
virtual void _seal() override {
app.parse_positional(positionalArgs);
}
virtual void _unseal() override {
}
virtual bool isSealed() const override {
return false;
}
virtual void printHelp(std::ostream &stream, const char * const argv0 [[maybe_unused]]) override {
stream << app.help({""});
}
virtual ParseResult _parseArgs(CLIRawArgs rawArgs) override {
auto result = app.parse(rawArgs.argc, rawArgs.argv);
PROCESS_CASE_OF_HELP_CALLED(result[DEFAULT_HELP_ARG.long_name.undashed].as<bool>());
size_t i=0;
std::pair<decltype(dashedSpec)&, bool> specs[]{
{dashedSpec, false},
{positionalSpec, true},
};
for(auto p: specs){
auto spec = p.first;
auto isPositional = p.second;
for(auto argPtr: spec){
if(result.count(argPtr->name)){
switch(argPtr->type){
case ArgType::flag:
{
auto specOptPtr = static_cast<FlagArg*>(argPtr);
specOptPtr->value = result[argPtr->name].as<bool>();
}
break;
case ArgType::s4:
{
auto specOptPtr = static_cast<IntArg*>(argPtr);
specOptPtr->value = result[argPtr->name].as<decltype(specOptPtr->value)>();
}
break;
case ArgType::string:
case ArgType::path:
{
auto specOptPtr = static_cast<StringArg*>(argPtr);
specOptPtr->value = result[argPtr->name].as<decltype(specOptPtr->value)>();
}
break;
}
} else {
PROCESS_ARGUMENT_MISSING_CASE();
}
++i;
}
}
//std::transform(begin(remaining), end(remaining), begin(remainingPtrs), [](std::string &el) -> char * {return el.data();});
return {
.parsingStatus = STATUS_OK,
.rest={
/*.argc = 0,
.argv = remainingPtrs.data()*/
.argc = 0,
.argv = nullptr
}
};
}
};
IArgsParser* argsParserFactory(const std::string& name, const std::string& descr, const std::string& usage [[maybe_unused]], std::vector<Arg*> dashedSpec, std::vector<Arg*> positionalSpec, Streams streams){
return new CXXOptsBackend(name, descr, usage, dashedSpec, positionalSpec, streams);
}
Note the method _seal
(it is called before _parseArgs
by the framework). If we could append positional args, we would be able to get rid of it.
I see the problem. You could probably just call parse_positional
in your _parseArgs before you call cxxopts::Options::parse. But I can see that it makes sense to have an append positional function. I can add that.
You could probably just call parse_positional in your _parseArgs before you call cxxopts::Options::parse.
It is already done like that (seal
, which calls _seal
is called by parseArgs
before it calls _parseArgs
(methods with leading underscores are actual implementations without checks and bookkeeping, the ones without leading underscores are the methods of the framework and trigger the checks, end users (not ones creating backends, but ones using HydrArgs in own apps) should use only them, unless they have really strong reasons to use )), but I'm not happy with such a workaround.
BTW, I have created a GH repo for the project, though it is completely unfinished currently. https://github.com/HydrArgs/HydrArgs