diff options
author | Nat Goodspeed <nat@lindenlab.com> | 2022-12-19 17:27:36 -0500 |
---|---|---|
committer | Nat Goodspeed <nat@lindenlab.com> | 2023-07-13 12:34:31 -0400 |
commit | 196e49c1f8bd58ab9ce81843c10d452284ca7569 (patch) | |
tree | d7769b713adbbdcc1488cb6dd306c9d240748088 | |
parent | c682603417e1ef8290aacf274ff49821bd204c0b (diff) |
DRTVWR-558: Add unit test for VAPPLY().
Add to apply_test.cpp a collect() function that incrementally accumulates an
arbitrary number of arguments into a std::vector<std::string>. Construct a
std::array<std::string> to pass it, using VAPPLY().
Clarify in header comments that LL::apply() can't call a variadic function
with arguments of dynamic size: std::vector or LLSD. The compiler can deduce
how many arguments to pass to a function with a fixed argument list; it can
deduce how many arguments to pass to a variadic function with a fixed number
of arguments. But it can't compile a call to a variadic function with an
arguments data structure whose size can vary at runtime.
(cherry picked from commit ceed33396266b123896f7cfb9b90abdf240e1eec)
-rw-r--r-- | indra/llcommon/apply.h | 6 | ||||
-rw-r--r-- | indra/llcommon/llsdutil.h | 6 | ||||
-rw-r--r-- | indra/llcommon/tests/apply_test.cpp | 52 |
3 files changed, 62 insertions, 2 deletions
diff --git a/indra/llcommon/apply.h b/indra/llcommon/apply.h index 26753f5017..9f4c268895 100644 --- a/indra/llcommon/apply.h +++ b/indra/llcommon/apply.h @@ -108,7 +108,11 @@ auto apply_impl(CALLABLE&& func, const std::vector<T>& args, std::index_sequence std::make_tuple(args[I]...)); } -// this goes beyond C++17 std::apply() +/** + * apply(function, std::vector) goes beyond C++17 std::apply(). For this case + * @a function @emph cannot be variadic: the compiler must know at compile + * time how many arguments to pass. This isn't Python. + */ template <typename CALLABLE, typename T> auto apply(CALLABLE&& func, const std::vector<T>& args) { diff --git a/indra/llcommon/llsdutil.h b/indra/llcommon/llsdutil.h index eaf8825791..eddaa64bd2 100644 --- a/indra/llcommon/llsdutil.h +++ b/indra/llcommon/llsdutil.h @@ -31,6 +31,7 @@ #include "llsd.h" #include <boost/functional/hash.hpp> +#include <boost/type_traits.hpp> #include <cassert> // U32 @@ -601,6 +602,11 @@ auto apply_impl(CALLABLE&& func, const LLSD& array, std::index_sequence<I...>) return std::forward<CALLABLE>(func)(LLSDParam<LLSD>(array[I])...); } +/** + * apply(function, LLSD) goes beyond C++17 std::apply(). For this case + * @a function @emph cannot be variadic: the compiler must know at compile + * time how many arguments to pass. This isn't Python. + */ template <typename CALLABLE> auto apply(CALLABLE&& func, const LLSD& args) { diff --git a/indra/llcommon/tests/apply_test.cpp b/indra/llcommon/tests/apply_test.cpp index 1f1085a702..28ee3f9c81 100644 --- a/indra/llcommon/tests/apply_test.cpp +++ b/indra/llcommon/tests/apply_test.cpp @@ -15,12 +15,27 @@ #include "apply.h" // STL headers // std headers +#include <iomanip> // external library headers // other Linden headers -#include "../test/lltut.h" #include "llsd.h" #include "llsdutil.h" +// for ensure_equals +std::ostream& operator<<(std::ostream& out, const std::vector<std::string>& stringvec) +{ + const char* delim = "["; + for (const auto& str : stringvec) + { + out << delim << std::quoted(str); + delim = ", "; + } + return out << ']'; +} + +// the above must be declared BEFORE ensure_equals(std::vector<std::string>) +#include "../test/lltut.h" + /***************************************************************************** * TUT *****************************************************************************/ @@ -54,6 +69,8 @@ namespace tut // ensure that apply() actually reaches the target method -- // lack of ensure_equals() failure could be due to no-op apply() bool called{ false }; + // capture calls from collect() + std::vector<std::string> collected; /*------------------------- test functions -------------------------*/ void various(LLSD::Boolean b, LLSD::Integer i, LLSD::Real f, const LLSD::String& s, @@ -101,6 +118,20 @@ namespace tut { called = true; } + + // recursion tail + void collect() + { + called = true; + } + + // collect(arbitrary) + template <typename... ARGS> + void collect(const std::string& first, ARGS&&... rest) + { + statics::collected.push_back(first); + collect(std::forward<ARGS>(rest)...); + } } // namespace statics struct apply_data @@ -109,6 +140,7 @@ namespace tut { // reset called before each test statics::called = false; + statics::collected.clear(); } }; typedef test_group<apply_data> apply_group; @@ -168,4 +200,22 @@ namespace tut statics::uu, statics::dt, statics::uri, statics::bin)); ensure("apply(LLSD array) failed", statics::called); } + + template<> template<> + void object::test<7>() + { + set_test_name("VAPPLY()"); + // Make a std::array<std::string> from statics::quick. We can't call a + // variadic function with a data structure of dynamic length. + std::array<std::string, 5> strray; + for (size_t i = 0; i < strray.size(); ++i) + strray[i] = statics::quick[i]; + // This doesn't work: the compiler doesn't know which overload of + // collect() to pass to LL::apply(). + // LL::apply(statics::collect, strray); + // That's what VAPPLY() is for. + VAPPLY(statics::collect, strray); + ensure("VAPPLY() failed", statics::called); + ensure_equals("collected mismatch", statics::collected, statics::quick); + } } // namespace tut |