1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2025-01-31 20:51:52 +01:00

[CommandLine] Add callbacks to Options

Summary:
Add a new cl::callback attribute to Option.

This attribute specifies a callback function that is called when
an option is seen, and can be used to set other options, as in
option A implies option B.  If the option is a `cl::list`, and
`cl::CommaSeparated` is also specified, the callback will fire
once for each value.  This could be used to validate combinations
or selectively set other options.

Reviewers: beanz, thomasfinch, MaskRay, thopre, serge-sans-paille

Reviewed By: beanz

Subscribers: llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D70620
This commit is contained in:
Don Hinton 2019-12-06 14:40:21 -08:00
parent 7d2c6fd3c7
commit 4da312a2f1
4 changed files with 149 additions and 2 deletions

View File

@ -996,6 +996,31 @@ This section describes the basic attributes that you can specify on options.
* The **cl::cat** attribute specifies the option category that the option
belongs to. The category should be a `cl::OptionCategory`_ object.
.. _cl::callback:
* The **cl::callback** attribute specifies a callback function that is
called when an option is seen, and can be used to set other options,
as in option B implies option A. If the option is a `cl::list`_,
and `cl::CommaSeparated`_ is also specified, the callback will fire
once for each value. This could be used to validate combinations or
selectively set other options.
.. code-block:: c++
cl::opt<bool> OptA("a", cl::desc("option a"));
cl::opt<bool> OptB(
"b", cl::desc("option b -- This option turns on option a"),
cl::callback([&](const bool &) { OptA = true; }));
cl::list<std::string, cl::list<std::string>> List(
"list",
cl::desc("option list -- This option turns on options a when "
"'foo' is included in list"),
cl::CommaSeparated,
cl::callback([&](const std::string &Str) {
if (Str == "foo")
OptA = true;
}));
Option Modifiers
----------------

View File

@ -90,6 +90,9 @@ Non-comprehensive list of changes in this release
``-cfguard-nochecks`` option. Note that this feature should always be used
with optimizations enabled.
* ``Callbacks`` have been added to ``CommandLine Options``. These can
be used to validate of selectively enable other options.
Changes to the LLVM IR
----------------------

View File

@ -471,6 +471,43 @@ struct sub {
template <class Opt> void apply(Opt &O) const { O.addSubCommand(Sub); }
};
// Specify a callback function to be called when an option is seen.
// Can be used to set other options automatically.
template <typename R, typename Ty> struct cb {
std::function<R(Ty)> CB;
cb(std::function<R(Ty)> CB) : CB(CB) {}
template <typename Opt> void apply(Opt &O) const { O.setCallback(CB); }
};
namespace detail {
template <typename F>
struct callback_traits : public callback_traits<decltype(&F::operator())> {};
template <typename R, typename C, typename... Args>
struct callback_traits<R (C::*)(Args...) const> {
using result_type = R;
using arg_type = typename std::tuple_element<0, std::tuple<Args...>>::type;
static_assert(sizeof...(Args) == 1, "callback function must have one and only one parameter");
static_assert(std::is_same<result_type, void>::value,
"callback return type must be void");
static_assert(
std::is_lvalue_reference<arg_type>::value &&
std::is_const<typename std::remove_reference<arg_type>::type>::value,
"callback arg_type must be a const lvalue reference");
};
} // namespace detail
template <typename F>
cb<typename detail::callback_traits<F>::result_type,
typename detail::callback_traits<F>::arg_type>
callback(F CB) {
using result_type = typename detail::callback_traits<F>::result_type;
using arg_type = typename detail::callback_traits<F>::arg_type;
return cb<result_type, arg_type>(CB);
}
//===----------------------------------------------------------------------===//
// OptionValue class
@ -1344,6 +1381,7 @@ class opt : public Option,
return true; // Parse error!
this->setValue(Val);
this->setPosition(pos);
Callback(Val);
return false;
}
@ -1402,6 +1440,7 @@ public:
template <class T> DataType &operator=(const T &Val) {
this->setValue(Val);
Callback(Val);
return this->getValue();
}
@ -1411,6 +1450,14 @@ public:
apply(this, Ms...);
done();
}
void setCallback(
std::function<void(const typename ParserClass::parser_data_type &)> CB) {
Callback = CB;
}
std::function<void(const typename ParserClass::parser_data_type &)> Callback =
[](const typename ParserClass::parser_data_type &) {};
};
extern template class opt<unsigned>;
@ -1550,6 +1597,7 @@ class list : public Option, public list_storage<DataType, StorageClass> {
list_storage<DataType, StorageClass>::addValue(Val);
setPosition(pos);
Positions.push_back(pos);
Callback(Val);
return false;
}
@ -1596,6 +1644,14 @@ public:
apply(this, Ms...);
done();
}
void setCallback(
std::function<void(const typename ParserClass::parser_data_type &)> CB) {
Callback = CB;
}
std::function<void(const typename ParserClass::parser_data_type &)> Callback =
[](const typename ParserClass::parser_data_type &) {};
};
// multi_val - Modifier to set the number of additional values.
@ -1696,6 +1752,7 @@ class bits : public Option, public bits_storage<DataType, Storage> {
this->addValue(Val);
setPosition(pos);
Positions.push_back(pos);
Callback(Val);
return false;
}

