Skip to content

Commit 6555995

Browse files
author
Don Hinton
committed
[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
1 parent 0a717d5 commit 6555995

File tree

4 files changed

+149
-2
lines changed

4 files changed

+149
-2
lines changed

llvm/docs/CommandLine.rst

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -996,6 +996,31 @@ This section describes the basic attributes that you can specify on options.
996996
* The **cl::cat** attribute specifies the option category that the option
997997
belongs to. The category should be a `cl::OptionCategory`_ object.
998998

999+
.. _cl::callback:
1000+
1001+
* The **cl::callback** attribute specifies a callback function that is
1002+
called when an option is seen, and can be used to set other options,
1003+
as in option B implies option A. If the option is a `cl::list`_,
1004+
and `cl::CommaSeparated`_ is also specified, the callback will fire
1005+
once for each value. This could be used to validate combinations or
1006+
selectively set other options.
1007+
1008+
.. code-block:: c++
1009+
1010+
cl::opt<bool> OptA("a", cl::desc("option a"));
1011+
cl::opt<bool> OptB(
1012+
"b", cl::desc("option b -- This option turns on option a"),
1013+
cl::callback([&](const bool &) { OptA = true; }));
1014+
cl::list<std::string, cl::list<std::string>> List(
1015+
"list",
1016+
cl::desc("option list -- This option turns on options a when "
1017+
"'foo' is included in list"),
1018+
cl::CommaSeparated,
1019+
cl::callback([&](const std::string &Str) {
1020+
if (Str == "foo")
1021+
OptA = true;
1022+
}));
1023+
9991024
Option Modifiers
10001025
----------------
10011026

llvm/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ Non-comprehensive list of changes in this release
9090
``-cfguard-nochecks`` option. Note that this feature should always be used
9191
with optimizations enabled.
9292

93+
* ``Callbacks`` have been added to ``CommandLine Options``. These can
94+
be used to validate of selectively enable other options.
95+
9396
Changes to the LLVM IR
9497
----------------------
9598

llvm/include/llvm/Support/CommandLine.h

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,43 @@ struct sub {
471471
template <class Opt> void apply(Opt &O) const { O.addSubCommand(Sub); }
472472
};
473473

474+
// Specify a callback function to be called when an option is seen.
475+
// Can be used to set other options automatically.
476+
template <typename R, typename Ty> struct cb {
477+
std::function<R(Ty)> CB;
478+
479+
cb(std::function<R(Ty)> CB) : CB(CB) {}
480+
481+
template <typename Opt> void apply(Opt &O) const { O.setCallback(CB); }
482+
};
483+
484+
namespace detail {
485+
template <typename F>
486+
struct callback_traits : public callback_traits<decltype(&F::operator())> {};
487+
488+
template <typename R, typename C, typename... Args>
489+
struct callback_traits<R (C::*)(Args...) const> {
490+
using result_type = R;
491+
using arg_type = typename std::tuple_element<0, std::tuple<Args...>>::type;
492+
static_assert(sizeof...(Args) == 1, "callback function must have one and only one parameter");
493+
static_assert(std::is_same<result_type, void>::value,
494+
"callback return type must be void");
495+
static_assert(
496+
std::is_lvalue_reference<arg_type>::value &&
497+
std::is_const<typename std::remove_reference<arg_type>::type>::value,
498+
"callback arg_type must be a const lvalue reference");
499+
};
500+
} // namespace detail
501+
502+
template <typename F>
503+
cb<typename detail::callback_traits<F>::result_type,
504+
typename detail::callback_traits<F>::arg_type>
505+
callback(F CB) {
506+
using result_type = typename detail::callback_traits<F>::result_type;
507+
using arg_type = typename detail::callback_traits<F>::arg_type;
508+
return cb<result_type, arg_type>(CB);
509+
}
510+
474511
//===----------------------------------------------------------------------===//
475512
// OptionValue class
476513

@@ -1344,6 +1381,7 @@ class opt : public Option,
13441381
return true; // Parse error!
13451382
this->setValue(Val);
13461383
this->setPosition(pos);
1384+
Callback(Val);
13471385
return false;
13481386
}
13491387

@@ -1402,6 +1440,7 @@ class opt : public Option,
14021440

14031441
template <class T> DataType &operator=(const T &Val) {
14041442
this->setValue(Val);
1443+
Callback(Val);
14051444
return this->getValue();
14061445
}
14071446

@@ -1411,6 +1450,14 @@ class opt : public Option,
14111450
apply(this, Ms...);
14121451
done();
14131452
}
1453+
1454+
void setCallback(
1455+
std::function<void(const typename ParserClass::parser_data_type &)> CB) {
1456+
Callback = CB;
1457+
}
1458+
1459+
std::function<void(const typename ParserClass::parser_data_type &)> Callback =
1460+
[](const typename ParserClass::parser_data_type &) {};
14141461
};
14151462

14161463
extern template class opt<unsigned>;
@@ -1550,6 +1597,7 @@ class list : public Option, public list_storage<DataType, StorageClass> {
15501597
list_storage<DataType, StorageClass>::addValue(Val);
15511598
setPosition(pos);
15521599
Positions.push_back(pos);
1600+
Callback(Val);
15531601
return false;
15541602
}
15551603

@@ -1596,6 +1644,14 @@ class list : public Option, public list_storage<DataType, StorageClass> {
15961644
apply(this, Ms...);
15971645
done();
15981646
}
1647+
1648+
void setCallback(
1649+
std::function<void(const typename ParserClass::parser_data_type &)> CB) {
1650+
Callback = CB;
1651+
}
1652+
1653+
std::function<void(const typename ParserClass::parser_data_type &)> Callback =
1654+
[](const typename ParserClass::parser_data_type &) {};
15991655
};
16001656

16011657
// multi_val - Modifier to set the number of additional values.
@@ -1696,6 +1752,7 @@ class bits : public Option, public bits_storage<DataType, Storage> {
16961752
this->addValue(Val);
16971753
setPosition(pos);
16981754
Positions.push_back(pos);
1755+
Callback(Val);
16991756
return false;
17001757
}
17011758

llvm/unittests/Support/CommandLineTest.cpp

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ class StackOption : public Base {
7171
~StackOption() override { this->removeArgument(); }
7272

7373
template <class DT> StackOption<T> &operator=(const DT &V) {
74-
this->setValue(V);
74+
Base::operator=(V);
7575
return *this;
7676
}
7777
};
@@ -1722,4 +1722,66 @@ TEST(CommandLineTest, OptionErrorMessageSuggest) {
17221722

17231723
cl::ResetAllOptionOccurrences();
17241724
}
1725-
} // anonymous namespace
1725+
1726+
TEST(CommandLineTest, Callback) {
1727+
cl::ResetCommandLineParser();
1728+
1729+
StackOption<bool> OptA("a", cl::desc("option a"));
1730+
StackOption<bool> OptB(
1731+
"b", cl::desc("option b -- This option turns on option a"),
1732+
cl::callback([&](const bool &) { OptA = true; }));
1733+
StackOption<bool> OptC(
1734+
"c", cl::desc("option c -- This option turns on options a and b"),
1735+
cl::callback([&](const bool &) { OptB = true; }));
1736+
StackOption<std::string, cl::list<std::string>> List(
1737+
"list",
1738+
cl::desc("option list -- This option turns on options a, b, and c when "
1739+
"'foo' is included in list"),
1740+
cl::CommaSeparated,
1741+
cl::callback([&](const std::string &Str) {
1742+
if (Str == "foo")
1743+
OptC = true;
1744+
}));
1745+
1746+
const char *args1[] = {"prog", "-a"};
1747+
EXPECT_TRUE(cl::ParseCommandLineOptions(2, args1));
1748+
EXPECT_TRUE(OptA);
1749+
EXPECT_FALSE(OptB);
1750+
EXPECT_FALSE(OptC);
1751+
EXPECT_TRUE(List.size() == 0);
1752+
cl::ResetAllOptionOccurrences();
1753+
1754+
const char *args2[] = {"prog", "-b"};
1755+
EXPECT_TRUE(cl::ParseCommandLineOptions(2, args2));
1756+
EXPECT_TRUE(OptA);
1757+
EXPECT_TRUE(OptB);
1758+
EXPECT_FALSE(OptC);
1759+
EXPECT_TRUE(List.size() == 0);
1760+
cl::ResetAllOptionOccurrences();
1761+
1762+
const char *args3[] = {"prog", "-c"};
1763+
EXPECT_TRUE(cl::ParseCommandLineOptions(2, args3));
1764+
EXPECT_TRUE(OptA);
1765+
EXPECT_TRUE(OptB);
1766+
EXPECT_TRUE(OptC);
1767+
EXPECT_TRUE(List.size() == 0);
1768+
cl::ResetAllOptionOccurrences();
1769+
1770+
const char *args4[] = {"prog", "--list=foo,bar"};
1771+
EXPECT_TRUE(cl::ParseCommandLineOptions(2, args4));
1772+
EXPECT_TRUE(OptA);
1773+
EXPECT_TRUE(OptB);
1774+
EXPECT_TRUE(OptC);
1775+
EXPECT_TRUE(List.size() == 2);
1776+
cl::ResetAllOptionOccurrences();
1777+
1778+
const char *args5[] = {"prog", "--list=bar"};
1779+
EXPECT_TRUE(cl::ParseCommandLineOptions(2, args5));
1780+
EXPECT_FALSE(OptA);
1781+
EXPECT_FALSE(OptB);
1782+
EXPECT_FALSE(OptC);
1783+
EXPECT_TRUE(List.size() == 1);
1784+
1785+
cl::ResetAllOptionOccurrences();
1786+
}
1787+
} // anonymous namespace

0 commit comments

Comments
 (0)