Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

parse_positional sets m_positional. We need a way to append into it too. #300

Open
KOLANICH opened this issue Aug 15, 2021 · 4 comments
Open

Comments

@KOLANICH
Copy link
Contributor

No description provided.

@jarro2783
Copy link
Owner

Are you able to read the positional arguments before you call it again?

@KOLANICH
Copy link
Contributor Author

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.

@jarro2783
Copy link
Owner

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.

@KOLANICH
Copy link
Contributor Author

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants