[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