Skip to content

Commit 9610f1f

Browse files
Functionality to retrieve Service Reference from Service (CppMicroServices#1044) (CppMicroServices#1157)
--------- Co-authored-by: tcormackMW <113473781+tcormackMW@users.noreply.github.com> Co-authored-by: Toby Cormack <tcormack@mathworks.com>
1 parent 1d7e2a9 commit 9610f1f

8 files changed

Lines changed: 269 additions & 112 deletions

File tree

compendium/DeclarativeServices/test/gtest/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ set(_declarativeservices_tests
9292
TestFixture.hpp
9393
TestServiceComponentRuntime.cpp
9494
TestServiceProvider.cpp
95+
TestServiceReferenceRetrieval.cpp
9596
TestSharedLibraryException.cpp
9697
TestUpdateConfiguration.cpp
9798
ImportTestBundles.cpp
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*=============================================================================
2+
3+
Library: CppMicroServices
4+
5+
Copyright (c) The CppMicroServices developers. See the COPYRIGHT
6+
file at the top-level directory of this distribution and at
7+
https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT .
8+
9+
Licensed under the Apache License, Version 2.0 (the "License");
10+
you may not use this file except in compliance with the License.
11+
You may obtain a copy of the License at
12+
13+
http://www.apache.org/licenses/LICENSE-2.0
14+
15+
Unless required by applicable law or agreed to in writing, software
16+
distributed under the License is distributed on an "AS IS" BASIS,
17+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18+
See the License for the specific language governing permissions and
19+
limitations under the License.
20+
21+
=============================================================================*/
22+
23+
#include "TestFixture.hpp"
24+
#include "cppmicroservices/Constants.h"
25+
#include "cppmicroservices/ServiceObjects.h"
26+
#include "cppmicroservices/ServiceReference.h"
27+
#include "gtest/gtest.h"
28+
29+
#include "TestInterfaces/Interfaces.hpp"
30+
31+
namespace test
32+
{
33+
struct int1
34+
{
35+
[[nodiscard]] virtual int getValue() const = 0;
36+
virtual ~int1() = default;
37+
int1(int1 const&) = default;
38+
int1& operator=(int1 const&) = default;
39+
int1(int1&&) noexcept = default;
40+
int1& operator=(int1&&) noexcept = default;
41+
42+
protected:
43+
int1() = default;
44+
};
45+
46+
struct TestServiceA : public int1
47+
{
48+
TestServiceA(int val) : id(val) {}
49+
[[nodiscard]] int
50+
getValue() const override
51+
{
52+
return id;
53+
}
54+
55+
private:
56+
int id;
57+
};
58+
/*
59+
* Tests that a user is able to retrieve a serviceReference from a serviceObject from the coreFramework or DS
60+
*/
61+
TEST_F(tServiceComponent, testServiceReferenceRetrievalForDSAndManReg)
62+
{
63+
auto const id1 = 100;
64+
auto const id2 = 85;
65+
auto reg = context.RegisterService<int1>(std::make_shared<TestServiceA>(id1));
66+
auto sref = reg.GetReference();
67+
auto sref2 = context.RegisterService<int1>(std::make_shared<TestServiceA>(id2)).GetReference();
68+
69+
auto service1 = context.GetService(sref);
70+
ASSERT_EQ(service1->getValue(), id1);
71+
auto service2 = context.GetService(sref2);
72+
ASSERT_EQ(service2->getValue(), id2);
73+
74+
auto srefFromService1 = cppmicroservices::ServiceReferenceFromService(service1);
75+
76+
ASSERT_EQ(srefFromService1, sref);
77+
ASSERT_FALSE(cppmicroservices::ServiceReferenceFromService(service1) == sref2);
78+
ASSERT_EQ(cppmicroservices::ServiceReferenceFromService(service2), sref2);
79+
80+
// Install and start bundle
81+
auto testBundle = ::test::InstallAndStartBundle(context, "TestBundleDSCA05");
82+
ASSERT_TRUE(testBundle);
83+
84+
auto dsRef = context.GetServiceReference<test::CAInterface>();
85+
ASSERT_TRUE(dsRef);
86+
auto dsSvc = context.GetService<test::CAInterface>(dsRef);
87+
auto retSRef = cppmicroservices::ServiceReferenceFromService(dsSvc);
88+
ASSERT_EQ(retSRef, dsRef);
89+
ASSERT_EQ(cppmicroservices::any_cast<std::string>(retSRef.GetProperty("ManifestProp")), "abc");
90+
91+
ASSERT_THROW(cppmicroservices::ServiceReferenceFromService(std::make_shared<TestServiceA>(id1)),
92+
std::runtime_error);
93+
94+
reg.Unregister();
95+
96+
// ensure only 1 sr exists in framework for int1
97+
ASSERT_EQ(context.GetServiceReferences<int1>().size(), 1);
98+
// assert that the serviceReference returned for my service object is not valid after unregistration
99+
ASSERT_FALSE(cppmicroservices::ServiceReferenceFromService(service1));
100+
101+
// assert identical behavior from ref from original registration and ref from serviceReferenceFromService
102+
ASSERT_FALSE(sref);
103+
ASSERT_FALSE(srefFromService1);
104+
ASSERT_EQ(sref, srefFromService1);
105+
}
106+
} // namespace test

framework/include/cppmicroservices/ServiceReference.h

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ namespace cppmicroservices
8686

8787
ServiceReference(ServiceReferenceBase const& base) : ServiceReferenceBase(base)
8888
{
89-
const std::string interfaceId(us_service_interface_iid<S>());
89+
std::string const interfaceId(us_service_interface_iid<S>());
9090
if (GetInterfaceId() != interfaceId)
9191
{
9292
if (this->IsConvertibleTo(interfaceId))
@@ -149,6 +149,31 @@ namespace cppmicroservices
149149
* interface identifier.
150150
*/
151151
using ServiceReferenceU = ServiceReference<void>;
152+
153+
/**
154+
* \ingroup MicroServices
155+
* \ingroup gr_servicereference
156+
*
157+
* A method to retrieve a <code>ServiceObject</code>'s original <code>ServiceReference<void></code>
158+
*
159+
*/
160+
US_Framework_EXPORT ServiceReferenceU ServiceReferenceFromService(std::shared_ptr<void> const& s);
161+
162+
/**
163+
* \ingroup MicroServices
164+
* \ingroup gr_servicereference
165+
*
166+
* A method to retrieve a <code>ServiceObject<T></code>'s original <code>ServiceReference<U></code>
167+
*
168+
* @tparam T The class type of the <code>ServiceObject</code>
169+
* @tparam U The class type of the <code>ServiceReference</code>. Defaults to <code>T</code> if not specified.
170+
*/
171+
template <typename T, typename U = T>
172+
ServiceReference<U>
173+
ServiceReferenceFromService(std::shared_ptr<T> const& s)
174+
{
175+
return ServiceReference<U>(ServiceReferenceFromService(std::static_pointer_cast<void>(s)));
176+
}
152177
} // namespace cppmicroservices
153178

154179
namespace std

framework/include/cppmicroservices/ServiceReferenceBase.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,6 @@ namespace cppmicroservices
209209
friend class BundleHooks;
210210
friend class ServiceHooks;
211211
friend class ServiceObjectsBase;
212-
friend struct UngetHelper;
213212
friend class ServiceObjectsBasePrivate;
214213
friend class ServiceRegistrationBase;
215214
friend class ServiceRegistrationBasePrivate;

framework/src/bundle/BundleContext.cpp

Lines changed: 3 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -234,52 +234,6 @@ namespace cppmicroservices
234234
return b->coreCtx->services.Get(b.get(), clazz);
235235
}
236236

237-
/* @brief Private helper struct used to facilitate the shared_ptr aliasing constructor
238-
* in BundleContext::GetService method. The aliasing constructor helps automate
239-
* the call to UngetService method.
240-
*
241-
* Service consumers can simply call GetService to obtain a shared_ptr to the
242-
* service object and not worry about calling UngetService when they are done.
243-
* The UngetService is called when all instances of the returned shared_ptr object
244-
* go out of scope.
245-
*/
246-
template <class S>
247-
struct ServiceHolder
248-
{
249-
std::weak_ptr<BundlePrivate> const b;
250-
ServiceReferenceBase const sref;
251-
std::shared_ptr<S> const service;
252-
253-
ServiceHolder(std::shared_ptr<BundlePrivate> const& b, ServiceReferenceBase const& sr, std::shared_ptr<S> s)
254-
: b(b)
255-
, sref(sr)
256-
, service(std::move(s))
257-
{
258-
}
259-
260-
~ServiceHolder()
261-
{
262-
try
263-
{
264-
sref.d.Load()->UngetService(b.lock(), true);
265-
}
266-
catch (...)
267-
{
268-
// Make sure that we don't crash if the shared_ptr service object outlives
269-
// the BundlePrivate or CoreBundleContext objects.
270-
if (!b.expired())
271-
{
272-
DIAG_LOG(*b.lock()->coreCtx->sink)
273-
<< "UngetService threw an exception. " << util::GetLastExceptionStr();
274-
}
275-
// don't throw exceptions from the destructor. For an explanation, see:
276-
// https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md
277-
// Following this rule means that a FrameworkEvent isn't an option here
278-
// since it contains an exception object which clients could throw.
279-
}
280-
}
281-
};
282-
283237
std::shared_ptr<void>
284238
BundleContext::GetService(ServiceReferenceBase const& reference)
285239
{
@@ -297,8 +251,8 @@ namespace cppmicroservices
297251
d->CheckValid();
298252
auto b = GetAndCheckBundlePrivate(d);
299253

300-
std::shared_ptr<ServiceHolder<void>> h(
301-
new ServiceHolder<void>(b, reference, reference.d.Load()->GetService(b.get())));
254+
auto serviceHolder = new ServiceHolder<void>(b, reference, reference.d.Load()->GetService(b.get()), nullptr);
255+
std::shared_ptr<ServiceHolder<void>> h(serviceHolder, CustomServiceDeleter { serviceHolder });
302256
return std::shared_ptr<void>(h, h->service.get());
303257
}
304258

@@ -321,7 +275,7 @@ namespace cppmicroservices
321275

322276
auto serviceInterfaceMap = reference.d.Load()->GetServiceInterfaceMap(b.get());
323277
std::shared_ptr<ServiceHolder<InterfaceMap const>> h(
324-
new ServiceHolder<InterfaceMap const>(b, reference, serviceInterfaceMap));
278+
new ServiceHolder<InterfaceMap const>(b, reference, serviceInterfaceMap, nullptr));
325279
return InterfaceMapConstPtr(h, h->service.get());
326280
}
327281

