views:

2335

answers:

3

Are there any bridges to make mixing Qt with STL and Boost as seamless and easy as possible?

This is a followup to Mixing Qt and Boost, where no specific answers how to accomplish this were given.

+2  A: 

What exactly is the problem?
You can ignore all the Qt collection classes if you want and use STL equivalents.
Similarly you can use Boost's cross platform file/network libs.

The main reason to use Qt's own is probably that boost isn't necessarily that widely available, especially on mobile devices. Some of Boost's libs are a little more complicated to use than the Qt ones for simple tasks.

Martin Beckett
@mgb This is not a question about which one to choose and why but about what to do when using both at the same time. See TimW's answer which is roughly what I was hoping for.
Piotr Dobrogost
OK I see, you might want to edit the question to rephrase it.
Martin Beckett
+1  A: 

In general you are going to fare better when using QT if you stick to the QT Collection classes rather than us STL. There is nothing per se in Qt, STL or Boost that would preclude using then within each other.

You would have to be careful when using smart pointers QT has parent/child relationship that can take care of destruction of objects, deallocating objects when they under control of Qt will make you crash.

Harald Scheirich
it is perfectly safe to delete an Qt object. In QObject's destructor it will unregister itself from its parent's child list. No crash at all.
Evan Teran
@Evan, there are some pitfalls... http://doc.qtsoftware.com/4.5/objecttrees.html at the bottom.
Idan K
I have had problems with this in the past. It might be due to the ordering problem noted in the above document.
Harald Scheirich
+14  A: 

What bridges do you need?

You can use all the Qt container classes with std algorithms. Most of the time I prefer the Qt container classes because I'm sure they use the copy-on-write idiom (constant time operation). Qt's foreach function creates a copy of the container so its nice that you know for sure it is a constant time operation.

If the Qt signal slot mechanism is to slow you can switch to the boost alternative. The great thing about Qt signal/slot is the signal/slot connection between two threads.

QtConcurrent works great with BOOST.Lambda


For "shared" child-parent relationship I use this helper function.

template <class Object>
static boost::shared_ptr<Object> makeSharedObject()
{
    using namespace boost;
    using namespace boost::lambda;
    return boost::shared_ptr<Object>( 
        new Object(),
        bind( &Object::deleteLater, _1 ) );
}


Qt containers are not supported by Boost.serialize, you'll have to write the serialize functions yourself. I would love a bridge between the Qt streaming classes and Boost.archive.

Here is my QList serialization template you can figure out the rest of them ...

///\file document is based on "boost/serialization/list.hpp"

namespace boost { 
    namespace serialization {

        //---------------------------------------------------------------------------
        /// Saves a QList object to a collection 
        template<class Archive, class U >
        inline void save(Archive &ar, const QList< U > &t, const uint /* file_version */ )
        {
            boost::serialization::stl::save_collection< Archive, QList<U> >(ar, t);
        }

        //---------------------------------------------------------------------------
        /// Loads a QList object from a collection 
        template<class Archive, class U>
        inline void load(Archive &ar, QList<U > &t, const uint /* file_version */ )
        {
                boost::serialization::stl::load_collection< 
                    Archive, 
                    QList<U>, 
                    boost::serialization::stl::archive_input_seq<Archive, QList<U> >,
                    boost::serialization::stl::no_reserve_imp< QList<U> > >(ar, t);
        }

        //---------------------------------------------------------------------------
        /// split non-intrusive serialization function member into separate
        /// non intrusive save/load member functions
        template<class Archive, class U >
        inline void serialize(Archive &ar, QList<U> &t, const uint file_version )
        {
            boost::serialization::split_free( ar, t, file_version);
        }

    } // namespace serialization
} // namespace boost

BOOST_SERIALIZATION_COLLECTION_TRAITS(QList)


If you want Boost.Bind to handle QPointer as a normal pointer (like shared_ptr):

namespace boost {

    template<typename T> T * get_pointer(QPointer<T> const& qPointer)
    {
        return qPointer;
    }
}


Using QIODevice where a std::stream is needed

namespace boost {
    namespace iostreams {

        class IoDeviceSource 
        {
        public:
            typedef char char_type;
            typedef source_tag category;

            explicit IoDeviceSource(QIODevice& source) 
                : m_source(source) 
            {
            }

            std::streamsize read(char* buffer, std::streamsize n)
            {
                return return m_source.read(buffer, n);
            }   
        private:
            QIODevice& m_source;
        };

        class IoDeviceSink {

        public:
            typedef char char_type;
            typedef sink_tag category;

            explicit IoDeviceSink(QIODevice& sink)
                : m_sink(sink)
            {
            }

            std::streamsize write(const char_type* buffer, std::streamsize n) 
            {
                return m_sink.write(buffer, n);
            }

        private:
            QIODevice &m_sink;
        };

        class IoDeviceDevice {

        public:
            typedef char char_type;
            typedef seekable_device_tag category;

            explicit IoDeviceDevice(QIODevice& device)
                :m_device(device) {
            }

            std::streamsize write(const char_type *buffer, std::streamsize n)
            {
                return m_device.write(buffer, n);
            }

            std::streamsize read(char* buffer, std::streamsize n)
            {
                return m_device.read(buffer, n);
            }

            stream_offset seek(stream_offset off, std::ios_base::seekdir way)
            {
                using namespace std;
                stream_offset next(0);

                if(way==ios_base::beg)
                {
                    next = m_device.pos();
                } 
                else if(way==ios_base::cur)
                {
                    next = m_device.pos() + offset;
                } 
                else if(way==ios_base::end)
                {
                    next = m_device.size() -1 + offset;
                }
                else
                {
                    throw ios_base::failure("bad seek direction");
                }

                if( !m_device.seek(next) )
                {
                    throw ios_base::failure("bad seek offset");
                }
                return m_device.pos();
            }

        private:    
            QIODevice &m_device;
        };
    }
}

Example

#include <iostream>
#include <QFile>
#include <boost/iostreams/stream.hpp>
#include "iodevicestream.h"

int main(int argc, char *argv[])
{
    namespace io = boost::iostreams;

    QVector<int> data;

    QFile fl("temp.bin");
    fl.open(QIODevice::ReadWrite);
    io::stream<io::IoDeviceDevice> inoutput( fl );  

    std::copy(data.begin(), data.end(), std::ostream_iterator<int>(inoutput, "\n"));
    inoutput.flush();
    inoutput.seekg(0, std::ios_base::beg);
    std::cout << inoutput;
    return 0;
}
TimW
@TimW That's the best answer so far... Thanks for real-world code. Knowing how popular Boost is I hope Qt team will make these kind of bridges part of the framework to save people from reinventing the wheel. I think that would also help in faster adoption of Qt among developers working with Boost already. This may be seen as a very important factor of Qt's popularity in certain C++ circles.
Piotr Dobrogost