On Mon, 14 Dec 2020 16:38:12 +0100, Aleksander Miera wrote:

| I started writing some RAII-related code for the library types/functions
| (e.g. custom deleters), and it made me wonder: would it not be good 
| to provide some of those functionalities on library level? 

I think, probably not.  In both interface and internals, the library
shows clear signs of an effort to adopt Java styles and to copy Java
idioms as slavishly as possible.  Java doesn't have deleters, so
adding them here would not be a good fit.  The lack of them actually
points to more fundamental issues.

The library should have had a C rather than C++ interface.  Tibco EMS
is based on the JMS specification by design and intent, but wisely the
interface is in C rather than C++.  Solace, as another example, offers
a client library in C but not in C++.  Professional products eschewing
a C++ interface is quite telling.

The basic reason is strategies for resource management.  Tibco and
Solace offer opaque pointers and external functions (in C++ parlance);
so you find both create_XXX() and destroy_XXX() calls in the API.
Wrapping these in C++ classes is straightforward.  More importantly,
how these wrapper classes are instantiated - heap allocated or stack
allocated - is left to the higher level C++ code, which is as it
should be. 

To use the Activemq-cpp library sanely, we end up doing the same
thing.  A custom deleter would be only part of a more general approach
to wrapping allocation and access.  E.g.

    class ConnectionPtr
    {
        std::unique_ptr<activemq::core::ActiveMQConnection> ptr_;
    public:
        ~ConnectionPtr() noexcept(false)
        { try { ptr_->close(); } catch (cms::CMSException&) {} }
        explicit
        ConnectionPtr(std::string const& brokerURI)

:ptr_(activemq::core::ActiveMQConnectionFactory(brokerURI).createConnection())
        {}
        activemq::core::ActiveMQConnection* operator->()
        { return ptr_.get(); }
    };
 
Then, use a ConnectionPtr as an argument to the constructor of a
SessionPtr defined along similar lines:

    class SessionPtr
    {
        std::unique_ptr<activemq::core::ActiveMQSession>  ptr_;
    public:
        ~SessionPtr() noexcept(false)
        { try { ptr_->close(); } catch (cms::CMSException&) {} }
        explicit
        SessionPtr(ConnectionPtr& cp,
                   cms::Session::AcknowledgeMode ackMode
                   = cms::Session::AUTO_ACKNOWLEDGE)
        : ptr_(cp->createSession( ackMode ))
        {}
        cms::Session* operator->() { return ptr_.get(); }
    };

[SessionPtr can be used similarly for a ProducerPtr, and so on.]

The Java-centric alternative of using explicit creational methods (in
contrast to dependency injection into constructors, as above) creates
needless difficulties, as you have to pre-commit to heap allocation,
willy nilly.  Instead, the idea should be to leave those creational
methods to the C layer and build the C++ layer on top of that, with
freedom to allocate on heap or stack as warranted.

Note that this involves treating the Activemq-cpp class pointers like
C layer opaque pointers as much as possible.

| Last but not least, auto_ptr deprecation warnings look just ugly on
| in my build log and maybe at least moving away from those should do 
| no harm.

C++17 removed std::auto_ptr altogether: the codebase shouldn't even
compile.  Replacing std::auto_ptr with std::unique_ptr is relatively
straightforward, as it happens. See, on this list,

http://activemq.2283324.n4.nabble.com/Replacing-std-auto-ptr-in-ActiveMQ-CPP-v-3-9-5-tp4759225.html

| If this is totally undesired and "backwards compatibility is king" 
| feel free to turn me down.

I get the impression that the Activemq-cpp library is transitioning
into maintenance mode.  There doesn't seem to be much interest.

--
:ar

Reply via email to