framework/src/service/ServiceObjects.cpp

Lines changed: 3 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -82,64 +82,6 @@ namespace cppmicroservices
8282
}
8383
}
8484

85-
/* @brief Private helper struct used to facilitate the shared_ptr aliasing constructor
86-
* in ServiceObjectsBase::GetService & ServiceObjectsBase::GetServiceInterfaceMap
87-
* methods. The aliasing constructor helps automate the call to UngetService method.
88-
*
89-
* Service consumers can simply call GetService to obtain a shared_ptr to the
90-
* service object and not worry about calling UngetService when they are done.
91-
* The UngetService is called when all instances of the returned shared_ptr object
92-
* go out of scope.
93-
*/
94-
struct UngetHelper
95-
{
96-
const InterfaceMapConstPtr interfaceMap;
97-
const ServiceReferenceBase sref;
98-
const std::weak_ptr<BundlePrivate> b;
99-
100-
UngetHelper(InterfaceMapConstPtr im, ServiceReferenceBase const& sr, std::shared_ptr<BundlePrivate> const& b)
101-
: interfaceMap(std::move(im))
102-
, sref(sr)
103-
, b(b)
104-
{
105-
}
106-
~UngetHelper()
107-
{
108-
try
109-
{
110-
auto bundle = b.lock();
111-
if (sref)
112-
{
113-
bool isPrototypeScope
114-
= sref.GetProperty(Constants::SERVICE_SCOPE).ToString() == Constants::SCOPE_PROTOTYPE;
115-
116-
if (isPrototypeScope)
117-
{
118-
sref.d.Load()->UngetPrototypeService(bundle, interfaceMap);
119-
}
120-
else
121-
{
122-
sref.d.Load()->UngetService(bundle, true);
123-
}
124-
}
125-
}
126-
catch (...)
127-
{
128-
// Make sure that we don't crash if the shared_ptr service object outlives
129-
// the BundlePrivate or CoreBundleContext objects.
130-
if (!b.expired())
131-
{
132-
DIAG_LOG(*b.lock()->coreCtx->sink)
133-
<< "UngetHelper threw an exception. " << util::GetLastExceptionStr();
134-
}
135-
// don't throw exceptions from the destructor. For an explanation, see:
136-
// https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md
137-
// Following this rule means that a FrameworkEvent isn't an option here
138-
// since it contains an exception object which clients could throw.
139-
}
140-
}
141-
};
142-
14385
std::shared_ptr<void>
14486
ServiceObjectsBase::GetService() const
14587
{
@@ -162,7 +104,7 @@ namespace cppmicroservices
162104
return nullptr;
163105
}
164106

