[llvm] 6555995 - [CommandLine] Add callbacks to Options
Don Hinton via llvm-commits
llvm-commits at lists.llvm.org
Fri Dec 6 15:25:26 PST 2019
Author: Don Hinton
Date: 2019-12-06T15:16:45-08:00
New Revision: 6555995a6d4545ff59dcf3388f9acfce3b6129a5
URL: https://github.com/llvm/llvm-project/commit/6555995a6d4545ff59dcf3388f9acfce3b6129a5
DIFF: https://github.com/llvm/llvm-project/commit/6555995a6d4545ff59dcf3388f9acfce3b6129a5.diff
LOG: [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
Added:
Modified:
llvm/docs/CommandLine.rst
llvm/docs/ReleaseNotes.rst
llvm/include/llvm/Support/CommandLine.h
llvm/unittests/Support/CommandLineTest.cpp
Removed:
################################################################################
diff --git a/llvm/docs/CommandLine.rst b/llvm/docs/CommandLine.rst
index a7db630d17d4..ab2826d789f2 100644
--- a/llvm/docs/CommandLine.rst
+++ b/llvm/docs/CommandLine.rst
@@ -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
----------------
diff --git a/llvm/docs/ReleaseNotes.rst b/llvm/docs/ReleaseNotes.rst
index c27f3bc8b692..714011612d2a 100644
--- a/llvm/docs/ReleaseNotes.rst
+++ b/llvm/docs/ReleaseNotes.rst
@@ -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
----------------------
diff --git a/llvm/include/llvm/Support/CommandLine.h b/llvm/include/llvm/Support/CommandLine.h
index 6c0bb6c2fc3a..8c1d35c3cbc7 100644
--- a/llvm/include/llvm/Support/CommandLine.h
+++ b/llvm/include/llvm/Support/CommandLine.h
@@ -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 @@ class opt : public Option,
template <class T> DataType &operator=(const T &Val) {
this->setValue(Val);
+ Callback(Val);
return this->getValue();
}
@@ -1411,6 +1450,14 @@ class opt : public Option,
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 @@ class list : public Option, public list_storage<DataType, StorageClass> {
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;
}
diff --git a/llvm/unittests/Support/CommandLineTest.cpp b/llvm/unittests/Support/CommandLineTest.cpp
index 694fec20a084..702aa526fdaf 100644
--- a/llvm/unittests/Support/CommandLineTest.cpp
+++ b/llvm/unittests/Support/CommandLineTest.cpp
@@ -71,7 +71,7 @@ class StackOption : public Base {
~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();
}
-} // anonymous namespace
+
+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
More information about the llvm-commits
mailing list