Client.h 7.06 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

David Thompson's avatar
David Thompson committed
25
26
27
#include "smtk/common/TypeName.h"
#include <iostream>

28
29
namespace smtk
{
30
namespace plugin
31
32
33
{
namespace detail
{
34
template<typename Registrar, typename Manager>
35
class Client;
36
37
}

38
/// plugin::Clients are static objects that live in a plugin. Their lifetime is
39
/// therefore confined to the lifetime to the plugin's own scope. Upon creation,
40
/// a Client adds a weak pointer to itself to the singleton PluginManager.
41
/// The PluginManager has an API for registering/unregistering Managers to/from
42
43
/// each of its available Clients. When a Manager is registered to a
/// Client, a Registry object is created; it tethers the manager
44
/// instance's extensions imbued by the plugin to its own lifetime. These
45
46
/// registry objects are held by the Client, guaranteeing that they go out
/// of scope when the Client goes out of scope.
47
///
48
/// A Client is a composition of detail::Clients, one for each
49
/// Manager the plugin can augment. To facilitate the registration of managers
50
/// to a Client without explicitly referring to the plugin's Registrar,
51
52
53
54
55
56
57
58
59
60
61
62
/// 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

63
64
65
66
67
template<typename Registrar, typename Manager, typename... T>
class SMTK_ALWAYS_EXPORT Client
  : public detail::Client<Registrar, Manager>
  , public detail::Client<Registrar, T>...
  , public ClientBase
68
69
{
public:
70
  static std::shared_ptr<ClientBase> create();
71
  ~Client() override = default;
72
73

private:
74
  Client() = default;
75
76
77
78
};

#else

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

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

96
template<typename Registrar>
97
class SMTK_ALWAYS_EXPORT Client<Registrar, detail::Sentinel> : public ClientBase
98
99
{
public:
100
  Client() = default;
101

102
  ~Client() override = default;
103
104
105
106
107
108
};

#endif

namespace detail
{
109
/// Clients are a composition of Registrars and Managers. When a developer
110
111
112
/// 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
113
/// it in detail::Client.
114
template<typename Manager>
115
class SMTK_ALWAYS_EXPORT ClientFor
116
117
{
public:
118
  virtual ~ClientFor();
119
120
121
122

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

123
protected:
124
  virtual smtk::plugin::RegistryBase* find(const std::shared_ptr<Manager>&) = 0;
125

126
  std::unordered_set<smtk::plugin::RegistryBase*> m_registries;
127
128
};

129
template<typename Manager>
130
ClientFor<Manager>::~ClientFor()
131
{
132
  for (auto* base_registry : m_registries)
133
134
135
136
137
  {
    delete base_registry;
  }
}

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

private:
151
  smtk::plugin::RegistryBase* find(const std::shared_ptr<Manager>&) override;
152
153
};

154
template<typename Registrar, typename Manager>
155
smtk::plugin::RegistryBase* Client<Registrar, Manager>::find(
156
157
158
159
160
161
162
  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.
163
164
  typedef smtk::plugin::Registry<Registrar, Manager> QueriedRegistry;
  for (auto base_registry : ClientFor<Manager>::m_registries)
165
166
167
168
169
170
171
172
  {
    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.
173
    if (registry != nullptr && registry->contains(manager))
174
175
176
177
178
179
180
    {
      return base_registry;
    }
  }
  return nullptr;
}

181
template<typename Registrar, typename Manager>
182
bool Client<Registrar, Manager>::registerPluginTo(const std::shared_ptr<Manager>& manager)
183
184
185
186
187
{
  // 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)
  {
188
189
    auto val = ClientFor<Manager>::m_registries.insert(
      new smtk::plugin::Registry<Registrar, Manager>(manager));
David Thompson's avatar
David Thompson committed
190
191
192
193
    std::cout
      << "Register " << smtk::common::typeName<Registrar>()
      << " to " << smtk::common::typeName<Manager>()
      << " " << manager << "\n";
194
195
196
197
198
    return val.second;
  }
  return false;
}

199
template<typename Registrar, typename Manager>
200
bool Client<Registrar, Manager>::unregisterPluginFrom(const std::shared_ptr<Manager>& manager)
201
202
203
204
205
{
  // 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))
  {
206
    ClientFor<Manager>::m_registries.erase(registry);
207
208
209
210
211
    delete registry;
    return true;
  }
  return false;
}
212
213
214
} // namespace detail
} // namespace plugin
} // namespace smtk
215
216

#endif