View File

@ -71,7 +71,7 @@ public:
~StackOption() override { this->removeArgument(); }
template <class DT> StackOption<T> &operator=(const DT &V) {
this->setValue(V);
Base::operator=(V);
return *this;
}
};
@ -1722,4 +1722,66 @@ TEST(CommandLineTest, OptionErrorMessageSuggest) {
cl::ResetAllOptionOccurrences();
}
TEST(CommandLineTest, Callback) {
cl::ResetCommandLineParser();
StackOption<bool> OptA("a", cl::desc("option a"));
StackOption<bool> OptB(
"b", cl::desc("option b -- This option turns on option a"),
cl::callback([&](const bool &) { OptA = true; }));
StackOption<bool> OptC(
"c", cl::desc("option c -- This option turns on options a and b"),
cl::callback([&](const bool &) { OptB = true; }));
StackOption<std::string, cl::list<std::string>> List(
"list",
cl::desc("option list -- This option turns on options a, b, and c when "
"'foo' is included in list"),
cl::CommaSeparated,
cl::callback([&](const std::string &Str) {
if (Str == "foo")
OptC = true;
}));
const char *args1[] = {"prog", "-a"};
EXPECT_TRUE(cl::ParseCommandLineOptions(2, args1));
EXPECT_TRUE(OptA);
EXPECT_FALSE(OptB);
EXPECT_FALSE(OptC);
EXPECT_TRUE(List.size() == 0);
cl::ResetAllOptionOccurrences();
const char *args2[] = {"prog", "-b"};
EXPECT_TRUE(cl::ParseCommandLineOptions(2, args2));
EXPECT_TRUE(OptA);
EXPECT_TRUE(OptB);
EXPECT_FALSE(OptC);
EXPECT_TRUE(List.size() == 0);
cl::ResetAllOptionOccurrences();
const char *args3[] = {"prog", "-c"};
EXPECT_TRUE(cl::ParseCommandLineOptions(2, args3));
EXPECT_TRUE(OptA);
EXPECT_TRUE(OptB);
EXPECT_TRUE(OptC);
EXPECT_TRUE(List.size() == 0);
cl::ResetAllOptionOccurrences();
const char *args4[] = {"prog", "--list=foo,bar"};
EXPECT_TRUE(cl::ParseCommandLineOptions(2, args4));
EXPECT_TRUE(OptA);
EXPECT_TRUE(OptB);
EXPECT_TRUE(OptC);
EXPECT_TRUE(List.size() == 2);
cl::ResetAllOptionOccurrences();
const char *args5[] = {"prog", "--list=bar"};
EXPECT_TRUE(cl::ParseCommandLineOptions(2, args5));
EXPECT_FALSE(OptA);
EXPECT_FALSE(OptB);
EXPECT_FALSE(OptC);
EXPECT_TRUE(List.size() == 1);
cl::ResetAllOptionOccurrences();
}
} // anonymous namespace