MyStack
is a private member of class A, then all users of the class must also import the header MyStack.h. If this variable is used by only one function and does not contain a state, then it must be made generally static. In addition, you should not forget that automatic variables live to the end of the block and use this to destroy more unnecessary variables by adding brackets to the block of code. #ifndef PIMPL_H #define PIMPL_H ///idea from GotW #101: Compilation Firewalls, Part 2s http://herbsutter.com/gotw/_101/ #include <boost/scoped_ptr.hpp> template<typename T> class PImpl { private: boost::scoped_ptr<T> m; public: PImpl() : m(new T) { } template<typename A1> PImpl(A1& a1) : m(new T(a1)) { } // 2 9 …. template<typename A1, typename A2, typename A3, typename A4, typename A5, typename A6 , typename A7, typename A8, typename A9, typename A10> PImpl(A1& a1, A2& a2, A3& a3 , A4& a4, A5& a5, A6& a6, A7& a7, A8& a8, A9& a9, A10& a10) : m(new T(a1, a2, a3, a4, a5 , a6, a7, a8, a9, a10)) { } PImpl(const PImpl& orig) : m(new T(*orig)) { } T* operator->() const { return m.get(); } T& operator*() const { return *m.get(); } PImpl& operator=(const PImpl& orig) { m.reset(new T(*orig)); return *this; } }; #endif /* PIMPL_H */
class Impl; PImpl<Impl> me;
me
borrowed from VBAthis
is passed to the Impl
constructor Impl
first parameter and is stored in the ppub
fieldstruct
since it has scope only in the current module.operator=
don't forget to set me
and ppub
.PImpl
, then we can very simply replace the current implementation with a stub using the linker. Testing the same hidden implementation is possible by including the implementation code in the test module using the #include directive. ------- Hasher.h ------ #include <PImpl.h> class Hasher { class Impl; // class struct PImpl<Impl> me; // public: Hasher(); void execute(); int getResults(); }; ------- Hasher.cpp ------ #include “Hasher.h” #include <HashLib.h> #include “SecTokens.h” // . struct struct Hasher::Impl { Hasher* ppub; // HashContext cnt; int hash; Impl(Hasher* ppub): ppub(ppub) { } void prepare() { HashLib::createContext(cnt); hash = 0; } void update(int val) { HashLib::updateHash(cnt, hash, val); } void finalize() { HashLib::releaseContext(cnt); } }; Hasher::Hasher(): me(this) { // } void Hasher::execute() { me->prepare(); me->update(SecTokens::one); me->update(SecTokens::two); me->finalize(); } int Hasher::getResults(){ return me->hash; } ------- Cryptor.h ------ #include <string> #include <PImpl.h> class Cryptor { class Impl; PImpl<Impl> me; public: Cryptor(std::string salt); std::string crypt(std::string plain); }; ------- Cryptor.cpp ------ #include <CryptoLib.h> #include “Cryptor.h” struct Cryptor::Impl { std::string salt; CryptoContext cnt; Impl(std::string salt): me(salt) { } void prepare() { CryptoLib::createContext(cnt); } void update(std::string plain) { CryptoLib::updateHash(cnt, plain); } std::string finalize() { return CryptoLib::releaseContext(cnt); } }; Cryptor::Cryptor(std::string salt): me(salt) { } std::string Cryptor::crypt(std::string plain) { me->prepare(); me->update(plain); return me->finalize(); } ------- MockHasher.cpp ------ #include “Hasher.h” struct Hasher::Impl { }; void Hasher::execute() { } int Hasher::getResults(){ return 4; } ------- TestCryptor.cpp ------ #include “Cryptor.cpp” int main(int argc, char** argv) { Cryptor::Impl impl(“salt”); impl.prepare(); // impl prepare impl.update(“text”); // impl update std::string crypto=impl.finalize(); // crypto }
Cryptor
(a wrapper for a certain CryptoLib
) for which you need to write a test and a class Hasher
(a wrapper for a certain HashLib
) on which Cryptor
depends. but Cryptor
still depends on the HashLib
and SecTokens
, and this is absolutely not necessary for us to test Cryptor
. Instead, prepare MockHasher.cpp.Platform
. It turns out the module with functions unrelated to each other, which I simply declared in the same namespace platform
. In the future, I am going to replace the module with implementation depending on the platforms. But the trouble is. One of the functions should fill in the <key, value> class pairs (this is std::map
, but with a specific comparator) declared generally in the private part of another public class Settings
.std::map
. I am not in favor of producing header files, except that changing the scope of the template comparator from a private to a more general one will result in an increase in component connectivity. Any change in it will recompile everything that depends on the platform-specific placeholder.boost::bind
and callback functions. The placeholder function will take a pointer to the function. void fillDefaults(boost::function<void(std::string, std::string) > setDefault);
void fillDefaults(std::map<std::string, std::string, ci_less>& defaults);
Settings
: void setDefault(std::string key, std::string value) { defaults[key] = value; } void fillDefaults() { platform::fillDefaults(boost::bind(&SettingsManager::Impl::setDefault, this, _1, _2)); }
void fillDefaults() { platform::fillDefaults(defaults); }
void Hasher::execute() { me->prepare(); me->update(SecTokens::one); me->update(SecTokens::two); me->finalize(); }
void Hasher::Impl::execute() { prepare(); update(SecTokens::one); update(SecTokens::two); finalize(); } void Hasher::execute() { me->execute(); }
------- Hasher.h ------ #include <boost/functions.hpp> #include <PImpl.h> class Hasher { class Impl; // class struct PImpl<Impl> me; // public: Hasher(); boost::function<void()> execute; int getResults(); }; ------- Hasher.cpp ------ //……... Hasher::Hasher(): me(this), execute(boost::bind(&Hasher::Impl::execute, &*me)) { } int Hasher::getResults(){ return me->hash; }
void f(Hasher& h) { h.execute(); }
void f(Hasher& h, boost::asio::io_service& executor) { executor.post(h.execute); }
void f(Hasher& h, boost::asio::io_service& executor) { executor.post(boost::bind(&Hasher::execute, &h)); }
execute
now a public class field and a new value can be randomly assigned to it during execution, which cannot happen to the function. Also, the usual redefinition of the virtual method is no longer available, although this problem is solved simply. void myFunction(int, int); int main(int argc, char** argv) { boost::function<void(int, int)> functor1(boost::bind(myFunction, _1, _2)); boost::function<void(int)> functor2(boost::bind(functor1, 4, _1)); }
int main(int argc, char** argv) { Bindable<void(int, int)> functor1(boost::bind(myFunction, _1, _2)); Bindable<void(int)> functor2(functor1.bind(4, _1)); }
#ifndef BINDABLE_H #define BINDABLE_H #include <boost/bind.hpp> #include <boost/function.hpp> template<typename Signature> struct Bindable : public boost::function<Signature> { Bindable() { } template<typename T> Bindable(const T& fn) : boost::function<Signature>(fn) { } template<typename NewSignature, typename A1> Bindable<NewSignature> bind(const A1& a1) { return boost::bind(this, a1); } // 2 9 template<typename NewSignature, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10> Bindable<NewSignature> bind(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9, const A10& a10) { return boost::bind(*this, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); } }; #endif /* BINDABLE_H */
globalStorage.getProfiles().getProfile(“Default”)
. In order not to repeat such an expression, an object or a link to it is also best stored in the class field. globalStorage.getHoster()->invoke();
Profile pr=globalStorage.getProfiles()->getProfile(“Default”);
Platform
discussed above, all who use globalStorage
forced to know that the GlobalStorage
interface is GlobalStorage
with all external types. But GlobalStorage
should really return an object of a given type (or implementing a specified interface) and there is no way to solve the problem as in Platform
. #include "InjectPtr.h" ///Helper interface class. Only for visual marking of needed methods. ///We can't do virtual template members namespace ioc { ///methods like http://tapestry.apache.org/defining-tapestry-ioc-services.html#DefiningTapestryIOCServices-ServiceBuilderMethods ///Like public @InjectService or @Inject annotation ///ServiceId Case http://tapestry.apache.org/defining-tapestry-ioc-services.html#DefiningTapestryIOCServices-ServiceIds template<typename T, size_t ID> InjectPtr<T> resolve(); ///Singleton or factory case template<typename T> InjectPtr<T> resolve(); };
boost::shared_ptr<Hoster> hoster = globalStorage.getHoster();
InjectPtr<Hoster> hoster = ioc::resolve<Hoster>();
Hoster
in the code, you should take care of importing its header yourself. The second parameter of the resolve
method template is the service identifier. It is used in case there are several services with one interface.InjectPtr
is a smart pointer to an object with delayed (lazy) initialization. Inside stores boost::shared_ptr
on boost::shared_ptr
on a stored object. The latter is initialized during the first dereference of InjectPtr
. To create an instance of a stored object, InjectPtr
gets a functor factory. #ifndef INJECT_PTR_H #define INJECT_PTR_H #include <cassert> #include <cstddef> #include <boost/shared_ptr.hpp> #include <boost/scoped_ptr.hpp> #include <boost/make_shared.hpp> #include <boost/function.hpp> #include <boost/thread/mutex.hpp> ///Pointer to lazy instantiative object template<typename T> class InjectPtr { private: typedef boost::function<T*() > Factory; boost::shared_ptr< boost::shared_ptr<T> > px; boost::shared_ptr< boost::scoped_ptr<boost::mutex> > instantiateMutex; Factory factory; public: ///Main constructor. Take factory for future instantiate object InjectPtr(Factory factory) : px(boost::make_shared<boost::shared_ptr<T> >()) , instantiateMutex(boost::make_shared<boost::scoped_ptr<boost::mutex> >(new boost::mutex)) , factory(factory) { } InjectPtr() : px(boost::make_shared<boost::shared_ptr<T> >()) , instantiateMutex(boost::make_shared<boost::scoped_ptr<boost::mutex> >()) { } InjectPtr(boost::shared_ptr<T> pObject) : px(boost::make_shared<boost::shared_ptr<T> >(pObject)) { assert(*px != 0); } InjectPtr(InjectPtr const &orig) : px(orig.px) , instantiateMutex(orig.instantiateMutex) , factory(orig.factory) { } InjectPtr & operator=(InjectPtr const & orig) { px = orig.px; instantiateMutex = orig.instantiateMutex; factory = orig.factory; return *this; } virtual ~InjectPtr() { } T & operator*() { instantiate(); return **px; } T * operator->() { instantiate(); return &**px; } bool operator!() const { return !*px; } void operator==(InjectPtr const& that) const { return *px == that->px; } void operator!=(InjectPtr const& that) const { return *px != that->px; } boost::shared_ptr<T> sharedPtr() { instantiate(); return *px; } void instantiate() { if (!*px && factory) { { boost::mutex::scoped_lock lock(**instantiateMutex); if (!*px) { px->reset(factory()); } } instantiateMutex->reset(); } } Factory getFactory() const { return factory; } void setFactory(Factory factory) { if(!*px && !this->factory){ if(!*instantiateMutex) instantiateMutex->reset(new boost::mutex); this->factory = factory; } } }; template<class T, class U> InjectPtr<T> static_pointer_cast(InjectPtr<U> r) { return InjectPtr<T>(boost::static_pointer_cast<T>(r.sharedPtr())); } #endif /* INJECT_PTR_H */
InjectPtr
thread safe. During the creation of the object, the operation is blocked by the mutex.ioc::resolve
method ioc::resolve
------- IOCModule.h ------ // #ifndef IOCMODULE_H #define IOCMODULE_H #include <boost/functional/factory.hpp> #include <boost/bind.hpp> #include <IOC.h> #endif /* IOCMODULE_H */ ------- IOCModule.cpp ------ #include "Hoster.h" #include "SomeService.h" #include "InjectPtr.h" #include <IOCModule.h> #include <IOC.h> //Module like http://tapestry.apache.org/tapestry-ioc-modules.html //Now only for: - To provide explicit code for building a service using namespace ioc; ///methods like http://tapestry.apache.org/defining-tapestry-ioc-services.html#DefiningTapestryIOCServices-ServiceBuilderMethods template<> InjectPtr<SomeService> resolve<SomeService>() { static InjectPtr<Hoster> result(boost::bind(boost::factory<SomeService*>())); return result; } ///Hoster takes SomeService in constructor template<> InjectPtr<Hoster> resolve<Hoster>() { static InjectPtr<Hoster> result(boost::bind(boost::factory<Hoster*>(), resolve<SomeService>())); return result; }
InjectPtr
keeper into a global static variable, which is probably initialized before the program code was launched. You can, of course, in separate variables, but then you have to invent a name for each. Here CoreStorage
is the keeper for the Core IOC module: #include "Hoster.h" #include "SomeService.h" #include "InjectPtr.h" #include <IOCModule.h> #include <IOC.h> //Module like http://tapestry.apache.org/tapestry-ioc-modules.html //Now only for: - To provide explicit code for building a service using namespace ioc; struct CoreStorage { InjectPtr<SomeService> someService; InjectPtr<Hoster> hoster; }; static CoreStorage storage; ///methods like http://tapestry.apache.org/defining-tapestry-ioc-services.html#DefiningTapestryIOCServices-ServiceBuilderMethods template<> InjectPtr<SomeService> resolve<SomeService>() { if(!storage.someService.getFactory()) { storage.someService.setFactory(boost::bind(boost::factory<SomeService*>())); } return storage.someService; } ///Hoster takes SomeService in constructor template<> InjectPtr<Hoster> resolve<Hoster>() { if(!storage.hoster.getFactory()) { storage.hoster.setFactory(boost::bind(boost::factory<Hoster*>(), resolve<SomeService>())); } return storage.hoster; }
Source: https://habr.com/ru/post/226333/
All Articles