// 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 
    ) : 
        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 

    ~pipe_example (
        // wait for all the messages to be processed

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

        // now block until our thread has terminated

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


    // 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.  

    // 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

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

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

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

    // 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.

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