summaryrefslogtreecommitdiff
path: root/indra/llcommon
diff options
context:
space:
mode:
authorNat Goodspeed <nat@lindenlab.com>2022-06-22 10:51:11 -0400
committerNat Goodspeed <nat@lindenlab.com>2022-06-22 10:51:11 -0400
commit6b53036f7499a4e42813378009050eaf02c0b69d (patch)
treecfa65d470b614a6ef246dce074186e54b4ffbc23 /indra/llcommon
parentf9d810ac2a02ef96c843e214c7479146dd4f4157 (diff)
DRTVWR-564: Allow LLLeapListener to report LazyEventAPIs too.
One important factor in the design of LazyEventAPI was the desire to allow LLLeapListener to query metadata for an LLEventAPI even if it hasn't yet been instantiated by LazyEventAPI. That's why LazyEventAPI requires the same metadata required by a classic LLEventAPI. Instead of just publicly exposing its data members, give LazyEventAPI a query API mimicking LLEventAPI / LLEventDispatcher. Protect data members and private methods. Adapt lazyeventapi_test.cpp accordingly. Extend LLLeapListener::getAPIs() and getAPI() to look through LazyEventAPIBase instances after first checking existing LLEventAPI instances. Because the query API for LazyEventAPIBase mimics LLEventAPI's, extract getAPI()'s actual metadata reporting to a new internal template function reportAPI(). While we're touching LLLeapListener, we no longer need BOOST_FOREACH().
Diffstat (limited to 'indra/llcommon')
-rw-r--r--indra/llcommon/lazyeventapi.cpp19
-rw-r--r--indra/llcommon/lazyeventapi.h36
-rw-r--r--indra/llcommon/llleaplistener.cpp70
-rw-r--r--indra/llcommon/tests/lazyeventapi_test.cpp24
4 files changed, 120 insertions, 29 deletions
diff --git a/indra/llcommon/lazyeventapi.cpp b/indra/llcommon/lazyeventapi.cpp
index aefc2db6da..028af9f33f 100644
--- a/indra/llcommon/lazyeventapi.cpp
+++ b/indra/llcommon/lazyeventapi.cpp
@@ -15,9 +15,11 @@
#include "lazyeventapi.h"
// STL headers
// std headers
+#include <algorithm> // std::find_if
// external library headers
// other Linden headers
#include "llevents.h"
+#include "llsdutil.h"
LL::LazyEventAPIBase::LazyEventAPIBase(
const std::string& name, const std::string& desc, const std::string& field)
@@ -51,3 +53,20 @@ LL::LazyEventAPIBase::~LazyEventAPIBase()
LLEventPumps::instance().unregisterPumpFactory(mParams.name);
}
}
+
+LLSD LL::LazyEventAPIBase::getMetadata(const std::string& name) const
+{
+ // Since mOperations is a vector rather than a map, just search.
+ auto found = std::find_if(mOperations.begin(), mOperations.end(),
+ [&name](const auto& namedesc)
+ { return (namedesc.first == name); });
+ if (found == mOperations.end())
+ return {};
+
+ // LLEventDispatcher() supplements the returned metadata in different
+ // ways, depending on metadata provided to the specific add() method.
+ // Don't try to emulate all that. At some point we might consider more
+ // closely unifying LLEventDispatcher machinery with LazyEventAPI, but for
+ // now this will have to do.
+ return llsd::map("name", found->first, "desc", found->second);
+}
diff --git a/indra/llcommon/lazyeventapi.h b/indra/llcommon/lazyeventapi.h
index 7267a3e4ec..a815b119f0 100644
--- a/indra/llcommon/lazyeventapi.h
+++ b/indra/llcommon/lazyeventapi.h
@@ -63,9 +63,6 @@ namespace LL
LazyEventAPIBase& operator=(const LazyEventAPIBase&) = delete;
LazyEventAPIBase& operator=(LazyEventAPIBase&&) = delete;
- // actually instantiate the companion LLEventAPI subclass
- virtual LLEventPump* construct(const std::string& name) = 0;
-
// capture add() calls we want to play back on LLEventAPI construction
template <typename... ARGS>
void add(const std::string& name, const std::string& desc, ARGS&&... rest)
@@ -96,12 +93,40 @@ namespace LL
});
}
+ // The following queries mimic the LLEventAPI / LLEventDispatcher
+ // query API.
+
+ // Get the string name of the subject LLEventAPI
+ std::string getName() const { return mParams.name; }
+ // Get the documentation string
+ std::string getDesc() const { return mParams.desc; }
+ // Retrieve the LLSD key we use for dispatching
+ std::string getDispatchKey() const { return mParams.field; }
+
+ // operations
+ using NameDesc = std::pair<std::string, std::string>;
+
+ private:
// metadata that might be queried by LLLeapListener
- std::vector<std::pair<std::string, std::string>> mOperations;
+ std::vector<NameDesc> mOperations;
+
+ public:
+ using const_iterator = decltype(mOperations)::const_iterator;
+ const_iterator begin() const { return mOperations.begin(); }
+ const_iterator end() const { return mOperations.end(); }
+ LLSD getMetadata(const std::string& name) const;
+
+ protected:
// Params with which to instantiate the companion LLEventAPI subclass
LazyEventAPIParams mParams;
private:
+ // true if we successfully registered our LLEventAPI on construction
+ bool mRegistered;
+
+ // actually instantiate the companion LLEventAPI subclass
+ virtual LLEventPump* construct(const std::string& name) = 0;
+
// Passing an overloaded function to any function that accepts an
// arbitrary callable is a PITB because you have to specify the
// correct overload. What we want is for the compiler to select the
@@ -117,8 +142,6 @@ namespace LL
{
instance->add(std::forward<ARGS>(args)...);
}
-
- bool mRegistered;
};
/**
@@ -168,6 +191,7 @@ namespace LL
LazyEventAPIBase(name, desc, field)
{}
+ private:
LLEventPump* construct(const std::string& /*name*/) override
{
// base class has carefully assembled LazyEventAPIParams embedded
diff --git a/indra/llcommon/llleaplistener.cpp b/indra/llcommon/llleaplistener.cpp
index 11bfec1b31..471f52e91c 100644
--- a/indra/llcommon/llleaplistener.cpp
+++ b/indra/llcommon/llleaplistener.cpp
@@ -14,14 +14,16 @@
// associated header
#include "llleaplistener.h"
// STL headers
-#include <map>
+#include <algorithm> // std::find_if
#include <functional>
+#include <map>
+#include <set>
// std headers
// external library headers
-#include <boost/foreach.hpp>
// other Linden headers
-#include "lluuid.h"
+#include "lazyeventapi.h"
#include "llsdutil.h"
+#include "lluuid.h"
#include "stringize.h"
/*****************************************************************************
@@ -110,7 +112,7 @@ LLLeapListener::~LLLeapListener()
// value_type, and Bad Things would happen if you copied an
// LLTempBoundListener. (Destruction of the original would disconnect the
// listener, invalidating every stored connection.)
- BOOST_FOREACH(ListenersMap::value_type& pair, mListeners)
+ for (ListenersMap::value_type& pair : mListeners)
{
pair.second.disconnect();
}
@@ -208,31 +210,65 @@ void LLLeapListener::getAPIs(const LLSD& request) const
{
Response reply(LLSD(), request);
+ // first, traverse existing LLEventAPI instances
+ std::set<std::string> instances;
for (auto& ea : LLEventAPI::instance_snapshot())
{
- LLSD info;
- info["desc"] = ea.getDesc();
- reply[ea.getName()] = info;
+ // remember which APIs are actually instantiated
+ instances.insert(ea.getName());
+ reply[ea.getName()] = llsd::map("desc", ea.getDesc());
+ }
+ // supplement that with *potential* instances: that is, instances of
+ // LazyEventAPI that can each instantiate an LLEventAPI on demand
+ for (const auto& lea : LL::LazyEventAPIBase::instance_snapshot())
+ {
+ // skip any LazyEventAPI that's already instantiated its LLEventAPI
+ if (instances.find(lea.getName()) == instances.end())
+ {
+ reply[lea.getName()] = llsd::map("desc", lea.getDesc());
+ }
}
}
+// Because LazyEventAPI deliberately mimics LLEventAPI's query API, this
+// function can be passed either -- even though they're unrelated types.
+template <typename API>
+void reportAPI(LLEventAPI::Response& reply, const API& api)
+{
+ reply["name"] = api.getName();
+ reply["desc"] = api.getDesc();
+ reply["key"] = api.getDispatchKey();
+ LLSD ops;
+ for (const auto& namedesc : api)
+ {
+ ops.append(api.getMetadata(namedesc.first));
+ }
+ reply["ops"] = ops;
+}
+
void LLLeapListener::getAPI(const LLSD& request) const
{
Response reply(LLSD(), request);
- auto found = LLEventAPI::getInstance(request["api"]);
- if (found)
+ // check first among existing LLEventAPI instances
+ auto foundea = LLEventAPI::getInstance(request["api"]);
+ if (foundea)
+ {
+ reportAPI(reply, *foundea);
+ }
+ else
{
- reply["name"] = found->getName();
- reply["desc"] = found->getDesc();
- reply["key"] = found->getDispatchKey();
- LLSD ops;
- for (LLEventAPI::const_iterator oi(found->begin()), oend(found->end());
- oi != oend; ++oi)
+ // Here the requested LLEventAPI doesn't yet exist, but do we have a
+ // registered LazyEventAPI for it?
+ LL::LazyEventAPIBase::instance_snapshot snap;
+ auto foundlea = std::find_if(snap.begin(), snap.end(),
+ [api = request["api"].asString()]
+ (const auto& lea)
+ { return (lea.getName() == api); });
+ if (foundlea != snap.end())
{
- ops.append(found->getMetadata(oi->first));
+ reportAPI(reply, *foundlea);
}
- reply["ops"] = ops;
}
}
diff --git a/indra/llcommon/tests/lazyeventapi_test.cpp b/indra/llcommon/tests/lazyeventapi_test.cpp
index 4c78fd7105..31b2d6d17f 100644
--- a/indra/llcommon/tests/lazyeventapi_test.cpp
+++ b/indra/llcommon/tests/lazyeventapi_test.cpp
@@ -109,16 +109,28 @@ namespace tut
{
set_test_name("LazyEventAPI metadata");
MyRegistrar regster;
+ // Of course we have 'regster' in hand; we don't need to search for
+ // it. But this next test verifies that we can find (all) LazyEventAPI
+ // instances using LazyEventAPIBase::instance_snapshot. Normally we
+ // wouldn't search; normally we'd just look at each instance in the
+ // loop body.
const MyRegistrar* found = nullptr;
for (const auto& registrar : LL::LazyEventAPIBase::instance_snapshot())
if ((found = dynamic_cast<const MyRegistrar*>(&registrar)))
break;
ensure("Failed to find MyRegistrar via LLInstanceTracker", found);
- ensure_equals("wrong API name", found->mParams.name, "Test");
- ensure_contains("wrong API desc", found->mParams.desc, "test LLEventAPI");
- ensure_equals("wrong API field", found->mParams.field, "op");
- ensure_equals("failed to find operations", found->mOperations.size(), 1);
- ensure_equals("wrong operation name", found->mOperations[0].first, "set");
- ensure_contains("wrong operation desc", found->mOperations[0].second, "set operation");
+
+ ensure_equals("wrong API name", found->getName(), "Test");
+ ensure_contains("wrong API desc", found->getDesc(), "test LLEventAPI");
+ ensure_equals("wrong API field", found->getDispatchKey(), "op");
+ // Normally we'd just iterate over *found. But for test purposes,
+ // actually capture the range of NameDesc pairs in a vector.
+ std::vector<LL::LazyEventAPIBase::NameDesc> ops{ found->begin(), found->end() };
+ ensure_equals("failed to find operations", ops.size(), 1);
+ ensure_equals("wrong operation name", ops[0].first, "set");
+ ensure_contains("wrong operation desc", ops[0].second, "set operation");
+ LLSD metadata{ found->getMetadata(ops[0].first) };
+ ensure_equals("bad metadata name", metadata["name"].asString(), ops[0].first);
+ ensure_equals("bad metadata desc", metadata["desc"].asString(), ops[0].second);
}
} // namespace tut