dlib C++ Library - pipe_ex_2.cpp

    // The contents of this file are in the public domain. See LICENSE_FOR_EXAMPLE_PROGRAMS.txt

    /*
        This is an example showing how to use the type_safe_union and pipe object from
        from the dlib C++ Library to send messages between threads.

        In this example we will create a class with a single thread in it.  This thread
        will receive messages from a pipe object and simply print them to the screen.
        The interesting thing about this example is that it shows how to use a pipe and
        type_safe_union to create a message channel between threads that can send many
        different types of objects in a type safe manner.

        Program output:
            got a float: 4.567
            got a string: string message
            got an int: 7
            got a string: yet another string message
    */

    #include <dlib/threads.h>
    #include <dlib/pipe.h>
    #include <dlib/type_safe_union.h>
    #include <iostream>

    using namespace dlib;
    using namespace std;

    // ----------------------------------------------------------------------------------------

    typedef type_safe_union<int, float, std::string> tsu_type;
    /*  This is a typedef for the type_safe_union we will be using in this example.
        This type_safe_union object is a type-safe analogue of a union declared as follows:
            union our_union_type
            {
                int a;
                float b;
                std::string c;
            };

        Note that the above union isn't actually valid C++ code because it contains a
        non-POD type.  That is, you can't put a std::string or any non-trivial
        C++ class in a union.   The type_safe_union, however, enables you to store non-POD
        types such as the std::string.

    */

    // ----------------------------------------------------------------------------------------

    class pipe_example : private threaded_object
    {
    public:
        pipe_example(
        ) :
            message_pipe(4) // This 4 here is the size of our message_pipe.  The significance is that
                        // if you try to enqueue more than 4 messages onto the pipe then enqueue() will
                        // block until there is room.
        {
            // start the thread
            start();
        }

        ~pipe_example (
        )
        {
            // wait for all the messages to be processed
            message_pipe.wait_until_empty();

            // Now disable the message_pipe.  Doing this will cause all calls to
            // message_pipe.dequeue() to return false so our thread will terminate
            message_pipe.disable();

            // now block until our thread has terminated
            wait();
        }

        // Here we declare our pipe object.  It will contain our messages.
        dlib::pipe<tsu_type> message_pipe;

    private:

        // When we call apply_to_contents() below these are the
        // functions which get called.
        void operator() (int val)
        {
            cout << "got an int: " << val << endl;
        }

        void operator() (float val)
        {
            cout << "got a float: " << val << endl;
        }

        void operator() (std::string val)
        {
            cout << "got a string: " << val << endl;
        }

        void thread ()
        {
            tsu_type msg;

            // Here we loop on messages from the message_pipe.
            while (message_pipe.dequeue(msg))
            {
                // Here we call the apply_to_contents() function on our type_safe_union.
                // It takes a function object and applies that function object
                // to the contents of the union.  In our case we have setup
                // the pipe_example class as our function object and so below we
                // tell the msg object to take whatever it contains and
                // call (*this)(contained_object);   So what happens here is
                // one of the three above functions gets called with the message
                // we just got.
                msg.apply_to_contents(*this);
            }
        }

        // Finally, note that since we declared the operator() member functions
        // private we need to declare the type_safe_union as a friend of this
        // class so that it will be able to call them.
        friend class type_safe_union<int, float, std::string>;

    };

    // ----------------------------------------------------------------------------------------

    int main()
    {
        pipe_example pe;

        // Make one of our type_safe_union objects
        tsu_type msg;

        // Treat our msg as a float and assign it 4.567
        msg.get<float>() = 4.567f;
        // Now put the message into the pipe
        pe.message_pipe.enqueue(msg);

        // Put a string into the pipe
        msg.get<std::string>() = "string message";
        pe.message_pipe.enqueue(msg);

        // And now an int
        msg.get<int>() = 7;
        pe.message_pipe.enqueue(msg);

        // And another string
        msg.get<std::string>() = "yet another string message";
        pe.message_pipe.enqueue(msg);

        // the main function won't really terminate here.  It will call the destructor for pe
        // which will block until all the messages have been processed.
    }

    // ----------------------------------------------------------------------------------------