Client.h 6.94 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
//=========================================================================
//  Copyright (c) Kitware, Inc.
//  All rights reserved.
//  See LICENSE.txt for details.
//
//  This software is distributed WITHOUT ANY WARRANTY; without even
//  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
//  PURPOSE.  See the above copyright notice for more information.
//=========================================================================

11
12
#ifndef __smtk_plugin_Client_h
#define __smtk_plugin_Client_h
13

14
15
#include "smtk/plugin/ClientBase.h"
#include "smtk/plugin/Registry.h"
16
17
18
19
20

#include <functional>
#include <memory>
#include <unordered_set>

21
22
23
24
#ifdef SMTK_MSVC
#include "smtk/plugin/Sentinel.h"
#endif

25
26
namespace smtk
{
27
namespace plugin
28
29
30
{
namespace detail
{
31
template<typename Registrar, typename Manager>
32
class Client;
33
34
}

35
/// plugin::Clients are static objects that live in a plugin. Their lifetime is
36
/// therefore confined to the lifetime to the plugin's own scope. Upon creation,
37
/// a Client adds a weak pointer to itself to the singleton PluginManager.
38
/// The PluginManager has an API for registering/unregistering Managers to/from
39
40
/// each of its available Clients. When a Manager is registered to a
/// Client, a Registry object is created; it tethers the manager
41
/// instance's extensions imbued by the plugin to its own lifetime. These
42
43
/// registry objects are held by the Client, guaranteeing that they go out
/// of scope when the Client goes out of scope.
44
///
45
/// A Client is a composition of detail::Clients, one for each
46
/// Manager the plugin can augment. To facilitate the registration of managers
47
/// to a Client without explicitly referring to the plugin's Registrar,
48
49
50
51
52
53
54
55
56
57
58
59
/// the Manager-specific API is separated from the implementation that uses
/// the Registrar to perform the actual registration.
///
/// Both gcc and clang are savvy to template class descriptions using parameter
/// packs. MSVC isn't quite there yet. The following code is bifurcated
/// according to compiler type because, even though the two code paths produce
/// functionally equivalent code, the first implementation does so with much
/// less compile-time (and library size) overhead. Eventually, only the first
/// implementation should be used.

#ifndef SMTK_MSVC

60
61
62
63
64
template<typename Registrar, typename Manager, typename... T>
class SMTK_ALWAYS_EXPORT Client
  : public detail::Client<Registrar, Manager>
  , public detail::Client<Registrar, T>...
  , public ClientBase
65
66
{
public:
67
  static std::shared_ptr<ClientBase> create();
68
  ~Client() override {}
69
70

private:
71
72
73
74
  Client()
    : detail::Client<Registrar, Manager>()
    , detail::Client<Registrar, T>()...
    , ClientBase()
75
76
77
78
79
80
  {
  }
};

#else

81
82
83
84
template<typename Registrar, typename Manager = detail::Sentinel, typename... T>
class SMTK_ALWAYS_EXPORT Client
  : public detail::Client<Registrar, Manager>
  , public Client<Registrar, T...>
85
86
{
public:
87
  static std::shared_ptr<ClientBase> create();
88
  ~Client() override {}
89
90

protected:
91
92
93
  Client()
    : detail::Client<Registrar, Manager>()
    , Client<Registrar, T...>()
94
95
96
97
  {
  }
};

98
template<typename Registrar>
99
class SMTK_ALWAYS_EXPORT Client<Registrar, detail::Sentinel> : public ClientBase
100
101
{
public:
102
103
  Client()
    : ClientBase()
104
105
106
  {
  }

107
  ~Client() override {}
108
109
110
111
112
113
};

#endif

namespace detail
{
114
/// Clients are a composition of Registrars and Managers. When a developer
115
116
117
/// wishes to register a manager to all available plugins, the Registrar
/// for each plugin is not known but the Manager type is. We therefore present
/// an API that only depends on the Manager type to the user, and we implement
118
/// it in detail::Client.
119
template<typename Manager>
120
class SMTK_ALWAYS_EXPORT ClientFor
121
122
{
public:
123
  virtual ~ClientFor();
124
125
126
127

  virtual bool registerPluginTo(const std::shared_ptr<Manager>&) = 0;
  virtual bool unregisterPluginFrom(const std::shared_ptr<Manager>&) = 0;

128
protected:
129
  virtual smtk::plugin::RegistryBase* find(const std::shared_ptr<Manager>&) = 0;
130

131
  std::unordered_set<smtk::plugin::RegistryBase*> m_registries;
132
133
};

134
template<typename Manager>
135
ClientFor<Manager>::~ClientFor()
136
137
138
139
140
141
142
{
  for (auto base_registry : m_registries)
  {
    delete base_registry;
  }
}

143
144
/// The "public" Client is simply a composition of detail::Clients,
/// one for each Manager type. The detail::Client constructs a Registry
145
146
/// object for its Registrar/Manager pair and stores it in its set of
/// Registries. The lifetime of these Registries are therefore tethered to the
147
/// lifetime of the Client, which lives in the plugin's library.
148
template<typename Registrar, typename Manager>
149
class SMTK_ALWAYS_EXPORT Client : public ClientFor<Manager>
150
151
152
153
154
155
{
public:
  bool registerPluginTo(const std::shared_ptr<Manager>&) override;
  bool unregisterPluginFrom(const std::shared_ptr<Manager>&) override;

private:
156
  smtk::plugin::RegistryBase* find(const std::shared_ptr<Manager>&) override;
157
158
};

159
template<typename Registrar, typename Manager>
160
smtk::plugin::RegistryBase* Client<Registrar, Manager>::find(
161
162
163
164
165
166
167
  const std::shared_ptr<Manager>& manager)
{
  // We are looking for a registry that links the Registrar and Manager. If one
  // exists, it may be the same Registry object that is used to link the
  // Registrar and another Manager. That's ok, because Registries are in turn
  // compositions of Registrar/Manager pairs, so a dynamic_cast to a Registry
  // that only contains the queried Manager will still succeed.
168
169
  typedef smtk::plugin::Registry<Registrar, Manager> QueriedRegistry;
  for (auto base_registry : ClientFor<Manager>::m_registries)
170
171
172
173
174
175
176
177
  {
    QueriedRegistry* registry = dynamic_cast<QueriedRegistry*>(base_registry);

    // To prevent cryptic compilation errors about a Registry object that is
    // linked to a Manager it doesn't support, Registries have the ability to
    // take as a template parameter a Manager that they do not support. Because
    // of this, we need to check if the accessed registry also supports the
    // Manager under query.
178
    if (registry != nullptr && registry->contains(manager))
179
180
181
182
183
184
185
    {
      return base_registry;
    }
  }
  return nullptr;
}

186
template<typename Registrar, typename Manager>
187
bool Client<Registrar, Manager>::registerPluginTo(const std::shared_ptr<Manager>& manager)
188
189
190
191
192
{
  // We search to ensure that a Registry object does not already exist for this
  // manager. That way, Registrars only register themselves to a manager once.
  if (this->find(manager) == nullptr)
  {
193
194
    auto val = ClientFor<Manager>::m_registries.insert(
      new smtk::plugin::Registry<Registrar, Manager>(manager));
195
196
197
198
199
    return val.second;
  }
  return false;
}

200
template<typename Registrar, typename Manager>
201
bool Client<Registrar, Manager>::unregisterPluginFrom(const std::shared_ptr<Manager>& manager)
202
203
204
205
206
{
  // We search for the Registry object that connects to this manager. If one
  // exists, we break the connection by deleting the registry.
  if (auto registry = this->find(manager))
  {
207
    ClientFor<Manager>::m_registries.erase(registry);
208
209
210
211
212
    delete registry;
    return true;
  }
  return false;
}
213
214
215
} // namespace detail
} // namespace plugin
} // namespace smtk
216
217

#endif