diff options
| -rw-r--r-- | indra/llcommon/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | indra/llcommon/lazyeventapi.cpp | 53 | ||||
| -rw-r--r-- | indra/llcommon/lazyeventapi.h | 204 | ||||
| -rw-r--r-- | indra/llcommon/lleventapi.cpp | 8 | ||||
| -rw-r--r-- | indra/llcommon/lleventapi.h | 23 | ||||
| -rw-r--r-- | indra/llcommon/lleventdispatcher.cpp | 13 | ||||
| -rw-r--r-- | indra/llcommon/lleventdispatcher.h | 105 | ||||
| -rw-r--r-- | indra/llcommon/llevents.cpp | 29 | ||||
| -rw-r--r-- | indra/llcommon/llevents.h | 6 | ||||
| -rw-r--r-- | indra/llcommon/tests/lazyeventapi_test.cpp | 89 | 
10 files changed, 466 insertions, 68 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index ca8b5e946f..36b2e09dc5 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -30,6 +30,7 @@ include_directories(  set(llcommon_SOURCE_FILES      indra_constants.cpp +    lazyeventapi.cpp      llallocator.cpp      llallocator_heap_profile.cpp      llapp.cpp @@ -128,10 +129,12 @@ set(llcommon_SOURCE_FILES  set(llcommon_HEADER_FILES      CMakeLists.txt +    apply.h      chrono.h      ctype_workaround.h      fix_macros.h      indra_constants.h +    lazyeventapi.h      linden_common.h      llalignedarray.h      llallocator.h @@ -338,6 +341,7 @@ if (LL_TESTS)        ${BOOST_SYSTEM_LIBRARY})    LL_ADD_INTEGRATION_TEST(commonmisc "" "${test_libs}")    LL_ADD_INTEGRATION_TEST(bitpack "" "${test_libs}") +  LL_ADD_INTEGRATION_TEST(lazyeventapi "" "${test_libs}")    LL_ADD_INTEGRATION_TEST(llbase64 "" "${test_libs}")    LL_ADD_INTEGRATION_TEST(llcond "" "${test_libs}")    LL_ADD_INTEGRATION_TEST(lldate "" "${test_libs}") diff --git a/indra/llcommon/lazyeventapi.cpp b/indra/llcommon/lazyeventapi.cpp new file mode 100644 index 0000000000..aefc2db6da --- /dev/null +++ b/indra/llcommon/lazyeventapi.cpp @@ -0,0 +1,53 @@ +/** + * @file   lazyeventapi.cpp + * @author Nat Goodspeed + * @date   2022-06-17 + * @brief  Implementation for lazyeventapi. + *  + * $LicenseInfo:firstyear=2022&license=viewerlgpl$ + * Copyright (c) 2022, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "lazyeventapi.h" +// STL headers +// std headers +// external library headers +// other Linden headers +#include "llevents.h" + +LL::LazyEventAPIBase::LazyEventAPIBase( +    const std::string& name, const std::string& desc, const std::string& field) +{ +    // populate embedded LazyEventAPIParams instance +    mParams.name = name; +    mParams.desc = desc; +    mParams.field = field; +    // mParams.init and mOperations are populated by subsequent add() calls. + +    // Our raison d'etre: register as an LLEventPumps::PumpFactory +    // so obtain() will notice any request for this name and call us. +    // Of course, our subclass constructor must finish running (making add() +    // calls) before mParams will be fully populated, but we expect that to +    // happen well before the first LLEventPumps::obtain(name) call. +    mRegistered = LLEventPumps::instance().registerPumpFactory( +        name, +        [this](const std::string& name){ return construct(name); }); +} + +LL::LazyEventAPIBase::~LazyEventAPIBase() +{ +    // If our constructor's registerPumpFactory() call was unsuccessful, that +    // probably means somebody else claimed the name first. If that's the +    // case, do NOT unregister their name out from under them! +    // If this is a static instance being destroyed at process shutdown, +    // LLEventPumps will probably have been cleaned up already. +    if (mRegistered && ! LLEventPumps::wasDeleted()) +    { +        // unregister the callback to this doomed instance +        LLEventPumps::instance().unregisterPumpFactory(mParams.name); +    } +} diff --git a/indra/llcommon/lazyeventapi.h b/indra/llcommon/lazyeventapi.h new file mode 100644 index 0000000000..2e947899dc --- /dev/null +++ b/indra/llcommon/lazyeventapi.h @@ -0,0 +1,204 @@ +/** + * @file   lazyeventapi.h + * @author Nat Goodspeed + * @date   2022-06-16 + * @brief  Declaring a static module-scope LazyEventAPI registers a specific + *         LLEventAPI for future on-demand instantiation. + *  + * $LicenseInfo:firstyear=2022&license=viewerlgpl$ + * Copyright (c) 2022, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LAZYEVENTAPI_H) +#define LL_LAZYEVENTAPI_H + +#include "apply.h" +#include "lleventapi.h" +#include "llinstancetracker.h" +#include <boost/signals2/signal.hpp> +#include <string> +#include <tuple> +#include <utility>                  // std::pair +#include <vector> + +namespace LL +{ +    /** +     * Bundle params we want to pass to LLEventAPI's protected constructor. We +     * package them this way so a subclass constructor can simply forward an +     * opaque reference to the LLEventAPI constructor. +     */ +    // This is a class instead of a plain struct mostly so when we forward- +    // declare it we don't have to remember the distinction. +    class LazyEventAPIParams +    { +    public: +        // package the parameters used by the normal LLEventAPI constructor +        std::string name, desc, field; +        // bundle LLEventAPI::add() calls collected by LazyEventAPI::add(), so +        // the special LLEventAPI constructor we engage can "play back" those +        // add() calls +        boost::signals2::signal<void(LLEventAPI*)> init; +    }; + +    // The tricky part is: can we capture a sequence of add() calls in the +    // LazyEventAPI subclass constructor and then, in effect, replay those +    // add() calls on instantiation of the registered LLEventAPI subclass? so +    // we don't have to duplicate the add() calls in both constructors? + +    // Derive a subclass from LazyEventAPI. Its constructor must pass +    // LazyEventAPI's constructor the name, desc, field params. Moreover the +    // constructor body must call add(name, desc, *args) for any of the +    // LLEventDispatcher add() methods, referencing the LLEventAPI subclass +    // methods. + +    // LazyEventAPI will store the name, desc, field params for the overall +    // LLEventAPI. It will support a single generic add() call accepting name, +    // desc, parameter pack. + +    // It will hold a std::vector<std::pair<name, desc>> for each operation. +    // It will make all these strings available to LLLeapListener. + +    // Maybe what we want is to store a vector of callables (a +    // boost::signals2!) and populate it with lambdas, each of which accepts +    // LLEventAPI* and calls the relevant add() method by forwarding exactly +    // the name, desc and parameter pack. Then, on constructing the target +    // LLEventAPI, we just fire the signal, passing the new instance pointer. + +    /** +     * LazyEventAPIBase implements most of the functionality of LazyEventAPI +     * (q.v.), but we need the LazyEventAPI template subclass so we can accept +     * the specific LLEventAPI subclass type. +     */ +    // No LLInstanceTracker key: we don't need to find a specific instance, +    // LLLeapListener just needs to be able to enumerate all instances. +    class LazyEventAPIBase: public LLInstanceTracker<LazyEventAPIBase> +    { +    public: +        LazyEventAPIBase(const std::string& name, const std::string& desc, +                         const std::string& field); +        virtual ~LazyEventAPIBase(); + +        // Do not copy or move: once constructed, LazyEventAPIBase must stay +        // put: we bind its instance pointer into a callback. +        LazyEventAPIBase(const LazyEventAPIBase&) = delete; +        LazyEventAPIBase(LazyEventAPIBase&&) = delete; +        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) +        { +            // capture the metadata separately +            mOperations.push_back(std::make_pair(name, desc)); +            // Use connect_extended() so the lambda is passed its own +            // connection. +            // We can't bind an unexpanded parameter pack into a lambda -- +            // shame really. Instead, capture it as a std::tuple and then, in +            // the lambda, use apply() to convert back to function args. +            mParams.init.connect_extended( +                [name, desc, rest = std::make_tuple(std::forward<ARGS>(rest)...)] +                (const boost::signals2::connection& conn, LLEventAPI* instance) +                { +                    // we only need this connection once +                    conn.disconnect(); +                    // Our add() method distinguishes name and desc because we +                    // capture them separately. But now, because apply() +                    // expects a tuple specifying ALL the arguments, expand to +                    // a tuple including add_trampoline() arguments: instance, +                    // name, desc, rest. +                    // apply() can't accept a template per se; it needs a +                    // particular specialization. +                    apply(&LazyEventAPIBase::add_trampoline<const std::string&, const std::string&,  ARGS...>, +                          std::tuple_cat(std::make_tuple(instance, name, desc), +                                         rest)); +                }); +        } + +        // metadata that might be queried by LLLeapListener +        std::vector<std::pair<std::string, std::string>> mOperations; +        // Params with which to instantiate the companion LLEventAPI subclass +        LazyEventAPIParams mParams; + +    private: +        // 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 +        // correct overload, based on the carefully-wrought enable_ifs in +        // LLEventDispatcher. This (one and only) add_trampoline() method +        // exists solely to pass to LL::apply(). Once add_trampoline() is +        // called with the expanded arguments, we hope the compiler will Do +        // The Right Thing in selecting the correct LLEventAPI::add() +        // overload. +        template <typename... ARGS> +        static +        void add_trampoline(LLEventAPI* instance, ARGS&&... args) +        { +            instance->add(std::forward<ARGS>(args)...); +        } + +        bool mRegistered; +    }; + +    /** +     * LazyEventAPI provides a way to register a particular LLEventAPI to be +     * instantiated on demand, that is, when its name is passed to +     * LLEventPumps::obtain(). +     * +     * Derive your listener from LLEventAPI as usual, with its various +     * operation methods, but code your constructor to accept +     * <tt>(const LL::LazyEventAPIParams& params)</tt> +     * and forward that reference to (the protected) +     * <tt>LLEventAPI(const LL::LazyEventAPIParams&)</tt> constructor. +     * +     * Then derive your listener registrar from +     * <tt>LazyEventAPI<your LLEventAPI subclass></tt>. The constructor should +     * look very like a traditional LLEventAPI constructor: +     * +     * * pass (name, desc [, field]) to LazyEventAPI's constructor +     * * in the body, make a series of add() calls referencing your LLEventAPI +     *   subclass methods. +     * +     * You may use any LLEventAPI::add() methods, that is, any +     * LLEventDispatcher::add() methods. But the target methods you pass to +     * add() must belong to your LLEventAPI subclass, not the LazyEventAPI +     * subclass. +     * +     * Declare a static instance of your LazyEventAPI listener registrar +     * class. When it's constructed at static initialization time, it will +     * register your LLEventAPI subclass with LLEventPumps. It will also +     * collect metadata for the LLEventAPI and its operations to provide to +     * LLLeapListener's introspection queries. +     * +     * When someone later calls LLEventPumps::obtain() to post an event to +     * your LLEventAPI subclass, obtain() will instantiate it using +     * LazyEventAPI's name, desc, field and add() calls. +     */ +    template <class EVENTAPI> +    class LazyEventAPI: public LazyEventAPIBase +    { +    public: +        // for subclass constructor to reference handler methods +        using listener = EVENTAPI; + +        LazyEventAPI(const std::string& name, const std::string& desc, +                     const std::string& field="op"): +            // Forward ctor params to LazyEventAPIBase +            LazyEventAPIBase(name, desc, field) +        {} + +        LLEventPump* construct(const std::string& /*name*/) override +        { +            // base class has carefully assembled LazyEventAPIParams embedded +            // in this instance, just pass to LLEventAPI subclass constructor +            return new EVENTAPI(mParams); +        } +    }; +} // namespace LL + +#endif /* ! defined(LL_LAZYEVENTAPI_H) */ diff --git a/indra/llcommon/lleventapi.cpp b/indra/llcommon/lleventapi.cpp index ff5459c1eb..3d46ef1034 100644 --- a/indra/llcommon/lleventapi.cpp +++ b/indra/llcommon/lleventapi.cpp @@ -35,6 +35,7 @@  // external library headers  // other Linden headers  #include "llerror.h" +#include "lazyeventapi.h"  LLEventAPI::LLEventAPI(const std::string& name, const std::string& desc, const std::string& field):      lbase(name, field), @@ -43,6 +44,13 @@ LLEventAPI::LLEventAPI(const std::string& name, const std::string& desc, const s  {  } +LLEventAPI::LLEventAPI(const LL::LazyEventAPIParams& params): +    LLEventAPI(params.name, params.desc, params.field) +{ +    // call initialization functions with our brand-new instance pointer +    params.init(this); +} +  LLEventAPI::~LLEventAPI()  {  } diff --git a/indra/llcommon/lleventapi.h b/indra/llcommon/lleventapi.h index ed62fa064a..a019458553 100644 --- a/indra/llcommon/lleventapi.h +++ b/indra/llcommon/lleventapi.h @@ -35,6 +35,13 @@  #include "llinstancetracker.h"  #include <string> +namespace LL +{ +    template <class EVENTAPI> +    class LazyEventAPI; +    class LazyEventAPIParams; +} +  /**   * LLEventAPI not only provides operation dispatch functionality, inherited   * from LLDispatchListener -- it also gives us event API introspection. @@ -45,6 +52,8 @@ class LL_COMMON_API LLEventAPI: public LLDispatchListener,  {      typedef LLDispatchListener lbase;      typedef LLInstanceTracker<LLEventAPI, std::string> ibase; +    template <class EVENTAPI> +    friend class LL::LazyEventAPI;  public: @@ -137,16 +146,20 @@ public:           * @endcode           */          LLSD& operator[](const LLSD::String& key) { return mResp[key]; } -		 -		 /** -		 * set the response to the given data -		 */ -		void setResponse(LLSD const & response){ mResp = response; } + +         /** +         * set the response to the given data +         */ +        void setResponse(LLSD const & response){ mResp = response; }          LLSD mResp, mReq;          LLSD::String mKey;      }; +protected: +    // constructor used only by subclasses registered by LazyEventAPI +    LLEventAPI(const LL::LazyEventAPIParams&); +  private:      std::string mDesc;  }; diff --git a/indra/llcommon/lleventdispatcher.cpp b/indra/llcommon/lleventdispatcher.cpp index 742d6cf51f..bc53ec3da0 100644 --- a/indra/llcommon/lleventdispatcher.cpp +++ b/indra/llcommon/lleventdispatcher.cpp @@ -706,8 +706,17 @@ LLSD LLEventDispatcher::getMetadata(const std::string& name) const  LLDispatchListener::LLDispatchListener(const std::string& pumpname, const std::string& key):      LLEventDispatcher(pumpname, key), -    mPump(pumpname, true),          // allow tweaking for uniqueness -    mBoundListener(mPump.listen("self", boost::bind(&LLDispatchListener::process, this, _1))) +    // Do NOT tweak the passed pumpname. In practice, when someone +    // instantiates a subclass of our LLEventAPI subclass, they intend to +    // claim that LLEventPump name in the global LLEventPumps namespace. It +    // would be mysterious and distressing if we allowed name tweaking, and +    // someone else claimed pumpname first for a completely unrelated +    // LLEventPump. Posted events would never reach our subclass listener +    // because we would have silently changed its name; meanwhile listeners +    // (if any) on that other LLEventPump would be confused by the events +    // intended for our subclass. +    LLEventStream(pumpname, false), +    mBoundListener(listen("self", [this](const LLSD& event){ return process(event); }))  {  } diff --git a/indra/llcommon/lleventdispatcher.h b/indra/llcommon/lleventdispatcher.h index 1b3e834aeb..ce9d3775cc 100644 --- a/indra/llcommon/lleventdispatcher.h +++ b/indra/llcommon/lleventdispatcher.h @@ -165,12 +165,12 @@ public:       * When calling this name, pass an LLSD::Array. Each entry in turn will be       * converted to the corresponding parameter type using LLSDParam.       */ -    template<typename Function> -    typename std::enable_if< -        boost::function_types::is_nonmember_callable_builtin<Function>::value -        >::type add(const std::string& name, -                    const std::string& desc, -                    Function f); +    // enable_if usage per https://stackoverflow.com/a/39913395/5533635 +    template<typename Function, +             typename = typename std::enable_if< +                 boost::function_types::is_nonmember_callable_builtin<Function>::value +             >::type> +    void add(const std::string& name, const std::string& desc, Function f);      /**       * Register a nonstatic class method with arbitrary parameters. @@ -189,14 +189,13 @@ public:       * When calling this name, pass an LLSD::Array. Each entry in turn will be       * converted to the corresponding parameter type using LLSDParam.       */ -    template<typename Method, typename InstanceGetter> -    typename std::enable_if< -        boost::function_types::is_member_function_pointer<Method>::value && -        ! std::is_convertible<InstanceGetter, LLSD>::value -        >::type add(const std::string& name, -                    const std::string& desc, -                    Method f, -                    const InstanceGetter& getter); +    template<typename Method, typename InstanceGetter, +             typename = typename std::enable_if< +                 boost::function_types::is_member_function_pointer<Method>::value && +                 ! std::is_convertible<InstanceGetter, LLSD>::value +             >::type> +    void add(const std::string& name, const std::string& desc, Method f, +             const InstanceGetter& getter);      /**       * Register a free function with arbitrary parameters. (This also works @@ -213,14 +212,12 @@ public:       * an LLSD::Array using LLSDArgsMapper and then convert each entry in turn       * to the corresponding parameter type using LLSDParam.       */ -    template<typename Function> -    typename std::enable_if< -        boost::function_types::is_nonmember_callable_builtin<Function>::value -        >::type add(const std::string& name, -                    const std::string& desc, -                    Function f, -                    const LLSD& params, -                    const LLSD& defaults=LLSD()); +    template<typename Function, +             typename = typename std::enable_if< +                 boost::function_types::is_nonmember_callable_builtin<Function>::value +             >::type> +    void add(const std::string& name, const std::string& desc, Function f, +             const LLSD& params, const LLSD& defaults=LLSD());      /**       * Register a nonstatic class method with arbitrary parameters. @@ -243,16 +240,14 @@ public:       * an LLSD::Array using LLSDArgsMapper and then convert each entry in turn       * to the corresponding parameter type using LLSDParam.       */ -    template<typename Method, typename InstanceGetter> -    typename std::enable_if< -        boost::function_types::is_member_function_pointer<Method>::value && -        ! std::is_convertible<InstanceGetter, LLSD>::value -        >::type add(const std::string& name, -                    const std::string& desc, -                    Method f, -                    const InstanceGetter& getter, -                    const LLSD& params, -                    const LLSD& defaults=LLSD()); +    template<typename Method, typename InstanceGetter, +             typename = typename std::enable_if< +                 boost::function_types::is_member_function_pointer<Method>::value && +                 ! std::is_convertible<InstanceGetter, LLSD>::value +             >::type> +    void add(const std::string& name, const std::string& desc, Method f, +             const InstanceGetter& getter, const LLSD& params, +             const LLSD& defaults=LLSD());      //@}     @@ -476,9 +471,8 @@ struct LLEventDispatcher::invoker<Function,To,To>      }  }; -template<typename Function> -typename std::enable_if< boost::function_types::is_nonmember_callable_builtin<Function>::value >::type -LLEventDispatcher::add(const std::string& name, const std::string& desc, Function f) +template<typename Function, typename> +void LLEventDispatcher::add(const std::string& name, const std::string& desc, Function f)  {      // Construct an invoker_function, a callable accepting const args_source&.      // Add to DispatchMap an ArrayParamsDispatchEntry that will handle the @@ -487,13 +481,9 @@ LLEventDispatcher::add(const std::string& name, const std::string& desc, Functio                                  boost::function_types::function_arity<Function>::value);  } -template<typename Method, typename InstanceGetter> -typename std::enable_if< -    boost::function_types::is_member_function_pointer<Method>::value && -    ! std::is_convertible<InstanceGetter, LLSD>::value ->::type -LLEventDispatcher::add(const std::string& name, const std::string& desc, Method f, -                       const InstanceGetter& getter) +template<typename Method, typename InstanceGetter, typename> +void LLEventDispatcher::add(const std::string& name, const std::string& desc, Method f, +                            const InstanceGetter& getter)  {      // Subtract 1 from the compile-time arity because the getter takes care of      // the first parameter. We only need (arity - 1) additional arguments. @@ -501,23 +491,18 @@ LLEventDispatcher::add(const std::string& name, const std::string& desc, Method                                  boost::function_types::function_arity<Method>::value - 1);  } -template<typename Function> -typename std::enable_if< boost::function_types::is_nonmember_callable_builtin<Function>::value >::type -LLEventDispatcher::add(const std::string& name, const std::string& desc, Function f, -                       const LLSD& params, const LLSD& defaults) +template<typename Function, typename> +void LLEventDispatcher::add(const std::string& name, const std::string& desc, Function f, +                            const LLSD& params, const LLSD& defaults)  {      // See comments for previous is_nonmember_callable_builtin add().      addMapParamsDispatchEntry(name, desc, make_invoker(f), params, defaults);  } -template<typename Method, typename InstanceGetter> -typename std::enable_if< -    boost::function_types::is_member_function_pointer<Method>::value && -    ! std::is_convertible<InstanceGetter, LLSD>::value ->::type -LLEventDispatcher::add(const std::string& name, const std::string& desc, Method f, -                       const InstanceGetter& getter, -                       const LLSD& params, const LLSD& defaults) +template<typename Method, typename InstanceGetter, typename> +void LLEventDispatcher::add(const std::string& name, const std::string& desc, Method f, +                            const InstanceGetter& getter, +                            const LLSD& params, const LLSD& defaults)  {      addMapParamsDispatchEntry(name, desc, make_invoker(f, getter), params, defaults);  } @@ -560,17 +545,21 @@ LLEventDispatcher::make_invoker(Method f, const InstanceGetter& getter)   * LLEventPump name and dispatch key, and add() its methods. Incoming events   * will automatically be dispatched.   */ -class LL_COMMON_API LLDispatchListener: public LLEventDispatcher +// Instead of containing an LLEventStream, LLDispatchListener derives from it. +// This allows an LLEventPumps::PumpFactory to return a pointer to an +// LLDispatchListener (subclass) instance, and still have ~LLEventPumps() +// properly clean it up. +class LL_COMMON_API LLDispatchListener: +    public LLEventDispatcher, +    public LLEventStream  {  public:      LLDispatchListener(const std::string& pumpname, const std::string& key); - -    std::string getPumpName() const { return mPump.getName(); } +    virtual ~LLDispatchListener() {}  private:      bool process(const LLSD& event); -    LLEventStream mPump;      LLTempBoundListener mBoundListener;  }; diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp index 5725dad9cc..1a305ec3dc 100644 --- a/indra/llcommon/llevents.cpp +++ b/indra/llcommon/llevents.cpp @@ -90,6 +90,13 @@ bool LLEventPumps::registerTypeFactory(const std::string& type, const TypeFactor      return true;  } +void LLEventPumps::unregisterTypeFactory(const std::string& type) +{ +    auto found = mFactories.find(type); +    if (found != mFactories.end()) +        mFactories.erase(found); +} +  bool LLEventPumps::registerPumpFactory(const std::string& name, const PumpFactory& factory)  {      // Do we already have a pump by this name? @@ -109,10 +116,30 @@ bool LLEventPumps::registerPumpFactory(const std::string& name, const PumpFactor      static std::string nul(1, '\0');      std::string type_name{ nul + name };      mTypes[name] = type_name; -    mFactories[type_name] = factory; +    // TypeFactory is called with (name, tweak, type), whereas PumpFactory +    // accepts only name. We could adapt with std::bind(), but this lambda +    // does the trick. +    mFactories[type_name] = +        [factory] +        (const std::string& name, bool /*tweak*/, const std::string& /*type*/) +        { return factory(name); };      return true;  } +void LLEventPumps::unregisterPumpFactory(const std::string& name) +{ +    auto tfound = mTypes.find(name); +    if (tfound != mTypes.end()) +    { +        auto ffound = mFactories.find(tfound->second); +        if (ffound != mFactories.end()) +        { +            mFactories.erase(ffound); +        } +        mTypes.erase(tfound); +    } +} +  LLEventPump& LLEventPumps::obtain(const std::string& name)  {      PumpMap::iterator found = mPumpMap.find(name); diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index 38adc31121..c1dbf4392f 100644 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -280,6 +280,7 @@ public:       * a TypeFactory for the specified @a type name.       */      bool registerTypeFactory(const std::string& type, const TypeFactory& factory); +    void unregisterTypeFactory(const std::string& type);      /// function passed to registerPumpFactory()      typedef std::function<LLEventPump*(const std::string&)> PumpFactory; @@ -304,6 +305,7 @@ public:       *   instantiated an LLEventPump(name), so obtain(name) returned that.       */      bool registerPumpFactory(const std::string& name, const PumpFactory& factory); +    void unregisterPumpFactory(const std::string& name);      /**       * Find the named LLEventPump instance. If it exists post the message to it. @@ -362,13 +364,13 @@ testable:      typedef std::set<LLEventPump*> PumpSet;      PumpSet mOurPumps;      // for make(), map string type name to LLEventPump subclass factory function -    typedef std::map<std::string, PumpFactory> PumpFactories; +    typedef std::map<std::string, TypeFactory> TypeFactories;      // Data used by make().      // One might think mFactories and mTypes could reasonably be static. So      // they could -- if not for the fact that make() or obtain() might be      // called before this module's static variables have been initialized.      // This is why we use singletons in the first place. -    PumpFactories mFactories; +    TypeFactories mFactories;      // for obtain(), map desired string instance name to string type when      // obtain() must create the instance diff --git a/indra/llcommon/tests/lazyeventapi_test.cpp b/indra/llcommon/tests/lazyeventapi_test.cpp new file mode 100644 index 0000000000..6639c5e540 --- /dev/null +++ b/indra/llcommon/tests/lazyeventapi_test.cpp @@ -0,0 +1,89 @@ +/** + * @file   lazyeventapi_test.cpp + * @author Nat Goodspeed + * @date   2022-06-18 + * @brief  Test for lazyeventapi. + *  + * $LicenseInfo:firstyear=2022&license=viewerlgpl$ + * Copyright (c) 2022, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "lazyeventapi.h" +// STL headers +// std headers +// external library headers +// other Linden headers +#include "../test/lltut.h" +#include "llevents.h" + +// LLEventAPI listener subclass +class MyListener: public LLEventAPI +{ +public: +    MyListener(const LL::LazyEventAPIParams& params): +        LLEventAPI(params) +    {} + +    void get(const LLSD& event) +    { +        std::cout << "MyListener::get() got " << event << std::endl; +    } +}; + +// LazyEventAPI registrar subclass +class MyRegistrar: public LL::LazyEventAPI<MyListener> +{ +    using super = LL::LazyEventAPI<MyListener>; +    using super::listener; +public: +    MyRegistrar(): +        super("Test", "This is a test LLEventAPI") +    { +        add("get", "This is a get operation", &listener::get);             +    } +}; +// Normally we'd declare a static instance of MyRegistrar -- but because we +// may want to test with and without, defer declaration to individual test +// methods. + +/***************************************************************************** +*   TUT +*****************************************************************************/ +namespace tut +{ +    struct lazyeventapi_data +    { +        ~lazyeventapi_data() +        { +            // after every test, reset LLEventPumps +            LLEventPumps::deleteSingleton(); +        } +    }; +    typedef test_group<lazyeventapi_data> lazyeventapi_group; +    typedef lazyeventapi_group::object object; +    lazyeventapi_group lazyeventapigrp("lazyeventapi"); + +    template<> template<> +    void object::test<1>() +    { +        set_test_name("LazyEventAPI"); +        // this is where the magic (should) happen +        // 'register' still a keyword until C++17 +        MyRegistrar regster; +        LLEventPumps::instance().obtain("Test").post("hey"); +    } + +    template<> template<> +    void object::test<2>() +    { +        set_test_name("No LazyEventAPI"); +        // Because the MyRegistrar declaration in test<1>() is local, because +        // it has been destroyed, we fully expect NOT to reach a MyListener +        // instance with this post. +        LLEventPumps::instance().obtain("Test").post("moot"); +    } +} // namespace tut  | 