165-
auto h = std::make_shared<UngetHelper>(interfaceMap, d->m_reference, bundle_);
107+
auto h = std::make_shared<ServiceHolder<void>>(bundle_, d->m_reference, nullptr, interfaceMap);
166108
auto deleter = h->interfaceMap->find(d->m_reference.GetInterfaceId())->second.get();
167109
return std::shared_ptr<void>(h, deleter);
168110
}
@@ -190,8 +132,8 @@ namespace cppmicroservices
190132
{
191133
return nullptr;
192134
}
193-
194-
std::shared_ptr<UngetHelper> h(new UngetHelper { result, d->m_reference, bundle_ });
135+
auto sh = new ServiceHolder<void> { bundle_, d->m_reference, nullptr, result };
136+
std::shared_ptr<ServiceHolder<void>> h(sh, CustomServiceDeleter { sh });
195137
return InterfaceMapConstPtr(h, h->interfaceMap.get());
196138
}
197139

framework/src/service/ServiceReferenceBasePrivate.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,4 +482,15 @@ namespace cppmicroservices
482482
}
483483
return false;
484484
}
485+
486+
ServiceReferenceU
487+
ServiceReferenceFromService(std::shared_ptr<void> const& s)
488+
{
489+
auto deleter = std::get_deleter<CustomServiceDeleter>(s);
490+
if (!deleter)
491+
{
492+
throw std::runtime_error("The input is not a CppMicroServices managed ServiceObject");
493+
}
494+
return deleter->getServiceRef();
495+
}
485496
} // namespace cppmicroservices

0 commit comments

Comments
 (0)